Browse Source

Implemented reference layer serializing

CPKreuz 2 years ago
parent
commit
401444500f

+ 10 - 0
src/ChunkyImageLib/Surface.cs

@@ -63,6 +63,16 @@ public class Surface : IDisposable
         return surface;
     }
 
+    public static Surface Load(byte[] encoded)
+    {
+        using var image = Image.FromEncodedData(encoded);
+
+        var surface = new Surface(new VecI(image.Width, image.Height));
+        surface.DrawingSurface.Canvas.DrawImage(image, 0, 0);
+
+        return surface;
+    }
+
     public unsafe void DrawBytes(VecI size, byte[] bytes, ColorType colorType, AlphaType alphaType)
     {
         ImageInfo info = new ImageInfo(size.X, size.Y, colorType, alphaType);

+ 73 - 0
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -1,5 +1,7 @@
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
 using ChunkyImageLib;
+using ChunkyImageLib.DataHolders;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface;
@@ -15,6 +17,7 @@ internal class DocumentViewModelBuilder : ChildrenBuilder
     public List<Color> Swatches { get; set; } = new List<Color>();
     public List<Color> Palette { get; set; } = new List<Color>();
     
+    public ReferenceLayerBuilder ReferenceLayer { get; set; }
 
     public DocumentViewModelBuilder WithSize(int width, int height)
     {
@@ -42,6 +45,27 @@ internal class DocumentViewModelBuilder : ChildrenBuilder
     public DocumentViewModelBuilder WithPalette<T>(IEnumerable<T> pallet, Func<T, Color> toColor) =>
         WithPalette(pallet.Select(toColor));
 
+    public DocumentViewModelBuilder WithReferenceLayer<T>(T reference, Action<T, ReferenceLayerBuilder> builder)
+    {
+        if (reference != null)
+        {
+            WithReferenceLayer(x => builder(reference, x));
+        }
+
+        return this;
+    }
+    
+    public DocumentViewModelBuilder WithReferenceLayer(Action<ReferenceLayerBuilder> builder)
+    {
+        var reference = new ReferenceLayerBuilder();
+
+        builder(reference);
+
+        ReferenceLayer = reference;
+        
+        return this;
+    }
+    
     public abstract class StructureMemberBuilder
     {
         private MaskBuilder maskBuilder;
@@ -276,6 +300,55 @@ internal class DocumentViewModelBuilder : ChildrenBuilder
             return this;
         }
     }
+
+    public class ReferenceLayerBuilder
+    {
+        public bool IsVisible { get; set; }
+        
+        public bool IsTopmost { get; set; }
+        
+        public VecI ImageSize { get; set; }
+        
+        public ShapeCorners Shape { get; set; }
+        
+        public byte[] ImagePbgra32Bytes { get; set; }
+
+        public ReferenceLayerBuilder WithIsVisible(bool isVisible)
+        {
+            IsVisible = isVisible;
+            return this;
+        }
+
+        public ReferenceLayerBuilder WithIsTopmost(bool isTopmost)
+        {
+            IsTopmost = isTopmost;
+            return this;
+        }
+
+        public ReferenceLayerBuilder WithSurface(Surface surface)
+        {
+            var writeableBitmap = surface.ToWriteableBitmap();
+            byte[] bytes = new byte[writeableBitmap.PixelHeight * writeableBitmap.BackBufferStride];
+            Marshal.Copy(surface.ToWriteableBitmap().BackBuffer, bytes, 0, bytes.Length);
+
+            WithImage(surface.Size, bytes);
+            
+            return this;
+        }
+
+        public ReferenceLayerBuilder WithImage(VecI size, byte[] pbgraData)
+        {
+            ImageSize = size;
+            ImagePbgra32Bytes = pbgraData;
+            return this;
+        }
+
+        public ReferenceLayerBuilder WithRect(VecD offset, VecD size)
+        {
+            Shape = new ShapeCorners(new RectD(offset, size));
+            return this;
+        }
+    }
     
 }
 

+ 9 - 2
src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs

@@ -1,4 +1,7 @@
-using PixiEditor.DrawingApi.Core.ColorsImpl;
+using ChunkyImageLib;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Models.IO;
 using PixiEditor.Parser;
 using PixiEditor.ViewModels.SubViewModels.Document;
 
