Browse Source

Fixed path serialization

flabbet 8 months ago
parent
commit
90dff6f410

+ 4 - 2
src/PixiEditor/Models/Serialization/Factories/EllipseSerializationFactory.cs

@@ -15,8 +15,10 @@ public class EllipseSerializationFactory : VectorShapeSerializationFactory<Ellip
         builder.AddVecD(original.Radius);
         builder.AddVecD(original.Radius);
     }
     }
 
 
-    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        float strokeWidth, out EllipseVectorData original)
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor,
+        float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out EllipseVectorData original)
     {
     {
         VecD center = extractor.GetVecD();
         VecD center = extractor.GetVecD();
         VecD radius = extractor.GetVecD();
         VecD radius = extractor.GetVecD();

+ 4 - 2
src/PixiEditor/Models/Serialization/Factories/LineSerializationFactory.cs

@@ -15,8 +15,10 @@ internal class LineSerializationFactory : VectorShapeSerializationFactory<LineVe
         builder.AddVecD(original.End);
         builder.AddVecD(original.End);
     }
     }
 
 
-    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        float strokeWidth, out LineVectorData original)
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor,
+        float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out LineVectorData original)
     {
     {
         VecD start = extractor.GetVecD();
         VecD start = extractor.GetVecD();
         VecD end = extractor.GetVecD();
         VecD end = extractor.GetVecD();

+ 4 - 2
src/PixiEditor/Models/Serialization/Factories/PointsDataSerializationFactory.cs

@@ -13,8 +13,10 @@ internal class PointsDataSerializationFactory : VectorShapeSerializationFactory<
         builder.AddVecDList(original.Points);
         builder.AddVecDList(original.Points);
     }
     }
 
 
-    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        float strokeWidth, out PointsVectorData original)
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor,
+        float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out PointsVectorData original)
     {
     {
         List<VecD> points = extractor.GetVecDList();
         List<VecD> points = extractor.GetVecDList();
         original = new PointsVectorData(points)
         original = new PointsVectorData(points)

+ 4 - 2
src/PixiEditor/Models/Serialization/Factories/RectangleSerializationFactory.cs

@@ -16,8 +16,10 @@ internal class RectangleSerializationFactory : VectorShapeSerializationFactory<R
         builder.AddVecD(original.Size);
         builder.AddVecD(original.Size);
     }
     }
 
 
