Browse 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
pull/17021/head
Benedikt Stebner 1 year ago
committed by GitHub
parent
commit
7da58ba205
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs
  2. 17
      tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs
  3. 51
      tests/Avalonia.RenderTests/CrossTests/Media/DrawingContextTests.cs
  4. 27
      tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs
  5. 3
      tests/Avalonia.RenderTests/CrossUI/CrossUI.cs
  6. 69
      tests/Avalonia.RenderTests/Media/DrawingContextTests.cs
  7. BIN
      tests/TestFiles/CrossTests/Media/DrawingContext/Transform_Should_Work_As_Expected.wpf.png
  8. BIN
      tests/TestFiles/Skia/Media/DrawingContext/Should_Render_LinesAndText.expected.png

7
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;
}
}
}
}

17
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
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 }
);
}
}

27
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
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
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Loading…
Cancel
Save