From 544d5372e31fc472bcb30ae926aaff5391ce9e59 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 10 Apr 2024 15:47:37 +0200 Subject: [PATCH] Fix TileBrush Source/DestinationRect handling (#15289) * Fix TileBrush Source/DestinationRect handling * Add unit test * Add cross render tests * Add missing test image --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 39 +++++------ .../CrossTests/Brushes/CrossTileBrushTests.cs | 61 ++++++++++++++++++ .../Media/ImageDrawingTests.cs | 4 +- .../Media/TileBrushTests.cs | 30 ++++++++- .../Should_Render_Scaled_TileBrush.wpf.png | Bin 0 -> 962 bytes .../Should_Render_With_Transform.wpf.png | Bin 0 -> 424 bytes .../DrawingBrushIsProperlyScaled.expected.png | Bin 0 -> 2038 bytes .../DrawingBrushIsProperlyScaled.expected.png | Bin 0 -> 1786 bytes 8 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 tests/TestFiles/CrossTests/Media/TileBrushes/Should_Render_Scaled_TileBrush.wpf.png create mode 100644 tests/TestFiles/CrossTests/Media/TileBrushes/Should_Render_With_Transform.wpf.png create mode 100644 tests/TestFiles/Direct2D1/Media/DrawingBrush/DrawingBrushIsProperlyScaled.expected.png create mode 100644 tests/TestFiles/Skia/Media/DrawingBrush/DrawingBrushIsProperlyScaled.expected.png diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 493d87988b..4f75a63321 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -1160,35 +1160,21 @@ namespace Avalonia.Skia return; } - var brushTransform = Matrix.CreateTranslation(-contentBounds.Position); - - contentBounds = contentBounds.TransformToAABB(brushTransform); + var brushTransform = Matrix.Identity; var destinationRect = content.Brush.DestinationRect.ToPixels(targetRect.Size); - if (tileBrush.Stretch != Stretch.None) - { - //scale content to destination size - var scale = tileBrush.Stretch.CalculateScaling(destinationRect.Size, contentBounds.Size); - - var scaleTransform = Matrix.CreateScale(scale); - - contentBounds = contentBounds.TransformToAABB(scaleTransform); - - brushTransform *= scaleTransform; - } - var sourceRect = tileBrush.SourceRect.ToPixels(contentBounds); - //scale content to source size - if (contentBounds.Size != sourceRect.Size) + brushTransform *= Matrix.CreateTranslation(-sourceRect.Position); + + if (sourceRect.Size != destinationRect.Size) { - var scale = tileBrush.Stretch.CalculateScaling(sourceRect.Size, contentBounds.Size); + //scale source to destination size + var scale = tileBrush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var scaleTransform = Matrix.CreateScale(scale); - contentBounds = contentBounds.TransformToAABB(scaleTransform); - brushTransform *= scaleTransform; } @@ -1199,11 +1185,16 @@ namespace Avalonia.Skia var transformOrigin = content.TransformOrigin.ToPixels(targetRect); var offset = Matrix.CreateTranslation(transformOrigin); transform = -offset * content.Transform.Value * offset; - } - if (content.Brush.TileMode == TileMode.None) - { - brushTransform *= transform; + if (tileBrush.TileMode == TileMode.None) + { + brushTransform *= transform; + + destinationRect = destinationRect.TransformToAABB(transform); + + destinationRect = new Rect(0, 0, destinationRect.Left + destinationRect.Width, + destinationRect.Top + destinationRect.Height); + } } if (tileBrush.Stretch == Stretch.None && transform == Matrix.Identity) diff --git a/tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs b/tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs index d7283cf9d9..160aa25942 100644 --- a/tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs +++ b/tests/Avalonia.RenderTests/CrossTests/Brushes/CrossTileBrushTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Avalonia.Media; using CrossUI; using Xunit; @@ -51,4 +52,64 @@ public class CrossTileBrushTests : CrossTestBase }); } + + [CrossFact] + public void Should_Render_Scaled_TileBrush() + { + var brush = new CrossDrawingBrush + { + TileMode = TileMode.Tile, + Viewbox = new Rect(0, 0, 20, 20), + ViewboxUnits = BrushMappingMode.Absolute, + Viewport = new Rect(0, 0, 20, 20), + ViewportUnits = BrushMappingMode.Absolute, + Drawing = new CrossGeometryDrawing(new CrossSvgGeometry("M 0 0 l 50 50")) + { + Pen = new CrossPen { Brush = new CrossSolidColorBrush(Colors.Red), Thickness = 5 } + } + }; + + RenderAndCompare(new CrossControl() + { + Width = 100, + Height = 100, + Background = brush + }); + + } + + [CrossFact] + public void Should_Render_With_Transform() + { + var brush = new CrossDrawingBrush() + { + TileMode = TileMode.None, + Viewbox = new Rect(0, 0, 1, 1), + ViewboxUnits = BrushMappingMode.RelativeToBoundingBox, + Viewport = new Rect(0, 0, 50, 50), + ViewportUnits = BrushMappingMode.Absolute, + Transform = Matrix.CreateTranslation(150, 150), + Drawing = new CrossDrawingGroup() + { + Children = new List() + { + new CrossGeometryDrawing(new CrossRectangleGeometry(new(0, 0, 100, 100))) + { + Brush = new CrossSolidColorBrush(Colors.Crimson) + }, + new CrossGeometryDrawing(new CrossRectangleGeometry(new(20, 20, 60, 60))) + { + Brush = new CrossSolidColorBrush(Colors.Blue) + } + } + } + }; + + RenderAndCompare(new CrossControl() + { + Width = 200, + Height = 200, + Background = brush + }); + } } diff --git a/tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs b/tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs index 3d593cd677..8a571c4d19 100644 --- a/tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs +++ b/tests/Avalonia.RenderTests/Media/ImageDrawingTests.cs @@ -107,8 +107,8 @@ namespace Avalonia.Direct2D1.RenderTests.Media _brush = new DrawingBrush() { TileMode = TileMode.None, - SourceRect = new RelativeRect(0, 0, 50, 50, RelativeUnit.Absolute), - DestinationRect = new RelativeRect(0, 0, 1, 1, RelativeUnit.Relative), + SourceRect = new RelativeRect(0, 0, 1, 1, RelativeUnit.Relative), + DestinationRect = new RelativeRect(0, 0, 50, 50, RelativeUnit.Absolute), Transform = new TranslateTransform(150, 150), Drawing = new DrawingGroup() { diff --git a/tests/Avalonia.RenderTests/Media/TileBrushTests.cs b/tests/Avalonia.RenderTests/Media/TileBrushTests.cs index c8ec8483ac..c3249422ea 100644 --- a/tests/Avalonia.RenderTests/Media/TileBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/TileBrushTests.cs @@ -40,7 +40,35 @@ public class DrawingBrushTests: TestBase await RenderToFile(target); CompareImages(); } - + + + [Fact] + public async Task DrawingBrushIsProperlyScaled() + { + Decorator target = new Decorator + { + Padding = new Thickness(10), + Width = 220, + Height = 220, + Child = new Rectangle + { + Fill = new DrawingBrush + { + TileMode = TileMode.Tile, + SourceRect = new RelativeRect(0, 0, 20, 20, RelativeUnit.Absolute), + DestinationRect = new RelativeRect(0, 0, 20, 20, RelativeUnit.Absolute), + Drawing = new GeometryDrawing() + { + Pen = new Pen(Brushes.Red, 5), + Geometry = Geometry.Parse("M 0 0 l 50 50") + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } #if AVALONIA_SKIA [Fact] diff --git a/tests/TestFiles/CrossTests/Media/TileBrushes/Should_Render_Scaled_TileBrush.wpf.png b/tests/TestFiles/CrossTests/Media/TileBrushes/Should_Render_Scaled_TileBrush.wpf.png new file mode 100644 index 0000000000000000000000000000000000000000..733e6200d839a3d4a830afb136f8e383ac204d54 GIT binary patch literal 962 zcmeAS@N?(olHy`uVBq!ia0vp^DImOF;d)q;TIn>R|W#{R2*Q__5DlFhy`KpPi$ovXyL2 zQ;=JU)en}XJugZ=aC%8zeEC3liqWFW2eD~*x%kTUeQj}I$-Lzb3)PbWRNcA+hZT^4X%->HW1m9c(<~s&YS3j3^P6VM%xNb!1@J z*w6hZkrl}2EbxddW?94KVRUF@9d3FRJFSN2l#Nk;ZYxSmpFS)dP?1RSY7$ z{n;t(-$eL=2z29Z?z;@&7=#ESm{)+3W?K9lIG?d2{4Kwcd z&e-3{#u0(Beu5SAuWhwAY7f2gRHX5{XYB8fI$@#HgIVq^FL~?!`G?*&tJrhD8^3eL z{(d{NrnlAx%=@zL@3k1Y;LAK z{Op5WuZp@3{NA$e??lTri}aRr?D2nft3Q3?s$FxGKG-eIwx4ypeBVSL-y5gxdbG`^&mwaVkT!T^_4uF8%500*^>fPWB!54Z zcvWP$^>wG{&y7kyXFY#i=aU0Y4TY|8>0S`##zjAV{p;D&XS)qlNW>|mx%YM*-K_^o zsK3OLq%JbwD?O>WOwjg0>~xu7#;r}d{_h4)SO_?em%i;klPFqh>H45H=z5(^cj#46 zDWDnq`=ZUh?!A7~Ki55|b-G?xtrnjw?+@bH?#e%4o^P6GLMwjz3qP)@vii!ZKU(wo*i-j@AYrJBD(!);StZrS4FqC z3SO7|p6(fYIP(AFJ)ckC&_5n9y?bS>|C$s1q9?E0#D7mtdJRqu+Mld~p-j=E*T1Jf zDSZQ}2AB-3vqE(4l~#fZr|kllJLEpB^X4<`eZlyC*OTGK72f^rc-n?V;5@!IyFP_) z^@`J)Oz$V}`t~?-%_?vy@bXr_5<2)NeDB^r)@w2v50`5Ah5+ks22WQ%mvv4FO#qhN B^LhXP literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/DrawingBrush/DrawingBrushIsProperlyScaled.expected.png b/tests/TestFiles/Skia/Media/DrawingBrush/DrawingBrushIsProperlyScaled.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..63873b65fdd86a02d846285fc2a049c08c128c3d GIT binary patch literal 1786 zcmeAS@N?(olHy`uVBq!ia0vp^cR-kf4M<8yoo@tEEX7WqAsj$Z!;#X#z`(ZA)5S5Q zV$Rz;hF#3wVlIKYPiJrbFPfpA{4e#1g_MAkYNJ(iMaW$4w{m+8X0^FJd8_6*Nd=B_ zbMNN8e);@<>F&Sn#~-&=S$_EM{_pVPk4$?l3)bF#yMMpj$;Wd~-oAIR(Ow?H+I9C| zrOf819&+z};+DKzT)AhNc;>GRx%DqyXW7jlCZ1>L<%ReXq zZL$Y5KPuH)emeI!_Gb5&mp~g{#VuK^Txk=xqTKyc#h&x>pZ-YOzVqb-T9*6%tN#nB zZ_8NvuYcM0{n>%7|32sk0-bd48z0}FpCHHG`*!Wa`m-M@eDyDVxo8u2x!a`H1gKca zSN`l{)4d*-fi@E(^~Iv-&1@l%{w;0$H4lQc#`o>{ToL|Fsn%-J@!h+`U%v2=>lc4@ zccP8l`_Jt*|K0klgPVW>al87j@UFJKU_Ea^!D;`;d&+U^$=mlfGuPWq0<%p2WE+$3 zHmHwm`(%ED143!-!+bBWkEbiYx4AcUugCnpCm+|nbF~7x6d1YXD*eZQ|4}@@|Kj6& zQyzSrb?;l;|CV)$U_JTO`Ft_2`Tq+&{dn$4?)wJ^=J!7VvGRHU-{K)7+)zWiU-|ex zNZ@7vfQ06krzfoY%Tx6)EmyAGv$}d>AJC;03-8IEeEiX_{>10}FFVXtEA}kgzW2KQ z!t5JhJ-6@8X1@OG@P3!dJ;s%{tt~!02Wkbfq7UykJ4{NPqPh*55+0w}2n{@`e~{1| tO^U>4lhLF&niO%BivCFTji=SC`pP|;rke^QIe?WJgQu&X%Q~loCIABv%X|O; literal 0 HcmV?d00001