Parcourir la source

Fix transform desync (#16363)

* Make sure wrapper and platform DrawingContext have the same transform after Flush

* Add some tests

* Update Avalonia.RenderTests.WpfCompare.csproj

* Remove comments

* Use test font
Benedikt Stebner il y a 1 an
Parent
commit
7da58ba205

+ 6 - 1
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs

@@ -148,5 +148,10 @@ internal partial class CompositorDrawingContextProxy
             ExecCommand(ref commands[index]);
 
         _commands.Clear();
+
+        if (Transform != _impl.Transform)
+        {
+            _impl.Transform = Transform;
+        }
     }
-}
+}

+ 16 - 1
tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs

@@ -296,7 +296,22 @@ namespace Avalonia.RenderTests.WpfCompare
                 return new DrawingImage(ConvertDrawing(di.Drawing));
             throw new NotSupportedException();
         }
-    
+
+        public void PushTransform(Matrix matrix)
+        {
+            _ctx.PushTransform(new MatrixTransform(matrix.ToWpf()));
+        }
+
+        public void Pop()
+        {
+            _ctx.Pop();
+        }
+
+        public void DrawLine(CrossPen pen, Point p1, Point p2)
+        {
+            _ctx.DrawLine(ConvertPen(pen), p1.ToWpf(), p2.ToWpf());
+        }
+
         public void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc) => _ctx.DrawRectangle(ConvertBrush(brush), ConvertPen(pen), rc.ToWpf());
         public void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geo) => 
             _ctx.DrawGeometry(ConvertBrush(brush), ConvertPen(pen), ConvertGeometry(geo));

+ 51 - 0
tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs

@@ -0,0 +1,51 @@
+using Avalonia.Media;
+using CrossUI;
+
+#if AVALONIA_SKIA
+namespace Avalonia.Skia.RenderTests.CrossTests;
+#elif AVALONIA_D2D
+namespace Avalonia.Direct2D1.RenderTests.CrossTests;
+#else
+namespace Avalonia.RenderTests.WpfCompare.CrossTests;
+#endif
+
+
+public class DrawingContextTests : CrossTestBase
+{
+    public DrawingContextTests() : base("Media/DrawingContext")
+    {
+    }
+
+    [CrossFact]
+    public void Transform_Should_Work_As_Expected()
+    {
+        RenderAndCompare(
+
+            new CrossFuncControl(ctx =>
+            {
+                ctx.PushTransform(Matrix.CreateTranslation(100, 100));
+                ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Red), Thickness = 1 },
+                    new Point(0, 0), new Point(100, 0));
+                ctx.Pop();
+
+                ctx.PushTransform(Matrix.CreateTranslation(200, 100));
+                ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Orange), Thickness = 1 },
+                    new Point(0, 0), new Point(0, 100));
+                ctx.Pop();
+
+                ctx.PushTransform(Matrix.CreateTranslation(200, 200));
+                ctx.DrawLine(
+                    new CrossPen { Brush = new CrossSolidColorBrush(Colors.Yellow), Thickness = 1 },
+                    new Point(0, 0), new Point(-100, 0));
+                ctx.Pop();
+
+                ctx.PushTransform(Matrix.CreateTranslation(100, 200));
+                ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Green), Thickness = 1 },
+                    new Point(0, 0), new Point(0, -100));
+                ctx.Pop();
+            }) { Width = 300, Height = 300 }
+
+        );
+
+    }
+}

+ 26 - 1
tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs

