diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs index bbf648d71ff..5a2b5eb68db 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs @@ -148,5 +148,10 @@ public void Flush() ExecCommand(ref commands[index]); _commands.Clear(); + + if (Transform != _impl.Transform) + { + _impl.Transform = Transform; + } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs b/tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs index 9f183729fec..633a0fff78d 100644 --- a/tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs +++ b/tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs @@ -296,7 +296,22 @@ private static ImageSource ConvertImage(CrossImage image) 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)); diff --git a/tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs b/tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs new file mode 100644 index 00000000000..5ce8f258d10 --- /dev/null +++ b/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 } + + ); + + } +} diff --git a/tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs b/tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs index 42929a3690d..f3ec416ad18 100644 --- a/tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs +++ b/tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs @@ -136,6 +136,7 @@ public override void Render(DrawingContext context) class AvaloniaCrossDrawingContext : ICrossDrawingContext { private readonly DrawingContext _ctx; + private readonly Stack _stack = new(); public AvaloniaCrossDrawingContext(DrawingContext ctx) { @@ -303,7 +304,31 @@ static IImage ConvertImage(CrossImage image) 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)); diff --git a/tests/Avalonia.RenderTests/CrossUI/CrossUI.cs b/tests/Avalonia.RenderTests/CrossUI/CrossUI.cs index 028ace0eff8..7c690fc8247 100644 --- a/tests/Avalonia.RenderTests/CrossUI/CrossUI.cs +++ b/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); diff --git a/tests/Avalonia.RenderTests/Media/DrawingContextTests.cs b/tests/Avalonia.RenderTests/Media/DrawingContextTests.cs new file mode 100644 index 00000000000..c201e7502af --- /dev/null +++ b/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 diff --git a/tests/TestFiles/CrossTests/Media/DrawingContext/Transform_Should_Work_As_Expected.wpf.png b/tests/TestFiles/CrossTests/Media/DrawingContext/Transform_Should_Work_As_Expected.wpf.png new file mode 100644 index 00000000000..0c3b8c2b14a Binary files /dev/null and b/tests/TestFiles/CrossTests/Media/DrawingContext/Transform_Should_Work_As_Expected.wpf.png differ diff --git a/tests/TestFiles/Skia/Media/DrawingContext/Should_Render_LinesAndText.expected.png b/tests/TestFiles/Skia/Media/DrawingContext/Should_Render_LinesAndText.expected.png new file mode 100644 index 00000000000..3e775c44a38 Binary files /dev/null and b/tests/TestFiles/Skia/Media/DrawingContext/Should_Render_LinesAndText.expected.png differ