Skip to content

Commit 7da58ba

Browse files
authored
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
1 parent ad8e67c commit 7da58ba

File tree

8 files changed

+171
-3
lines changed

8 files changed

+171
-3
lines changed

src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,10 @@ public void Flush()
148148
ExecCommand(ref commands[index]);
149149

150150
_commands.Clear();
151+
152+
if (Transform != _impl.Transform)
153+
{
154+
_impl.Transform = Transform;
155+
}
151156
}
152-
}
157+
}

tests/Avalonia.RenderTests.WpfCompare/CrossUI.Wpf.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,22 @@ private static ImageSource ConvertImage(CrossImage image)
296296
return new DrawingImage(ConvertDrawing(di.Drawing));
297297
throw new NotSupportedException();
298298
}
299-
299+
300+
public void PushTransform(Matrix matrix)
301+
{
302+
_ctx.PushTransform(new MatrixTransform(matrix.ToWpf()));
303+
}
304+
305+
public void Pop()
306+
{
307+
_ctx.Pop();
308+
}
309+
310+
public void DrawLine(CrossPen pen, Point p1, Point p2)
311+
{
312+
_ctx.DrawLine(ConvertPen(pen), p1.ToWpf(), p2.ToWpf());
313+
}
314+
300315
public void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc) => _ctx.DrawRectangle(ConvertBrush(brush), ConvertPen(pen), rc.ToWpf());
301316
public void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geo) =>
302317
_ctx.DrawGeometry(ConvertBrush(brush), ConvertPen(pen), ConvertGeometry(geo));
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Avalonia.Media;
2+
using CrossUI;
3+
4+
#if AVALONIA_SKIA
5+
namespace Avalonia.Skia.RenderTests.CrossTests;
6+
#elif AVALONIA_D2D
7+
namespace Avalonia.Direct2D1.RenderTests.CrossTests;
8+
#else
9+
namespace Avalonia.RenderTests.WpfCompare.CrossTests;
10+
#endif
11+
12+
13+
public class DrawingContextTests : CrossTestBase
14+
{
15+
public DrawingContextTests() : base("Media/DrawingContext")
16+
{
17+
}
18+
19+
[CrossFact]
20+
public void Transform_Should_Work_As_Expected()
21+
{
22+
RenderAndCompare(
23+
24+
new CrossFuncControl(ctx =>
25+
{
26+
ctx.PushTransform(Matrix.CreateTranslation(100, 100));
27+
ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Red), Thickness = 1 },
28+
new Point(0, 0), new Point(100, 0));
29+
ctx.Pop();
30+
31+
ctx.PushTransform(Matrix.CreateTranslation(200, 100));
32+
ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Orange), Thickness = 1 },
33+
new Point(0, 0), new Point(0, 100));
34+
ctx.Pop();
35+
36+
ctx.PushTransform(Matrix.CreateTranslation(200, 200));
37+
ctx.DrawLine(
38+
new CrossPen { Brush = new CrossSolidColorBrush(Colors.Yellow), Thickness = 1 },
39+
new Point(0, 0), new Point(-100, 0));
40+
ctx.Pop();
41+
42+
ctx.PushTransform(Matrix.CreateTranslation(100, 200));
43+
ctx.DrawLine(new CrossPen { Brush = new CrossSolidColorBrush(Colors.Green), Thickness = 1 },
44+
new Point(0, 0), new Point(0, -100));
45+
ctx.Pop();
46+
}) { Width = 300, Height = 300 }
47+
48+
);
49+
50+
}
51+
}

