|
@@ -3,6 +3,7 @@ using System.Drawing;
|
|
|
using ChunkyImageLib;
|
|
|
using ChunkyImageLib.DataHolders;
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
+using PixiEditor.ChangeableDocument.Changeables.Animations;
|
|
|
using PixiEditor.Helpers.Extensions;
|
|
|
using PixiEditor.Models.IO.FileEncoders;
|
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
|
|
@@ -13,6 +14,7 @@ using PixiEditor.DrawingApi.Core.Surfaces;
|
|
|
using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
|
|
|
using PixiEditor.Extensions.CommonApi.Palettes;
|
|
|
using PixiEditor.Helpers;
|
|
|
+using PixiEditor.Models.Handlers;
|
|
|
using PixiEditor.Models.Serialization;
|
|
|
using PixiEditor.Models.Serialization.Factories;
|
|
|
using PixiEditor.Numerics;
|
|
@@ -21,7 +23,13 @@ using PixiEditor.Parser.Collections;
|
|
|
using PixiEditor.Parser.Graph;
|
|
|
using PixiEditor.Parser.Skia;
|
|
|
using PixiEditor.Parser.Skia.Encoders;
|
|
|
+using PixiEditor.SVG;
|
|
|
+using PixiEditor.SVG.Elements;
|
|
|
+using PixiEditor.SVG.Features;
|
|
|
+using PixiEditor.SVG.Units;
|
|
|
+using PixiEditor.ViewModels.Document.Nodes;
|
|
|
using IKeyFrameChildrenContainer = PixiEditor.ChangeableDocument.Changeables.Interfaces.IKeyFrameChildrenContainer;
|
|
|
+using KeyFrameData = PixiEditor.Parser.KeyFrameData;
|
|
|
using PixiDocument = PixiEditor.Parser.Document;
|
|
|
using ReferenceLayer = PixiEditor.Parser.ReferenceLayer;
|
|
|
|
|
@@ -35,12 +43,12 @@ internal partial class DocumentViewModel
|
|
|
ImageEncoder encoder = new QoiEncoder();
|
|
|
var serializationConfig = new SerializationConfig(encoder);
|
|
|
var doc = Internals.Tracker.Document;
|
|
|
-
|
|
|
+
|
|
|
Dictionary<Guid, int> nodeIdMap = new();
|
|
|
Dictionary<Guid, int> keyFrameIdMap = new();
|
|
|
|
|
|
List<SerializationFactory> factories =
|
|
|
- ViewModelMain.Current.Services.GetServices<SerializationFactory>().ToList(); // a bit ugly, sorry
|
|
|
+ ViewModelMain.Current.Services.GetServices<SerializationFactory>().ToList();
|
|
|
|
|
|
AddNodes(doc.NodeGraph, graph, nodeIdMap, keyFrameIdMap, serializationConfig, factories);
|
|
|
|
|
@@ -61,7 +69,85 @@ internal partial class DocumentViewModel
|
|
|
return document;
|
|
|
}
|
|
|
|
|
|
- private static void AddNodes(IReadOnlyNodeGraph graph, NodeGraph targetGraph,
|
|
|
+ public SvgDocument ToSvgDocument(KeyFrameTime atTime, VecI exportSize)
|
|
|
+ {
|
|
|
+ SvgDocument svgDocument = new(new RectD(0, 0, exportSize.X, exportSize.Y));
|
|
|
+
|
|
|
+ float resizeFactorX = (float)exportSize.X / Width;
|
|
|
+ float resizeFactorY = (float)exportSize.Y / Height;
|
|
|
+ VecD resizeFactor = new VecD(resizeFactorX, resizeFactorY);
|
|
|
+
|
|
|
+ AddElements(NodeGraph.StructureTree.Members, svgDocument, atTime, resizeFactor);
|
|
|
+
|
|
|
+ return svgDocument;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddElements(IEnumerable<INodeHandler> root, IElementContainer elementContainer, KeyFrameTime atTime,
|
|
|
+ VecD resizeFactor)
|
|
|
+ {
|
|
|
+ foreach (var member in root)
|
|
|
+ {
|
|
|
+ if (member is FolderNodeViewModel folderNodeViewModel)
|
|
|
+ {
|
|
|
+ var group = new SvgGroup();
|
|
|
+
|
|
|
+ AddElements(folderNodeViewModel.Children, group, atTime, resizeFactor);
|
|
|
+ elementContainer.Children.Add(group);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (member is IRasterLayerHandler)
|
|
|
+ {
|
|
|
+ AddSvgImage(elementContainer, atTime, member, resizeFactor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddSvgImage(IElementContainer elementContainer, KeyFrameTime atTime, INodeHandler member,
|
|
|
+ VecD resizeFactor)
|
|
|
+ {
|
|
|
+ IReadOnlyImageNode imageNode = (IReadOnlyImageNode)Internals.Tracker.Document.FindNode(member.Id);
|
|
|
+
|
|
|
+ var tightBounds = imageNode.GetTightBounds(atTime);
|
|
|
+
|
|
|
+ if (tightBounds == null || tightBounds.Value.IsZeroArea) return;
|
|
|
+
|
|
|
+ SvgImage image = new SvgImage();
|
|
|
+
|
|
|
+ RectI bounds = (RectI)tightBounds.Value;
|
|
|
+
|
|
|
+ using Surface surface = new Surface(bounds.Size);
|
|
|
+ imageNode.GetLayerImageAtFrame(atTime.Frame).DrawMostUpToDateRegionOn(
|
|
|
+ (RectI)tightBounds.Value, ChunkResolution.Full, surface.DrawingSurface, VecI.Zero);
|
|
|
+
|
|
|
+ byte[] targetBytes;
|
|
|
+
|
|
|
+ RectD targetBounds = tightBounds.Value;
|
|
|
+
|
|
|
+ if (!resizeFactor.AlmostEquals(new VecD(1, 1)))
|
|
|
+ {
|
|
|
+ VecI newSize = new VecI((int)(bounds.Width * resizeFactor.X), (int)(bounds.Height * resizeFactor.Y));
|
|
|
+ using var resized = surface.Resize(newSize, ResizeMethod.NearestNeighbor);
|
|
|
+ using var snapshot = resized.DrawingSurface.Snapshot();
|
|
|
+ targetBytes = snapshot.Encode().AsSpan().ToArray();
|
|
|
+
|
|
|
+ targetBounds = new RectD(targetBounds.X * resizeFactor.X, targetBounds.Y * resizeFactor.Y, newSize.X, newSize.Y);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ using var snapshot = surface.DrawingSurface.Snapshot();
|
|
|
+ targetBytes = snapshot.Encode().AsSpan().ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ image.X.Unit = SvgNumericUnit.FromUserUnits(targetBounds.X);
|
|
|
+ image.Y.Unit = SvgNumericUnit.FromUserUnits(targetBounds.Y);
|
|
|
+ image.Width.Unit = SvgNumericUnit.FromUserUnits(targetBounds.Width);
|
|
|
+ image.Height.Unit = SvgNumericUnit.FromUserUnits(targetBounds.Height);
|
|
|
+ image.Href.Unit = new SvgStringUnit($"data:image/png;base64,{Convert.ToBase64String(targetBytes)}");
|
|
|
+
|
|
|
+ elementContainer.Children.Add(image);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void AddNodes(IReadOnlyNodeGraph graph, NodeGraph targetGraph,
|
|
|
Dictionary<Guid, int> nodeIdMap,
|
|
|
Dictionary<Guid, int> keyFrameIdMap,
|
|
|
SerializationConfig config, IReadOnlyList<SerializationFactory> allFactories)
|
|
@@ -85,7 +171,8 @@ internal partial class DocumentViewModel
|
|
|
properties[i] = new NodePropertyValue()
|
|
|
{
|
|
|
PropertyName = node.InputProperties[i].InternalPropertyName,
|
|
|
- Value = SerializationUtil.SerializeObject(node.InputProperties[i].NonOverridenValue, config, allFactories)
|
|
|
+ Value = SerializationUtil.SerializeObject(node.InputProperties[i].NonOverridenValue, config,
|
|
|
+ allFactories)
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -93,23 +180,23 @@ internal partial class DocumentViewModel
|
|
|
node.SerializeAdditionalData(additionalData);
|
|
|
|
|
|
KeyFrameData[] keyFrames = new KeyFrameData[node.KeyFrames.Count];
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < node.KeyFrames.Count; i++)
|
|
|
{
|
|
|
keyFrameIdMap[node.KeyFrames[i].KeyFrameGuid] = keyFrameId + 1;
|
|
|
keyFrames[i] = new KeyFrameData
|
|
|
{
|
|
|
Id = keyFrameId + 1,
|
|
|
- Data = SerializationUtil.SerializeObject(node.KeyFrames[i].Data, config, allFactories),
|
|
|
+ Data = SerializationUtil.SerializeObject(node.KeyFrames[i].Data, config, allFactories),
|
|
|
AffectedElement = node.KeyFrames[i].AffectedElement,
|
|
|
- StartFrame = node.KeyFrames[i].StartFrame,
|
|
|
+ StartFrame = node.KeyFrames[i].StartFrame,
|
|
|
Duration = node.KeyFrames[i].Duration,
|
|
|
IsVisible = node.KeyFrames[i].IsVisible
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
keyFrameId++;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Dictionary<string, object> converted = ConvertToSerializable(additionalData, config, allFactories);
|
|
|
|
|
|
List<PropertyConnection> connections = new();
|
|
@@ -181,7 +268,7 @@ internal partial class DocumentViewModel
|
|
|
|
|
|
var shape = layer.Shape;
|
|
|
var imageSize = layer.ImageSize;
|
|
|
-
|
|
|
+
|
|
|
var imageBytes = config.Encoder.Encode(layer.ImageBgra8888Bytes.ToArray(), imageSize.X, imageSize.Y);
|
|
|
|
|
|
return new ReferenceLayer
|
|
@@ -204,7 +291,8 @@ internal partial class DocumentViewModel
|
|
|
private ColorCollection ToCollection(IList<PaletteColor> collection) =>
|
|
|
new(collection.Select(x => Color.FromArgb(255, x.R, x.G, x.B)));
|
|
|
|
|
|
- private AnimationData ToAnimationData(IReadOnlyAnimationData animationData, IReadOnlyNodeGraph graph, Dictionary<Guid, int> nodeIdMap, Dictionary<Guid, int> keyFrameIds)
|
|
|
+ private AnimationData ToAnimationData(IReadOnlyAnimationData animationData, IReadOnlyNodeGraph graph,
|
|
|
+ Dictionary<Guid, int> nodeIdMap, Dictionary<Guid, int> keyFrameIds)
|
|
|
{
|
|
|
var animData = new AnimationData();
|
|
|
animData.KeyFrameGroups = new List<KeyFrameGroup>();
|
|
@@ -241,7 +329,8 @@ internal partial class DocumentViewModel
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static void BuildRasterKeyFrame(IReadOnlyRasterKeyFrame rasterKeyFrame, IReadOnlyNodeGraph graph, KeyFrameGroup group,
|
|
|
+ private static void BuildRasterKeyFrame(IReadOnlyRasterKeyFrame rasterKeyFrame, IReadOnlyNodeGraph graph,
|
|
|
+ KeyFrameGroup group,
|
|
|
Dictionary<Guid, int> idMap, Dictionary<Guid, int> keyFrameIds)
|
|
|
{
|
|
|
IReadOnlyChunkyImage image = rasterKeyFrame.GetTargetImage(graph.AllNodes);
|
|
@@ -261,8 +350,7 @@ internal partial class DocumentViewModel
|
|
|
|
|
|
group.Children.Add(new ElementKeyFrame()
|
|
|
{
|
|
|
- NodeId = idMap[rasterKeyFrame.NodeId],
|
|
|
- KeyFrameId = keyFrameIds[rasterKeyFrame.Id],
|
|
|
+ NodeId = idMap[rasterKeyFrame.NodeId], KeyFrameId = keyFrameIds[rasterKeyFrame.Id],
|
|
|
});
|
|
|
}
|
|
|
}
|