-    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        float strokeWidth, out RectangleVectorData original)
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor,
+        float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out RectangleVectorData original)
     {
     {
         VecD center = extractor.GetVecD();
         VecD center = extractor.GetVecD();
         VecD size = extractor.GetVecD();
         VecD size = extractor.GetVecD();

+ 145 - 6
src/PixiEditor/Models/Serialization/Factories/VectorPathSerializationFactory.cs

@@ -3,24 +3,49 @@ using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Vector;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.Views.Overlays.PathOverlay;
 
 
 namespace PixiEditor.Models.Serialization.Factories;
 namespace PixiEditor.Models.Serialization.Factories;
 
 
-internal class VectorPathSerializationFactory : VectorShapeSerializationFactory<PathVectorData> 
+internal class VectorPathSerializationFactory : VectorShapeSerializationFactory<PathVectorData>
 {
 {
     public override string DeserializationId { get; } = "PixiEditor.PathData";
     public override string DeserializationId { get; } = "PixiEditor.PathData";
 
 
     protected override void AddSpecificData(ByteBuilder builder, PathVectorData original)
     protected override void AddSpecificData(ByteBuilder builder, PathVectorData original)
     {
     {
-        builder.AddString(original.Path.ToSvgPathData());
+        if (original.Path == null)
+        {
+            return;
+        }
+
+        EditableVectorPath path = new EditableVectorPath(original.Path);
+
+        builder.AddInt((int)path.Path.FillType);
+        builder.AddInt(path.SubShapes.Count);
+
+        foreach (var subShape in path.SubShapes)
+        {
+            SerializeSubShape(builder, subShape);
+        }
     }
     }
 
 
-    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        float strokeWidth, out PathVectorData original)
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor,
+        float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out PathVectorData original)
     {
     {
-        string path = extractor.GetString();
+        VectorPath path;
+        if (IsOldSerializer(serializerData))
+        {
+            string svgPath = extractor.GetString();
+            path = VectorPath.FromSvgPath(svgPath);
+        }
+        else
+        {
+            path = DeserializePath(extractor).ToVectorPath();
+        }
 
 
-        original = new PathVectorData(VectorPath.FromSvgPath(path))
+        original = new PathVectorData(path)
         {
         {
             StrokeColor = strokeColor,
             StrokeColor = strokeColor,
             FillColor = fillColor,
             FillColor = fillColor,
@@ -30,4 +55,118 @@ internal class VectorPathSerializationFactory : VectorShapeSerializationFactory<
 
 
         return true;
         return true;
     }
     }
+
+    private void SerializeSubShape(ByteBuilder builder, SubShape subShape)
+    {
+        builder.AddInt(subShape.IsClosed ? 1 : 0);
+        builder.AddInt(subShape.Points.Count);
+
+        foreach (var point in subShape.Points)
+        {
+            builder.AddFloat(point.Position.X);
+            builder.AddFloat(point.Position.Y);
+
+            builder.AddInt(point.Verb.IsEmptyVerb() ? -1 : (int)point.Verb.VerbType);
+            builder.AddFloat(point.Verb.From.X);
+            builder.AddFloat(point.Verb.From.Y);
+            
+            builder.AddFloat(point.Verb.To.X);
+            builder.AddFloat(point.Verb.To.Y);
+
+            if (HasControlPoint1(point))
+            {
+                builder.AddFloat(point.Verb.ControlPoint1.Value.X);
+                builder.AddFloat(point.Verb.ControlPoint1.Value.Y);
+            }
+
+            if (HasControlPoint2(point))
+            {
+                builder.AddFloat(point.Verb.ControlPoint2.Value.X);
+                builder.AddFloat(point.Verb.ControlPoint2.Value.Y);
+            }
+
+            if (IsConic(point))
+            {
+                builder.AddFloat(point.Verb.ConicWeight);
+            }
+        }
+    }
+    
+    private EditableVectorPath DeserializePath(ByteExtractor extractor)
+    {
+        PathFillType fillType = (PathFillType)extractor.GetInt();
+        int subShapesCount = extractor.GetInt();
+        List<SubShape> subShapes = new List<SubShape>();
+
+        for (int i = 0; i < subShapesCount; i++)
+        {
+            SubShape subShape = DeserializeSubShape(extractor);
+            subShapes.Add(subShape);
+        }
+
+        return new EditableVectorPath(subShapes, fillType);
+    }
+    
+    private SubShape DeserializeSubShape(ByteExtractor extractor)
+    {
+        bool isClosed = extractor.GetInt() == 1;
+        int pointsCount = extractor.GetInt();
+        List<ShapePoint> points = new List<ShapePoint>();
+
+        for (int i = 0; i < pointsCount; i++)
+        {
+            VecF position = new VecF(extractor.GetFloat(), extractor.GetFloat());
+            PathVerb verbType = (PathVerb)extractor.GetInt();
+            
+            VecF from = new VecF(extractor.GetFloat(), extractor.GetFloat());
+            VecF to = new VecF(extractor.GetFloat(), extractor.GetFloat());
+            
+            VecF? controlPoint1 = verbType is PathVerb.Cubic or PathVerb.Quad or PathVerb.Conic
+                ? new VecF(extractor.GetFloat(), extractor.GetFloat())
+                : null;
+            VecF? controlPoint2 = verbType is PathVerb.Cubic or PathVerb.Quad
+                ? new VecF(extractor.GetFloat(), extractor.GetFloat())
+                : null;
+            float conicWeight = verbType == PathVerb.Conic ? extractor.GetFloat() : 0;
+
+            Verb verb = new Verb(verbType, from, to, controlPoint1, controlPoint2, conicWeight);
+            
+            points.Add(new ShapePoint(position, points.Count, verb));
+        }
+
+        return new SubShape(points, isClosed);
+    }
+
+    private bool HasControlPoint1(ShapePoint point)
+    {
+        return (point.Verb.VerbType is PathVerb.Conic or PathVerb.Cubic or PathVerb.Quad) &&
+               point.Verb.ControlPoint1.HasValue;
+    }
+
+    private bool HasControlPoint2(ShapePoint point)
+    {
+        return (point.Verb.VerbType is PathVerb.Cubic or PathVerb.Quad) &&
+               point.Verb.ControlPoint2.HasValue;
+    }
+
+    private bool IsConic(ShapePoint point)
+    {
+        return point.Verb.VerbType == PathVerb.Conic;
+    }
+
+    private bool IsOldSerializer((string serializerName, string serializerVersion) serializerData)
+    {
+        if (string.IsNullOrEmpty(serializerData.serializerName) ||
+            string.IsNullOrEmpty(serializerData.serializerVersion))
+        {
+            return false;
+        }
+
+        if (Version.TryParse(serializerData.serializerVersion, out Version version))
+        {
+            return version is { Major: 2, Minor: 0, Build: 0, Revision: < 35 };
+        }
+
+        return false;
+    }
 }
 }

+ 4 - 2
src/PixiEditor/Models/Serialization/Factories/VectorShapeSerializationFactory.cs

@@ -46,8 +46,10 @@ public abstract class VectorShapeSerializationFactory<T> : SerializationFactory<
             strokeWidth = extractor.GetFloat();
             strokeWidth = extractor.GetFloat();
         }
         }
 
 