@@ -12,7 +15,11 @@ internal static class PixiParserDocumentEx
         {
             b.WithSize(document.Width, document.Height)
                 .WithPalette(document.Palette, x => new Color(x.R, x.G, x.B, x.A))
-                .WithSwatches(document.Swatches, x => new(x.R, x.G, x.B, x.A));
+                .WithSwatches(document.Swatches, x => new(x.R, x.G, x.B, x.A))
+                .WithReferenceLayer(document.ReferenceLayer, (r, builder) => builder
+                    .WithIsVisible(r.Enabled)
+                    .WithRect(new VecD(r.OffsetX, r.OffsetY), new VecD(r.Width, r.Height))
+                    .WithSurface(Surface.Load(r.ImageBytes)));
 
             BuildChildren(b, document.RootFolder.Children);
         });

+ 38 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.Serialization.cs

@@ -8,7 +8,9 @@ using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
+using PixiEditor.Helpers;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.IO;
 using PixiEditor.Parser;
 using PixiEditor.Parser.Collections;
 using BlendMode = PixiEditor.Parser.BlendMode;
@@ -31,12 +33,47 @@ internal partial class DocumentViewModel
         {
             Width = Width, Height = Height,
             Swatches = ToCollection(Swatches), Palette = ToCollection(Palette),
-            RootFolder = root, PreviewImage = (MaybeRenderWholeImage().Value as Surface)?.DrawingSurface.Snapshot().Encode().AsSpan().ToArray()
+            RootFolder = root, PreviewImage = (MaybeRenderWholeImage().Value as Surface)?.DrawingSurface.Snapshot().Encode().AsSpan().ToArray(),
+            ReferenceLayer = GetReferenceLayer(doc)
         };
 
         return document;
     }
 
+    private static ReferenceLayer GetReferenceLayer(IReadOnlyDocument document)
+    {
+        if (document.ReferenceLayer == null)
+        {
+            return null;
+        }
+
+        var layer = document.ReferenceLayer!;
+
+        var surface = new Surface(new VecI(layer.ImageSize.X, layer.ImageSize.Y));
+        
+        surface.DrawBytes(surface.Size, layer.ImagePbgra32Bytes.ToArray(), ColorType.Bgra8888, AlphaType.Premul);
+
+        var encoder = new PngBitmapEncoder();
+
+        using var stream = new MemoryStream();
+        
+        encoder.Frames.Add(BitmapFrame.Create(surface.ToWriteableBitmap()));
+        encoder.Save(stream);
+
+        stream.Position = 0;
+
+        return new ReferenceLayer
+        {
+            Enabled = layer.IsVisible,
+            Width = (float)layer.Shape.RectSize.X,
+            Height = (float)layer.Shape.RectSize.Y,
+            OffsetX = (float)layer.Shape.TopLeft.X,
+            OffsetY = (float)layer.Shape.TopLeft.Y,
+            Opacity = 1,
+            ImageBytes = stream.ToArray()
+        };
+    }
+
     private static void AddMembers(IEnumerable<IReadOnlyStructureMember> members, IReadOnlyDocument document, Folder parent)
     {
         foreach (var member in members)

+ 8 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -1,4 +1,5 @@
-using System.IO;
+using System.Collections.Immutable;
+using System.IO;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
@@ -201,6 +202,12 @@ internal partial class DocumentViewModel : NotifyableObject
             new SymmetryAxisPosition_Action(SymmetryAxisDirection.Vertical, builderInstance.Width / 2),
             new EndSymmetryAxisPosition_Action());
 
+        if (builderInstance.ReferenceLayer is { } refLayer)
+        {
+            acc
+                .AddActions(new SetReferenceLayer_Action(refLayer.Shape, refLayer.ImagePbgra32Bytes.ToImmutableArray(), refLayer.ImageSize));
+        }
+        
         viewModel.Swatches = new WpfObservableRangeCollection<Color>(builderInstance.Swatches);
         viewModel.Palette = new WpfObservableRangeCollection<Color>(builderInstance.Palette);