|
@@ -1,11 +1,17 @@
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
using ChunkyImageLib;
|
|
|
using Drawie.Backend.Core;
|
|
|
+using Drawie.Backend.Core.ColorsImpl;
|
|
|
using Drawie.Backend.Core.Surfaces;
|
|
|
+using Drawie.Backend.Core.Surfaces.PaintImpl;
|
|
|
using PixiEditor.Extensions.CommonApi.Palettes;
|
|
|
using Drawie.Numerics;
|
|
|
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
|
|
|
+using PixiEditor.Models.Serialization.Factories;
|
|
|
using PixiEditor.Parser;
|
|
|
+using PixiEditor.Parser.Graph;
|
|
|
using PixiEditor.Parser.Old.PixiV4;
|
|
|
+using PixiEditor.Parser.Skia.Encoders;
|
|
|
using PixiEditor.ViewModels.Document;
|
|
|
using BlendMode = PixiEditor.Parser.BlendMode;
|
|
|
|
|
@@ -15,32 +21,63 @@ internal static class PixiParserPixiV4DocumentEx
|
|
|
{
|
|
|
public static DocumentViewModel ToDocument(this DocumentV4 document)
|
|
|
{
|
|
|
- // TODO: Implement?
|
|
|
return DocumentViewModel.Build(b =>
|
|
|
{
|
|
|
- /*b.WithSize(document.Width, document.Height)
|
|
|
+ b.ImageEncoderUsed = "PNG";
|
|
|
+ b.WithSize(document.Width, document.Height)
|
|
|
.WithPalette(document.Palette, x => new PaletteColor(x.R, x.G, x.B))
|
|
|
.WithSwatches(document.Swatches, x => new(x.R, x.G, x.B))
|
|
|
- .WithReferenceLayer(document.ReferenceLayer, (r, builder) => builder
|
|
|
- .WithIsVisible(r.Enabled)
|
|
|
- .WithShape(r.Corners)
|
|
|
- .WithIsTopmost(r.Topmost)
|
|
|
- .WithSurface(Surface.Load(r.ImageBytes)));
|
|
|
-
|
|
|
- BuildChildren(b, document.RootFolder.Children);*/
|
|
|
+ .WithReferenceLayer(document.ReferenceLayer, (r, builder, encoder) => builder
|
|
|
+ .WithIsVisible(r.Enabled)
|
|
|
+ .WithShape(r.Corners)
|
|
|
+ .WithIsTopmost(r.Topmost)
|
|
|
+ .WithSurface(Surface.Load(r.ImageBytes)),
|
|
|
+ new PngEncoder());
|
|
|
+
|
|
|
+ b.WithGraph(graphBuilder =>
|
|
|
+ {
|
|
|
+ int lastIndex = GetIndexOfMember(document.RootFolder.Children[^1], document.RootFolder.Children);
|
|
|
+ graphBuilder.WithNodeOfType(typeof(OutputNode)).WithId(-1)
|
|
|
+ .WithConnections([
|
|
|
+ new PropertyConnection()
|
|
|
+ {
|
|
|
+ InputPropertyName = OutputNode.InputPropertyName,
|
|
|
+ OutputNodeId = lastIndex,
|
|
|
+ OutputPropertyName = "Output"
|
|
|
+ }
|
|
|
+ ]);
|
|
|
+
|
|
|
+ BuildChildren(graphBuilder, document.RootFolder.Children, OutputNode.InputPropertyName);
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
- void BuildChildren(ChildrenBuilder builder, IEnumerable<IStructureMember> members)
|
|
|
+ void BuildChildren(NodeGraphBuilder builder, IList<IStructureMember> members, string inputProperty)
|
|
|
{
|
|
|
- foreach (var member in members)
|
|
|
+ for (var i = members.Count - 1; i >= 0; i--)
|
|
|
{
|
|
|
+ var member = members[i];
|
|
|
if (member is Folder folder)
|
|
|
{
|
|
|
- builder.WithFolder(x => BuildFolder(x, folder));
|
|
|
+ builder.WithNode(nodeBuilder =>
|
|
|
+ {
|
|
|
+ int indexOfFolder = GetIndexOfMember(folder, document.RootFolder.Children);
|
|
|
+ BuildFolder(nodeBuilder, folder, indexOfFolder);
|
|
|
+ inputProperty = OutputNode.InputPropertyName;
|
|
|
+ });
|
|
|
+
|
|
|
+ if (folder.Children.Count > 0)
|
|
|
+ {
|
|
|
+ BuildChildren(builder, folder.Children, FolderNode.ContentInternalName);
|
|
|
+ }
|
|
|
}
|
|
|
else if (member is ImageLayer layer)
|
|
|
{
|
|
|
- builder.WithLayer(x => BuildLayer(x, layer));
|
|
|
+ builder.WithNode(nodeBuilder =>
|
|
|
+ {
|
|
|
+ int indexOfLayer = GetIndexOfMember(layer, document.RootFolder.Children);
|
|
|
+ BuildLayer(nodeBuilder, layer, indexOfLayer);
|
|
|
+ inputProperty = OutputNode.InputPropertyName;
|
|
|
+ });
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -50,39 +87,159 @@ internal static class PixiParserPixiV4DocumentEx
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void BuildFolder(FolderBuilder builder, Folder folder) => builder
|
|
|
- .WithName(folder.Name)
|
|
|
- .WithVisibility(folder.Enabled)
|
|
|
- .WithOpacity(folder.Opacity)
|
|
|
- .WithBlendMode(folder.BlendMode)
|
|
|
- .WithChildren(x => BuildChildren(x, folder.Children))
|
|
|
- .WithClipToBelow(folder.ClipToMemberBelow)
|
|
|
- .WithMask(folder.Mask,
|
|
|
- (x, m) => x.WithVisibility(m.Enabled).WithSurface(m.Width, m.Height,
|
|
|
- x => x.WithImage(m.ImageBytes, m.OffsetX, m.OffsetY)));
|
|
|
+ void BuildFolder(NodeGraphBuilder.NodeBuilder builder, Folder folder, int structureIndex)
|
|
|
+ {
|
|
|
+ Dictionary<string, object> inputValues = new Dictionary<string, object>();
|
|
|
+ inputValues["IsVisible"] = folder.Enabled;
|
|
|
+ inputValues["Opacity"] = folder.Opacity;
|
|
|
+ inputValues["BlendMode"] = (int)folder.BlendMode;
|
|
|
+
|
|
|
+ Dictionary<string, object> additionalValues = new Dictionary<string, object>();
|
|
|
+ additionalValues["clipToPreviousMember"] = folder.ClipToMemberBelow;
|
|
|
+
|
|
|
+ if (folder.Mask is not null)
|
|
|
+ {
|
|
|
+ inputValues["MaskIsVisible"] = folder.Mask.Enabled;
|
|
|
+ additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(folder.Mask.ImageBytes), document.Width, document.Height));
|
|
|
+ }
|
|
|
|
|
|
- void BuildLayer(LayerBuilder builder, ImageLayer layer)
|
|
|
+ PropertyConnection contentConnection = new PropertyConnection()
|
|
|
+ {
|
|
|
+ InputPropertyName = FolderNode.ContentInternalName,
|
|
|
+ OutputNodeId = structureIndex - 1,
|
|
|
+ OutputPropertyName = "Output"
|
|
|
+ };
|
|
|
+
|
|
|
+ int childrenCount = CountChildren(folder.Children);
|
|
|
+ int previousIndex = structureIndex - childrenCount - 1;
|
|
|
+ PropertyConnection backgroundConnection = new PropertyConnection()
|
|
|
+ {
|
|
|
+ InputPropertyName = OutputNode.InputPropertyName,
|
|
|
+ OutputNodeId = previousIndex,
|
|
|
+ OutputPropertyName = "Output"
|
|
|
+ };
|
|
|
+
|
|
|
+ builder
|
|
|
+ .WithId(structureIndex)
|
|
|
+ .WithName(folder.Name)
|
|
|
+ .WithUniqueNodeName("PixiEditor.Folder")
|
|
|
+ .WithInputValues(inputValues)
|
|
|
+ .WithAdditionalData(additionalValues)
|
|
|
+ .WithConnections([contentConnection, backgroundConnection]);
|
|
|
+ }
|
|
|
+
|
|
|
+ void BuildLayer(NodeGraphBuilder.NodeBuilder builder, ImageLayer layer, int structureIndex)
|
|
|
{
|
|
|
+ Dictionary<string, object> inputValues = new Dictionary<string, object>();
|
|
|
+ inputValues["IsVisible"] = layer.Enabled;
|
|
|
+ inputValues["Opacity"] = layer.Opacity;
|
|
|
+ inputValues["BlendMode"] = (int)layer.BlendMode;
|
|
|
+
|
|
|
+ Dictionary<string, object> additionalValues = new Dictionary<string, object>();
|
|
|
+ additionalValues["clipToPreviousMember"] = layer.ClipToMemberBelow;
|
|
|
+
|
|
|
+ if (layer.Mask is not null)
|
|
|
+ {
|
|
|
+ inputValues["MaskIsVisible"] = layer.Mask.Enabled;
|
|
|
+ additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(layer.Mask.ImageBytes), document.Width, document.Height));
|
|
|
+ }
|
|
|
+
|
|
|
+ PropertyConnection connection = new PropertyConnection()
|
|
|
+ {
|
|
|
+ InputPropertyName = OutputNode.InputPropertyName, OutputNodeId = structureIndex - 1, OutputPropertyName = "Output"
|
|
|
+ };
|
|
|
+
|
|
|
builder
|
|
|
+ .WithId(structureIndex)
|
|
|
.WithName(layer.Name)
|
|
|
- .WithGuid(layer.Guid)
|
|
|
- .WithVisibility(layer.Enabled)
|
|
|
- .WithOpacity(layer.Opacity)
|
|
|
- .WithBlendMode(layer.BlendMode)
|
|
|
- .WithRect(layer.Width, layer.Height, layer.OffsetX, layer.OffsetY)
|
|
|
- .WithClipToBelow(layer.ClipToMemberBelow)
|
|
|
- .WithLockAlpha(layer.LockAlpha)
|
|
|
- .WithMask(layer.Mask,
|
|
|
- (x, m) => x.WithVisibility(m.Enabled).WithSurface(m.Width, m.Height,
|
|
|
- x => x.WithImage(m.ImageBytes, m.OffsetX, m.OffsetY)));
|
|
|
-
|
|
|
- if (layer is { Width: > 0, Height: > 0 })
|
|
|
+ .WithUniqueNodeName("PixiEditor.ImageLayer")
|
|
|
+ .WithInputValues(inputValues)
|
|
|
+ .WithAdditionalData(additionalValues)
|
|
|
+ .WithKeyFrames(new[]
|
|
|
+ {
|
|
|
+ new KeyFrameData()
|
|
|
+ {
|
|
|
+ AffectedElement = ImageLayerNode.ImageLayerKey,
|
|
|
+ Data = new ChunkyImage(Surface.Load(layer.ImageBytes)),
|
|
|
+ Duration = 0,
|
|
|
+ StartFrame = 0,
|
|
|
+ IsVisible = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .WithConnections([connection]);
|
|
|
+
|
|
|
+ /*if (layer is { Width: > 0, Height: > 0 })
|
|
|
{
|
|
|
builder.WithSurface(x => x.WithImage(layer.ImageBytes, 0, 0));
|
|
|
+ }*/
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Surface ConvertToNewMaskFormat(Surface surface, int width, int height)
|
|
|
+ {
|
|
|
+ // convert opaque pixels to white and transparent pixels to black
|
|
|
+ var newSurface = new Surface(new VecI(width, height));
|
|
|
+ newSurface.DrawingSurface.Canvas.Clear(Colors.Black);
|
|
|
+ using ColorFilter colorFilter = ColorFilter.CreateBlendMode(Colors.White, Drawie.Backend.Core.Surfaces.BlendMode.SrcATop);
|
|
|
+ using Paint paint = new()
|
|
|
+ {
|
|
|
+ Color = Colors.White,
|
|
|
+ BlendMode = Drawie.Backend.Core.Surfaces.BlendMode.SrcOver,
|
|
|
+ ColorFilter = colorFilter
|
|
|
+ };
|
|
|
+
|
|
|
+ newSurface.DrawingSurface.Canvas.DrawSurface(surface.DrawingSurface, 0, 0, paint);
|
|
|
+ surface.Dispose();
|
|
|
+ return newSurface;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int GetIndexOfMember(IStructureMember structureMember, IList<IStructureMember> members)
|
|
|
+ {
|
|
|
+ int index = 0;
|
|
|
+ bool found = GetIndexOfMember(structureMember, members, ref index);
|
|
|
+ return found ? index : -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static bool GetIndexOfMember(IStructureMember structureMember, IList<IStructureMember> members,
|
|
|
+ ref int index)
|
|
|
+ {
|
|
|
+ for (int i = 0; i < members.Count; i++)
|
|
|
+ {
|
|
|
+ var member = members[i];
|
|
|
+
|
|
|
+ if (member is Folder folder1)
|
|
|
+ {
|
|
|
+ bool found = GetIndexOfMember(structureMember, folder1.Children, ref index);
|
|
|
+ if (found) return true;
|
|
|
}
|
|
|
+
|
|
|
+ if (member == structureMember)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ index++;
|
|
|
}
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
+
|
|
|
+ private static int CountChildren(IList<IStructureMember> children)
|
|
|
+ {
|
|
|
+ int count = 0;
|
|
|
+ for (int i = 0; i < children.Count; i++)
|
|
|
+ {
|
|
|
+ var child = children[i];
|
|
|
+ if (child is Folder folder)
|
|
|
+ {
|
|
|
+ count += CountChildren(folder.Children);
|
|
|
+ }
|
|
|
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return count;
|
|
|
+ }
|
|
|
|
|
|
internal class ChildrenBuilder
|
|
|
{
|