@@ -136,6 +136,7 @@ namespace Avalonia.Direct2D1.RenderTests.CrossUI
     class AvaloniaCrossDrawingContext : ICrossDrawingContext
     {
         private readonly DrawingContext _ctx;
+        private readonly Stack<DrawingContext.PushedState> _stack = new();
 
         public AvaloniaCrossDrawingContext(DrawingContext ctx)
         {
@@ -303,7 +304,31 @@ namespace Avalonia.Direct2D1.RenderTests.CrossUI
                 return new DrawingImage(ConvertDrawing(di.Drawing));
             throw new NotSupportedException();
         }
-    
+
+        public void PushTransform(Matrix matrix)
+        {
+            _stack.Push(_ctx.PushTransform(matrix));
+        }
+
+        public void Pop()
+        {
+            var state = _stack.Pop();
+
+            state.Dispose();
+        }
+
+        public void DrawLine(CrossPen pen, Point p1, Point p2)
+        {
+            var avPen = ConvertPen(pen);
+
+            if (avPen == null)
+            {
+                return;
+            }
+
+            _ctx.DrawLine(avPen, p1, p2);
+        }
+
         public void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc) => _ctx.DrawRectangle(ConvertBrush(brush), ConvertPen(pen), rc);
         public void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geometry) =>
             _ctx.DrawGeometry(ConvertBrush(brush), ConvertPen(pen), ConvertGeometry(geometry));

+ 3 - 0
tests/Avalonia.RenderTests/CrossUI/CrossUI.cs

@@ -193,6 +193,9 @@ public interface ICrossStreamGeometryContextImplProvider
 
 public interface ICrossDrawingContext
 {
+    void PushTransform(Matrix  matrix);
+    void Pop();
+    void DrawLine(CrossPen pen, Point p1, Point p2);
     void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc);
     void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geometry);
     void DrawImage(CrossImage image, Rect rc);

+ 69 - 0
tests/Avalonia.RenderTests/Media/DrawingContextTests.cs

@@ -0,0 +1,69 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Xunit;
+#pragma warning disable CS0649
+
+#if AVALONIA_SKIA
+namespace Avalonia.Skia.RenderTests;
+
+public class DrawingContextTests : TestBase
+{
+    public DrawingContextTests() : base(@"Media\DrawingContext")
+    {
+    }
+
+    [Fact]
+    public async Task Should_Render_LinesAndText()
+    {
+        var target = new Border
+        {
+            Width = 300,
+            Height = 300,
+            Background = Brushes.White,
+            Child = new RenderControl()
+        };
+        
+        await RenderToFile(target);
+        CompareImages(skipImmediate: true);
+    }
+
+    internal class RenderControl : Control
+    {
+        private static readonly Typeface s_typeface = new Typeface(TestFontFamily);
+
+        public override void Render(DrawingContext context)
+        {
+            var pen = new Pen(Brushes.LightGray, 10);
+            RenderLine1(context, pen);
+            RenderLine2(context, pen);
+            RenderLine3(context, pen);
+            RenderLine4(context, pen);
+
+            RenderLine1(context, new Pen(Brushes.Red));
+            RenderAText(context, new Point(50, 20));
+            RenderLine2(context, new Pen(Brushes.Orange));
+            RenderAText(context, new Point(50, -50));
+            RenderLine3(context, new Pen(Brushes.Yellow));
+            RenderAText(context, new Point(0, 0));
+            RenderLine4(context, new Pen(Brushes.Green));
+        }
+
+        private static void RenderLine1(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(100, 100), new Point(200, 100));
+        private static void RenderLine2(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(200, 100), new Point(200, 200));
+        private static void RenderLine3(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(200, 200), new Point(100, 200));
+        private static void RenderLine4(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(100, 200), new Point(100, 100));
+
+        private static void RenderAText(DrawingContext context, Point point)
+        {
+            using (context.PushOpacity(0.7))
+            {
+                context.DrawText(
+                    new FormattedText("any text to render", CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
+                        s_typeface, 12, Brushes.Black), point);
+            }
+        }
+    }
+}
+#endif

BIN
tests/TestFiles/CrossTests/Media/DrawingContext/Transform_Should_Work_As_Expected.wpf.png


BIN
tests/TestFiles/Skia/Media/DrawingContext/Should_Render_LinesAndText.expected.png