Browse Source

Improve internal canvas implementation

Marcin Ziąbek 8 months ago
parent
commit
0740e03cb9

+ 15 - 10
Source/QuestPDF/Drawing/DrawingCanvases/FreeDrawingCanvas.cs

@@ -1,4 +1,5 @@
-using QuestPDF.Infrastructure;
+using System.Collections.Generic;
+using QuestPDF.Infrastructure;
 using QuestPDF.Skia;
 using QuestPDF.Skia.Text;
 
@@ -6,6 +7,10 @@ namespace QuestPDF.Drawing.DrawingCanvases
 {
     internal sealed class FreeDrawingCanvas : IDrawingCanvas
     {
+        private Stack<SkCanvasMatrix> MatrixStack { get; } = new();
+        private SkCanvasMatrix CurrentMatrix { get; set; } = SkCanvasMatrix.Identity;
+        private int CurrentZIndex { get; set; } = 0;
+        
         public DocumentPageSnapshot GetSnapshot()
         {
             return new DocumentPageSnapshot();
@@ -18,47 +23,47 @@ namespace QuestPDF.Drawing.DrawingCanvases
 
         public void Save()
         {
-            
+            MatrixStack.Push(CurrentMatrix);
         }
 
         public void Restore()
         {
-            
+            CurrentMatrix = MatrixStack.Pop();
         }
 
         public void SetZIndex(int index)
         {
-            
+            CurrentZIndex = index;
         }
 
         public int GetZIndex()
         {
-            return 0;
+            return CurrentZIndex;
         }
         
         public SkCanvasMatrix GetCurrentMatrix()
         {
-            return default;
+            return CurrentMatrix;
         }
 
         public void SetMatrix(SkCanvasMatrix matrix)
         {
-            
+            CurrentMatrix = matrix;
         }
 
         public void Translate(Position vector)
         {
-            
+            CurrentMatrix = CurrentMatrix.Translate(vector.X, vector.Y);
         }
         
         public void Scale(float scaleX, float scaleY)
         {
-            
+            CurrentMatrix = CurrentMatrix.Scale(scaleX, scaleY);
         }
         
         public void Rotate(float angle)
         {
-            
+            CurrentMatrix = CurrentMatrix.Rotate(angle);
         }
 
         public void DrawFilledRectangle(Position vector, Size size, Color color)

+ 4 - 0
Source/QuestPDF/Drawing/DrawingCanvases/SkiaDrawingCanvas.cs

@@ -109,8 +109,12 @@ namespace QuestPDF.Drawing.DrawingCanvases
         
         public void SetZIndex(int index)
         {
+            var currentMatrix = CurrentCanvas?.GetCurrentMatrix() ?? SkCanvasMatrix.Identity;
+            
             CurrentZIndex = index;
             CurrentCanvas = GetCanvasForZIndex(CurrentZIndex);
+            
+            CurrentCanvas.SetCurrentMatrix(currentMatrix);
         }
 
         public int GetZIndex()

+ 1 - 3
Source/QuestPDF/Drawing/Proxy/SnapshotCacheRecorderProxy.cs

@@ -61,9 +61,6 @@ internal sealed class SnapshotCacheRecorderProxy : ElementProxy, IDisposable
         
     internal override void Draw(Size availableSpace)
     {
-        // element may overflow the available space
-        // capture as much as possible around the origin point
-        
         if (DrawCache.TryGetValue(PageContext.CurrentPage, out var snapshot))
         {
             Canvas.DrawSnapshot(snapshot);
@@ -76,6 +73,7 @@ internal sealed class SnapshotCacheRecorderProxy : ElementProxy, IDisposable
         using var skiaCanvas = new SkiaDrawingCanvas(Size.Max.Width, Size.Max.Height);
         RecorderCanvas.Target = skiaCanvas;
         RecorderCanvas.SetZIndex(0);
+        RecorderCanvas.SetMatrix(Canvas.GetCurrentMatrix());
         
         base.Draw(availableSpace);
         

+ 1 - 6
Source/QuestPDF/Elements/ZIndex.cs

@@ -8,16 +8,11 @@ namespace QuestPDF.Elements
         
         internal override void Draw(Size availableSpace)
         {
-            var previousMatrix = Canvas.GetCurrentMatrix();
             var previousZIndex = Canvas.GetZIndex();
-            Canvas.SetZIndex(Depth);
-            Canvas.SetMatrix(previousMatrix);
             
+            Canvas.SetZIndex(Depth);
             base.Draw(availableSpace);
-            
-            var newMatrix = Canvas.GetCurrentMatrix();
             Canvas.SetZIndex(previousZIndex);
-            Canvas.SetMatrix(newMatrix);
         }
     }
 }

+ 0 - 31
Source/QuestPDF/Skia/SkCanvas.cs

@@ -4,37 +4,6 @@ using QuestPDF.Skia.Text;
 
 namespace QuestPDF.Skia;
 
-[StructLayout(LayoutKind.Sequential)]
-internal struct SkCanvasMatrix
-{
-    public float ScaleX;
-    public float SkewX;
-    public float TranslateX;
-    
-    public float SkewY;
-    public float ScaleY;
-    public float TranslateY;
-
-    public float Perspective1;
-    public float Perspective2;
-    public float Perspective3;
-
-    public static SkCanvasMatrix Identity => new SkCanvasMatrix
-    {
-        ScaleX = 1,
-        SkewX = 0,
-        TranslateX = 0,
-        
-        SkewY = 0,
-        ScaleY = 1,
-        TranslateY = 0,
-        
-        Perspective1 = 0,
-        Perspective2 = 0,
-        Perspective3 = 1
-    };
-}
-
 internal sealed class SkCanvas : IDisposable
 {
     public IntPtr Instance { get; private set; }

+ 60 - 0
Source/QuestPDF/Skia/SkCanvasMatrix.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace QuestPDF.Skia;
+
+[StructLayout(LayoutKind.Sequential)]
+internal struct SkCanvasMatrix
+{
+    public float ScaleX;
+    public float SkewX;
+    public float TranslateX;
+    
+    public float SkewY;
+    public float ScaleY;
+    public float TranslateY;
+
+    public float Perspective1;
+    public float Perspective2;
+    public float Perspective3;
+
+    public static SkCanvasMatrix Identity => new()
+    {
+        ScaleX = 1,
+        SkewX = 0,
+        TranslateX = 0,
+        
+        SkewY = 0,
+        ScaleY = 1,
+        TranslateY = 0,
+        
+        Perspective1 = 0,
+        Perspective2 = 0,
+        Perspective3 = 1
+    };
+    
+    public SkCanvasMatrix Translate(float x, float y)
+    {
+        return this with { TranslateX = TranslateX + x, TranslateY = TranslateY + y };
+    }  
+    
+    public SkCanvasMatrix Scale(float x, float y)
+    {
+        return this with { ScaleX = ScaleX * x, ScaleY = ScaleY * y };
+    }
+    
+    public SkCanvasMatrix Rotate(float angle)
+    {
+        var radians = Math.PI * angle / 180;
+        var cos = (float)Math.Cos(radians);
+        var sin = (float)Math.Sin(radians);
+        
+        return this with
+        {
+            ScaleX = ScaleX * cos - SkewY * sin,
+            SkewX = ScaleX * sin + SkewY * cos,
+            ScaleY = ScaleY * cos - SkewX * sin,
+            SkewY = ScaleY * sin + SkewX * cos
+        };
+    }
+}