-        return DeserializeVectorData(extractor, matrix, strokeColor, fillColor, strokeWidth, out original);
+        return DeserializeVectorData(extractor, matrix, strokeColor, fillColor, strokeWidth, serializerData, out original);
     }
     }
     
     
-    protected abstract bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor, float strokeWidth, out T original);
+    protected abstract bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor,
+        Color fillColor, float strokeWidth, (string serializerName, string serializerVersion) serializerData,
+        out T original);
 }
 }

+ 2 - 2
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -41,5 +41,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.0.34")]
-[assembly: AssemblyFileVersion("2.0.0.34")]
+[assembly: AssemblyVersion("2.0.0.35")]
+[assembly: AssemblyFileVersion("2.0.0.35")]

+ 26 - 5
src/PixiEditor/Views/Overlays/PathOverlay/EditableVectorPath.cs

@@ -5,9 +5,9 @@ namespace PixiEditor.Views.Overlays.PathOverlay;
 
 
 public class EditableVectorPath
 public class EditableVectorPath
 {
 {
-    private VectorPath path;
+    private VectorPath? path;
 
 
-    public VectorPath Path
+    public VectorPath? Path
     {
     {
         get => path;
         get => path;
         set
         set
@@ -23,6 +23,8 @@ public class EditableVectorPath
 
 
     public int TotalPoints => subShapes.Sum(x => x.Points.Count);
     public int TotalPoints => subShapes.Sum(x => x.Points.Count);
 
 
+    public PathFillType FillType { get; set; }
+
     public int ControlPointsCount
     public int ControlPointsCount
     {
     {
         get
         get
@@ -32,6 +34,12 @@ public class EditableVectorPath
         }
         }
     }
     }
 
 
+    public EditableVectorPath(IEnumerable<SubShape> subShapes, PathFillType fillType)
+    {
+        this.subShapes = new List<SubShape>(subShapes);
+        FillType = fillType;
+    }
+
     public EditableVectorPath(VectorPath path)
     public EditableVectorPath(VectorPath path)
     {
     {
         if (path != null)
         if (path != null)
@@ -47,8 +55,19 @@ public class EditableVectorPath
 
 
     public VectorPath ToVectorPath()
     public VectorPath ToVectorPath()
     {
     {
-        VectorPath newPath = new VectorPath(Path);
-        newPath.Reset(); // preserve fill type and other properties
+        VectorPath newPath;
+        if (Path != null)
+        {
+            newPath = new VectorPath(Path);
+            newPath.Reset(); // preserve fill type and other properties
+        }
+        else
+        {
+            newPath = new VectorPath();
+        }
+        
+        newPath.FillType = FillType;
+
         foreach (var subShape in subShapes)
         foreach (var subShape in subShapes)
         {
         {
             AddVerbToPath(CreateMoveToVerb(subShape), newPath);
             AddVerbToPath(CreateMoveToVerb(subShape), newPath);
@@ -127,7 +146,7 @@ public class EditableVectorPath
                     {
                     {
                         subShapes.Add(new SubShape(currentSubShapePoints, isSubShapeClosed));
                         subShapes.Add(new SubShape(currentSubShapePoints, isSubShapeClosed));
                         currentSubShapePoints.Clear();
                         currentSubShapePoints.Clear();
-                        
+
                         currentSubShapePoints.Add(new ShapePoint(data.points[0], 0, new Verb()));
                         currentSubShapePoints.Add(new ShapePoint(data.points[0], 0, new Verb()));
                     }
                     }
                     else
                     else
@@ -143,6 +162,8 @@ public class EditableVectorPath
 
 
             globalVerbIndex++;
             globalVerbIndex++;
         }
         }
+
+        FillType = from.FillType;
     }
     }
 
 
     private void AddVerbToPath(Verb verb, VectorPath newPath)
     private void AddVerbToPath(Verb verb, VectorPath newPath)

+ 10 - 0
src/PixiEditor/Views/Overlays/PathOverlay/ShapePoint.cs

@@ -78,6 +78,16 @@ public class Verb
         VerbType = null;
         VerbType = null;
     }
     }
     
     
+    public Verb(PathVerb verb, VecF from, VecF to, VecF? controlPoint1, VecF? controlPoint2, float conicWeight)
+    {
+        VerbType = verb;
+        From = from;
+        To = to;
+        ControlPoint1 = controlPoint1;
+        ControlPoint2 = controlPoint2;
+        ConicWeight = conicWeight;
+    }
+    
     public Verb((PathVerb verb, VecF[] points, float conicWeight) verbData)
     public Verb((PathVerb verb, VecF[] points, float conicWeight) verbData)
     {
     {
         VerbType = verbData.verb;
         VerbType = verbData.verb;