DocumentViewModel.Serialization.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System.Collections.Generic;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Linq;
  5. using ChunkyImageLib;
  6. using ChunkyImageLib.DataHolders;
  7. using PixiEditor.AvaloniaUI.Helpers;
  8. using PixiEditor.AvaloniaUI.Models.Handlers;
  9. using PixiEditor.AvaloniaUI.Models.IO.FileEncoders;
  10. using PixiEditor.ChangeableDocument.Changeables.Interfaces;
  11. using PixiEditor.DrawingApi.Core.Bridge;
  12. using PixiEditor.DrawingApi.Core.Numerics;
  13. using PixiEditor.DrawingApi.Core.Surface;
  14. using PixiEditor.DrawingApi.Core.Surface.ImageData;
  15. using PixiEditor.Extensions.CommonApi.Palettes;
  16. using PixiEditor.Numerics;
  17. using PixiEditor.Parser;
  18. using PixiEditor.Parser.Collections;
  19. using BlendMode = PixiEditor.Parser.BlendMode;
  20. using IKeyFrameChildrenContainer = PixiEditor.ChangeableDocument.Changeables.Interfaces.IKeyFrameChildrenContainer;
  21. using PixiDocument = PixiEditor.Parser.Document;
  22. namespace PixiEditor.AvaloniaUI.ViewModels.Document;
  23. internal partial class DocumentViewModel
  24. {
  25. public PixiDocument ToSerializable()
  26. {
  27. var root = new Folder();
  28. var doc = Internals.Tracker.Document;
  29. AddMembers(doc.StructureRoot.Children, doc, root);
  30. var document = new PixiDocument
  31. {
  32. Width = Width, Height = Height,
  33. Swatches = ToCollection(Swatches), Palette = ToCollection(Palette),
  34. RootFolder = root, PreviewImage = (TryRenderWholeImage(0).Value as Surface)?.DrawingSurface.Snapshot().Encode().AsSpan().ToArray(),
  35. ReferenceLayer = GetReferenceLayer(doc),
  36. AnimationData = ToAnimationData(doc.AnimationData)
  37. };
  38. return document;
  39. }
  40. private static ReferenceLayer? GetReferenceLayer(IReadOnlyDocument document)
  41. {
  42. if (document.ReferenceLayer == null)
  43. {
  44. return null;
  45. }
  46. var layer = document.ReferenceLayer!;
  47. var surface = new Surface(new VecI(layer.ImageSize.X, layer.ImageSize.Y));
  48. surface.DrawBytes(surface.Size, layer.ImageBgra8888Bytes.ToArray(), ColorType.Bgra8888, AlphaType.Premul);
  49. var encoder = new UniversalFileEncoder(EncodedImageFormat.Png);
  50. using var stream = new MemoryStream();
  51. encoder.Save(stream, surface);
  52. stream.Position = 0;
  53. return new ReferenceLayer
  54. {
  55. Enabled = layer.IsVisible,
  56. Width = (float)layer.Shape.RectSize.X,
  57. Height = (float)layer.Shape.RectSize.Y,
  58. OffsetX = (float)layer.Shape.TopLeft.X,
  59. OffsetY = (float)layer.Shape.TopLeft.Y,
  60. Corners = new Corners
  61. {
  62. TopLeft = layer.Shape.TopLeft.ToVector2(),
  63. TopRight = layer.Shape.TopRight.ToVector2(),
  64. BottomLeft = layer.Shape.BottomLeft.ToVector2(),
  65. BottomRight = layer.Shape.BottomRight.ToVector2()
  66. },
  67. Opacity = 1,
  68. ImageBytes = stream.ToArray()
  69. };
  70. }
  71. private static void AddMembers(IEnumerable<IReadOnlyStructureMember> members, IReadOnlyDocument document, Folder parent)
  72. {
  73. foreach (var member in members)
  74. {
  75. if (member is IReadOnlyFolder readOnlyFolder)
  76. {
  77. var folder = ToSerializable(readOnlyFolder);
  78. AddMembers(readOnlyFolder.Children, document, folder);
  79. parent.Children.Add(folder);
  80. }
  81. else if (member is IReadOnlyLayer readOnlyLayer)
  82. {
  83. parent.Children.Add(ToSerializable(readOnlyLayer, document));
  84. }
  85. }
  86. }
  87. private static Folder ToSerializable(IReadOnlyFolder folder)
  88. {
  89. return new Folder
  90. {
  91. Name = folder.Name,
  92. BlendMode = (BlendMode)(int)folder.BlendMode,
  93. Enabled = folder.IsVisible,
  94. Opacity = folder.Opacity,
  95. ClipToMemberBelow = folder.ClipToMemberBelow,
  96. Mask = GetMask(folder.Mask, folder.MaskIsVisible)
  97. };
  98. }
  99. private static ImageLayer ToSerializable(IReadOnlyLayer layer, IReadOnlyDocument document)
  100. {
  101. var result = document.GetLayerRasterizedImage(layer.GuidValue, 0);
  102. var tightBounds = document.GetChunkAlignedLayerBounds(layer.GuidValue, 0);
  103. using var data = result?.DrawingSurface.Snapshot().Encode();
  104. byte[] bytes = data?.AsSpan().ToArray();
  105. var serializable = new ImageLayer
  106. {
  107. Width = result?.Size.X ?? 0, Height = result?.Size.Y ?? 0, OffsetX = tightBounds?.X ?? 0, OffsetY = tightBounds?.Y ?? 0,
  108. Enabled = layer.IsVisible, BlendMode = (BlendMode)(int)layer.BlendMode, ImageBytes = bytes,
  109. ClipToMemberBelow = layer.ClipToMemberBelow, Name = layer.Name,
  110. Guid = layer.GuidValue,
  111. LockAlpha = layer is ITransparencyLockable { LockTransparency: true },
  112. Opacity = layer.Opacity, Mask = GetMask(layer.Mask, layer.MaskIsVisible)
  113. };
  114. return serializable;
  115. }
  116. private static Mask GetMask(IReadOnlyChunkyImage mask, bool maskVisible)
  117. {
  118. if (mask == null)
  119. return null;
  120. var maskBound = mask.FindChunkAlignedMostUpToDateBounds();
  121. if (maskBound == null)
  122. {
  123. return new Mask();
  124. }
  125. var surface = DrawingBackendApi.Current.SurfaceImplementation.Create(new ImageInfo(
  126. maskBound.Value.Width,
  127. maskBound.Value.Height));
  128. mask.DrawMostUpToDateRegionOn(new RectI(0, 0, maskBound.Value.Width, maskBound.Value.Height), ChunkResolution.Full, surface, new VecI(0, 0));
  129. return new Mask
  130. {
  131. Width = maskBound.Value.Width, Height = maskBound.Value.Height,
  132. OffsetX = maskBound.Value.X, OffsetY = maskBound.Value.Y,
  133. Enabled = maskVisible, ImageBytes = surface.Snapshot().Encode().AsSpan().ToArray()
  134. };
  135. }
  136. private ColorCollection ToCollection(IList<PaletteColor> collection) =>
  137. new(collection.Select(x => Color.FromArgb(255, x.R, x.G, x.B)));
  138. private AnimationData ToAnimationData(IReadOnlyAnimationData animationData)
  139. {
  140. var animData = new AnimationData();
  141. animData.KeyFrameGroups = new List<KeyFrameGroup>();
  142. BuildKeyFrames(animationData.KeyFrames, animData);
  143. return animData;
  144. }
  145. private static void BuildKeyFrames(IReadOnlyList<IReadOnlyKeyFrame> root, AnimationData animationData)
  146. {
  147. foreach (var keyFrame in root)
  148. {
  149. if(keyFrame is IKeyFrameChildrenContainer container)
  150. {
  151. KeyFrameGroup group = new();
  152. group.LayerGuid = keyFrame.LayerGuid;
  153. group.Enabled = keyFrame.IsVisible;
  154. foreach (var child in container.Children)
  155. {
  156. if (child is IKeyFrameChildrenContainer groupKeyFrame)
  157. {
  158. BuildKeyFrames(groupKeyFrame.Children, null);
  159. }
  160. else if (child is IReadOnlyRasterKeyFrame rasterKeyFrame)
  161. {
  162. BuildRasterKeyFrame(rasterKeyFrame, group);
  163. }
  164. }
  165. animationData?.KeyFrameGroups.Add(group);
  166. }
  167. }
  168. }
  169. private static void BuildRasterKeyFrame(IReadOnlyRasterKeyFrame rasterKeyFrame, KeyFrameGroup group)
  170. {
  171. var bounds = rasterKeyFrame.Image.FindChunkAlignedMostUpToDateBounds();
  172. DrawingSurface surface = null;
  173. if (bounds != null)
  174. {
  175. surface = DrawingBackendApi.Current.SurfaceImplementation.Create(
  176. new ImageInfo(bounds.Value.Width, bounds.Value.Height));
  177. rasterKeyFrame.Image.DrawMostUpToDateRegionOn(
  178. new RectI(0, 0, bounds.Value.Width, bounds.Value.Height), ChunkResolution.Full, surface,
  179. new VecI(0, 0));
  180. }
  181. group.Children.Add(new RasterKeyFrame()
  182. {
  183. LayerGuid = rasterKeyFrame.LayerGuid,
  184. StartFrame = rasterKeyFrame.StartFrame,
  185. Duration = rasterKeyFrame.Duration,
  186. ImageBytes = surface?.Snapshot().Encode().AsSpan().ToArray(),
  187. });
  188. }
  189. }