tests/Avalonia.RenderTests/CrossUI/CrossUI.Avalonia.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public override void Render(DrawingContext context)
136136
class AvaloniaCrossDrawingContext : ICrossDrawingContext
137137
{
138138
private readonly DrawingContext _ctx;
139+
private readonly Stack<DrawingContext.PushedState> _stack = new();
139140

140141
public AvaloniaCrossDrawingContext(DrawingContext ctx)
141142
{
@@ -303,7 +304,31 @@ static IImage ConvertImage(CrossImage image)
303304
return new DrawingImage(ConvertDrawing(di.Drawing));
304305
throw new NotSupportedException();
305306
}
306-
307+
308+
public void PushTransform(Matrix matrix)
309+
{
310+
_stack.Push(_ctx.PushTransform(matrix));
311+
}
312+
313+
public void Pop()
314+
{
315+
var state = _stack.Pop();
316+
317+
state.Dispose();
318+
}
319+
320+
public void DrawLine(CrossPen pen, Point p1, Point p2)
321+
{
322+
var avPen = ConvertPen(pen);
323+
324+
if (avPen == null)
325+
{
326+
return;
327+
}
328+
329+
_ctx.DrawLine(avPen, p1, p2);
330+
}
331+
307332
public void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc) => _ctx.DrawRectangle(ConvertBrush(brush), ConvertPen(pen), rc);
308333
public void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geometry) =>
309334
_ctx.DrawGeometry(ConvertBrush(brush), ConvertPen(pen), ConvertGeometry(geometry));

tests/Avalonia.RenderTests/CrossUI/CrossUI.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ public interface ICrossStreamGeometryContextImplProvider
193193

194194
public interface ICrossDrawingContext
195195
{
196+
void PushTransform(Matrix matrix);
197+
void Pop();
198+
void DrawLine(CrossPen pen, Point p1, Point p2);
196199
void DrawRectangle(CrossBrush? brush, CrossPen? pen, Rect rc);
197200
void DrawGeometry(CrossBrush? brush, CrossPen? pen, CrossGeometry geometry);
198201
void DrawImage(CrossImage image, Rect rc);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Globalization;
2+
using System.Threading.Tasks;
3+
using Avalonia.Controls;
4+
using Avalonia.Media;
5+
using Xunit;
6+
#pragma warning disable CS0649
7+
8+
#if AVALONIA_SKIA
9+
namespace Avalonia.Skia.RenderTests;
10+
11+
public class DrawingContextTests : TestBase
12+
{
13+
public DrawingContextTests() : base(@"Media\DrawingContext")
14+
{
15+
}
16+
17+
[Fact]
18+
public async Task Should_Render_LinesAndText()
19+
{
20+
var target = new Border
21+
{
22+
Width = 300,
23+
Height = 300,
24+
Background = Brushes.White,
25+
Child = new RenderControl()
26+
};
27+
28+
await RenderToFile(target);
29+
CompareImages(skipImmediate: true);
30+
}
31+
32+
internal class RenderControl : Control
33+
{
34+
private static readonly Typeface s_typeface = new Typeface(TestFontFamily);
35+
36+
public override void Render(DrawingContext context)
37+
{
38+
var pen = new Pen(Brushes.LightGray, 10);
39+
RenderLine1(context, pen);
40+
RenderLine2(context, pen);
41+
RenderLine3(context, pen);
42+
RenderLine4(context, pen);
43+
44+
RenderLine1(context, new Pen(Brushes.Red));
45+
RenderAText(context, new Point(50, 20));
46+
RenderLine2(context, new Pen(Brushes.Orange));
47+
RenderAText(context, new Point(50, -50));
48+
RenderLine3(context, new Pen(Brushes.Yellow));
49+
RenderAText(context, new Point(0, 0));
50+
RenderLine4(context, new Pen(Brushes.Green));
51+
}
52+
53+
private static void RenderLine1(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(100, 100), new Point(200, 100));
54+
private static void RenderLine2(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(200, 100), new Point(200, 200));
55+
private static void RenderLine3(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(200, 200), new Point(100, 200));
56+
private static void RenderLine4(DrawingContext context, IPen pen) => context.DrawLine(pen, new Point(100, 200), new Point(100, 100));
57+
58+
private static void RenderAText(DrawingContext context, Point point)
59+
{
60+
using (context.PushOpacity(0.7))
61+
{
62+
context.DrawText(
63+
new FormattedText("any text to render", CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
64+
s_typeface, 12, Brushes.Black), point);
65+
}
66+
}
67+
}
68+
}
69+
#endif
Loading
Loading

0 commit comments

Comments
 (0)