From d15e86c2a70dfd6bcfaf8d85c972fa8a1ae627b7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 8 Sep 2018 16:24:43 +0200 Subject: [PATCH] Pass correct node bounds to PushOpacityMask. (#1870) * Pass correct node bounds. Use node bounds not clip bounds for opacity mask rect. Fixes #1118. * Use layout bounds for opacity mask. And added a new render test to make sure that the render transform is applied to it. * Fix bad XAML formatting. --- samples/ControlCatalog/Pages/CanvasPage.xaml | 3 +- .../Rendering/SceneGraph/IVisualNode.cs | 11 +++--- .../Rendering/SceneGraph/SceneBuilder.cs | 4 +- .../Rendering/SceneGraph/VisualNode.cs | 5 ++- .../Avalonia.RenderTests/OpacityMaskTests.cs | 37 ++++++++++++++++++ ...sform_Applies_To_Opacity_Mask.expected.png | Bin 0 -> 1048 bytes ...sform_Applies_To_Opacity_Mask.expected.png | Bin 0 -> 834 bytes 7 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png create mode 100644 tests/TestFiles/Skia/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png diff --git a/samples/ControlCatalog/Pages/CanvasPage.xaml b/samples/ControlCatalog/Pages/CanvasPage.xaml index 10a38895a2..d6c138a4f7 100644 --- a/samples/ControlCatalog/Pages/CanvasPage.xaml +++ b/samples/ControlCatalog/Pages/CanvasPage.xaml @@ -11,7 +11,8 @@ - + + diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs index e640925b3a..10c8d36dc8 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs @@ -30,19 +30,20 @@ namespace Avalonia.Rendering.SceneGraph Matrix Transform { get; } /// - /// Gets the bounds for the node's geometry in global coordinates. + /// Gets the bounds of the node's geometry in global coordinates. /// Rect Bounds { get; } /// /// Gets the clip bounds for the node in global coordinates. /// - /// - /// This clip does not take into account parent clips, to find the absolute clip bounds - /// it is necessary to traverse the tree. - /// Rect ClipBounds { get; } + /// + /// Gets the layout bounds for the node in global coordinates. + /// + Rect LayoutBounds { get; } + /// /// Whether the node is clipped to . /// diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 799380cb85..2fcea8c205 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -167,8 +167,9 @@ namespace Avalonia.Rendering.SceneGraph using (context.PushPostTransform(m)) using (context.PushTransformContainer()) { + var globalBounds = bounds.TransformToAABB(contextImpl.Transform); var clipBounds = clipToBounds ? - bounds.TransformToAABB(contextImpl.Transform).Intersect(clip) : + globalBounds.Intersect(clip) : clip; forceRecurse = forceRecurse || @@ -179,6 +180,7 @@ namespace Avalonia.Rendering.SceneGraph node.Transform = contextImpl.Transform; node.ClipBounds = clipBounds; node.ClipToBounds = clipToBounds; + node.LayoutBounds = globalBounds; node.GeometryClip = visual.Clip?.PlatformImpl; node.Opacity = opacity; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 760cedcbf0..8000d413ea 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -58,6 +58,9 @@ namespace Avalonia.Rendering.SceneGraph /// public Rect ClipBounds { get; set; } + /// + public Rect LayoutBounds { get; set; } + /// public bool ClipToBounds { get; set; } @@ -266,7 +269,7 @@ namespace Avalonia.Rendering.SceneGraph if (OpacityMask != null) { - context.PushOpacityMask(OpacityMask, ClipBounds); + context.PushOpacityMask(OpacityMask, LayoutBounds); } } diff --git a/tests/Avalonia.RenderTests/OpacityMaskTests.cs b/tests/Avalonia.RenderTests/OpacityMaskTests.cs index 1934654c26..4edf4daa13 100644 --- a/tests/Avalonia.RenderTests/OpacityMaskTests.cs +++ b/tests/Avalonia.RenderTests/OpacityMaskTests.cs @@ -55,5 +55,42 @@ namespace Avalonia.Direct2D1.RenderTests await RenderToFile(target); CompareImages(); } + + [Fact] + public async Task RenderTansform_Applies_To_Opacity_Mask() + { + var target = new Canvas + { + OpacityMask = new LinearGradientBrush + { + StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 1, RelativeUnit.Relative), + GradientStops = new List + { + new GradientStop(Color.FromUInt32(0xffffffff), 0), + new GradientStop(Color.FromUInt32(0x00ffffff), 1) + } + }, + RenderTransform = new RotateTransform(90), + Width = 76, + Height = 76, + Children = + { + new Path + { + Width = 32, + Height = 40, + [Canvas.LeftProperty] = 23, + [Canvas.TopProperty] = 18, + Stretch = Stretch.Fill, + Fill = Brushes.Red, + Data = StreamGeometry.Parse("F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z") + } + } + }; + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png b/tests/TestFiles/Direct2D1/OpacityMask/RenderTansform_Applies_To_Opacity_Mask.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..bf9775ba0beb386dec654781a4b559fb3be1ca91 GIT binary patch literal 1048 zcmV+z1n2vSP)Px#1ZP1_K>z@;j|==^1poj8!bwCyRCr$Pn#pPvK^TQQi6aghF_RjNE(C`piVzef zqM#38)-D`yp@PpKxN+sug%6-E1wjNA1QAqp;jnRtv+p39_?=31)3Z9LI4S$8j9TaU92S9LI4S$8j9TsU6cvGK@~3b#cfXzIzM0h<3!G z%IJ#K95%m^GQk3yXOg5F|BU8V_;4OL_K+&`?}veTVE!q}QKWTVqnnslRCCM&Bb*_$ zAG-pX+KokL^J^PF%sfi@pigJxn=?!aA|$y{@?H z7?a@ig;pFx>k;RWOmpHh>^OKdL;pAZUHDR%)XN-QSppl56KCo4M_%$D<8(?e|s>tb{PvzpofW5X0((b<}FcoQrGE8QrjAv83G zG8j-urY+Hh>@X;xAd+EQws100vmZ#u5vO|ZVgN;i$khqi;hLehk8bR4^;Fo$lF z??vZy3{?XPZ=C-0UxsEvrMG0>W70qtp`Az{PEv+YaZbarIX{fi173`ya0@f`H=&tO z`7N3Em=Fkcgn=WBZH)mn&58YNJm*JgCYiG@KTb1WXeO8iRy^@m9-~0|@Ov~)Uw?a+ z&1pI|=MQ4CD>EIZnJY9C%mRy>E!$|5$20^&e<7ijdYV?DnP7TL#?2NtTIww)8}b9@DPSOt5~2rJJi`XeOB6lDW-=4aa`zi4;Ai($J_Tm_GDT zZ9_vQm_PJYY3dl73D$vS8>OjRXeOB7l2O6a)G0I*D!C=|o~ACLnZQ~$u&cF{ZAkx4 z)c5KSLkgZouX56Hy|Pw6`#c>l^O&;N-E+=K$B|xd-@&}4{x|w!QxqIux}D7{xP}v; zX=i93&|OT+-vbtc7OMn;(+fYnPuKhS?8IoQ4sD9DdvxB@H%ALqN(@df3>92BF`6nu z`-mQ3rl^&njkE;?$6_O#T)5)!R-t{S{}3s#%F!m;3c=~wHi8CNe8HiiFX$2G6SZw< z105j-H_8SE|1q?$=rKltwHU;RWaU92S9LI4S$8j9TaU92S9LK4OB>4lpu~V=D Sba9{n0000zh``h(Eul=4*6P#j@P!)XxQ9E0`go56Kj_^Q>sPX!yi6L@!Y zh9p+|@J=+lsG27>Lvuznt8e07wcA$fwzIKDnhQi$8dhG?6FqUgE>nUtH_=tC?ojuf z6IMZ`e;+Mx?3ECCqAPkr{J7SXyOZQ@aGz;y&_2+}WN3ZKCWim#w27y*rB*jR+op8y zX4LzT%E!iu^S60Czq|JS4!KjQX&pBvUKC?Hs%Gc9c~jT1HC!Fo2ZGh7PVISeNXhYz(AlobleV$ilm|tg)bx!w(!Ig;$!g}s z9ke|XXZRAciX z)^T3L_O4&NdkTXhlb0KT%+@m6zTHpjG-fjNI2nI=?JAR-eMLusnS#O7)z4*}Q$iB} D$aQjr literal 0 HcmV?d00001