From 4f8f46cb5d56e14a64c86f58dd85497cec1defa1 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 19 Mar 2024 19:15:05 +0100 Subject: [PATCH] Fix TransformOrigin for brushes (#15037) --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 5 ++- .../Media/DrawingContextImpl.cs | 5 ++- .../Media/ImageBrushTests.cs | 31 ++++++++++++++++++ ...d_Render_With_TransformOrigin.expected.png | Bin 0 -> 917 bytes ...d_Render_With_TransformOrigin.expected.png | Bin 0 -> 1473 bytes 5 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png create mode 100644 tests/TestFiles/Skia/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index ead84a82e2..562faa6c94 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -1158,7 +1158,10 @@ namespace Avalonia.Skia if (content.Transform is not null) { - transform = content.Transform.Value * transform; + var transformOrigin = content.TransformOrigin.ToPixels(targetRect); + var offset = Matrix.CreateTranslation(transformOrigin); + + transform *= -offset * content.Transform.Value * offset; } var calc = new TileBrushCalculator(tileBrush, contentSize, targetRect.Size); diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 3ddaf5986d..d889e394e6 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -625,7 +625,10 @@ namespace Avalonia.Direct2D1.Media if (sceneBrush?.Transform is not null) { - ctx.Transform *= sceneBrush.Transform.Value; + var transformOrigin = sceneBrushContent.TransformOrigin.ToPixels(rect); + var offset = Matrix.CreateTranslation(transformOrigin); + + ctx.Transform = -offset * sceneBrush.Transform.Value * offset; } sceneBrushContent.Render(ctx, diff --git a/tests/Avalonia.RenderTests/Media/ImageBrushTests.cs b/tests/Avalonia.RenderTests/Media/ImageBrushTests.cs index 699d0ea089..2a49444a10 100644 --- a/tests/Avalonia.RenderTests/Media/ImageBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/ImageBrushTests.cs @@ -490,5 +490,36 @@ namespace Avalonia.Direct2D1.RenderTests.Media CompareImages(); } + + [Fact] + public async Task ImageBrush_Should_Render_With_TransformOrigin() + { + var image = new Image + { + Width = 200, + Height = 200, + Source = new DrawingImage + { + Drawing = new GeometryDrawing + { + Brush = new DrawingBrush + { + Transform = new RotateTransform(45), + TransformOrigin = new RelativePoint(.5,.5, RelativeUnit.Relative), + Drawing = new GeometryDrawing + { + Brush = Brushes.MediumBlue, + Geometry = new RectangleGeometry { Rect = new Rect(0, 0, 48, 48) } + } + }, + Geometry = new RectangleGeometry { Rect = new Rect(0, 0, 48, 48) } + } + } + }; + + await RenderToFile(image); + + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png b/tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..b14d474414eaccd695fed3f62ea9b3a21dff3a56 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(Ggg+1}H|F{C2y?ajNscLGG*E-IP^N`we;DVqwiMRr-t%IQt0%h`ObJD)k) zeg2<<8*8sGf4Z^a$6lKq9owG+Sv8SzyFX?BWK!o~X>w3_lhJ%b=KI$3zK@z51UOh4 z9d3MJ+h}<$SAO;p)5&lT*APJK_6UVIm( zZW8Y!b%o-|GI#5z?|`a%x5Y7s?_A;kdR0A;*#aCb(zyb&o4t4ZRV#E<06QQi6X<|D zhrYs%f9NEA=EbRKb`%L zL;m^GkDH;<^{}?t$Bfhd5I`uSl|AuOG;$@50{PV+vy{JxXWSS&ckHuIU9 z*9Y)JcYS1(H&&Xy6pLIOCId!Yu|t5Vd0JplZ9s) OfWXt$&t;ucLK6V`&Mo%< literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png b/tests/TestFiles/Skia/Media/ImageBrush/ImageBrush_Should_Render_With_TransformOrigin.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad8d52329d10ef754a71dcb106c381ccf087b2d GIT binary patch literal 1473 zcmds%>r)eF6o(TQ*hnF!MkR$6BCJrrK`DaC-L{wI2;at}rgLEIbHXKYEO?Hy(iIGbL!LyuB`)vm?1WG@PI8Dp zQQu7`M1`ei-`a)g+0r$ox-+$!NrijnwSe(B)n?x3xu$PFRMl6_Fq$2m6@|OrzF(3r z?0of&6&GSZOqztp=R=Qahbl)Abq1F9+*|vkfY+7u&U+uGpt63<#lJv;O0!>=(0lmC zpvb#ql|w3)^IAp6@Qvl7X0pmY6>I-TNpAr+Rznt};0|l(!+c|mXqGH?OvSD}D50g` z#tx9h(0grIMMv|EL!w5q7i$YfJ z^aC~im$qIFhbg)Q<+KshUVMc0xIpi=8-CgVka(;>HZ+$s7e$ zU#Rj54F*6!Mx-nP0F}QLPuBpTvBO;g`ApYe$j7WZg4TdT5i&21X)v%xJ*|Pf-kOLv z!@kPRUh^9{rm~dzby|afu3)!Jth3Cb#?>{lq{(s0x_o;AvK#Xq&fstFBxrd7$wbQS z9K9Tz#ZElM^7R})>Hj@pVG8;^!nZxOFe{I##S8bDm5b*2Inyf4Q?9@0EeXZWEiG*> zGXMKREuMOQiPRx?Xm*Qg=f1Z+86v)dnGop;lLymS4=WFtU*(`+V9h0*M zvmAt){ZAhWCNC46MN zEWQ!OzTwANzidrzEOs~tK--=^5lBw~NZ0RAb?yhy$I9U;dHn7%%j?NMazh@z+&3VQ z4s)jqeU*C!n{Iacth{S_b1z@5K{hM_DbL1y*duE}L1UU;AiWQRzplJ$p(AY?q|OHJ>sKOm zHt<}(3aPVQz{rF^dKBb_W#?mMUeNw7FcK^CfrGG*i_)-U`U{zXIRpETUOG+@y#TiQ zWME|*IP0|?-R>yOwpY{o35r;N%*|d;RL6oqwHrm8u?6b6s16|X&95k;9RdvvuBgr# z0##KMF=`9cyQ4Z_;QBF&n1{gC22ZpV7