From ce60f607cf497c8e68a690d1201f257bf9a92f83 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 19 Oct 2021 14:28:05 +0200 Subject: [PATCH 1/3] Added failing tests for #6359. --- .../Controls/CustomRenderTests.cs | 48 ++++++++++++++++++ .../Clip_With_Transform.expected.png | Bin 0 -> 621 bytes .../GeometryClip_With_Transform.expected.png | Bin 0 -> 621 bytes 3 files changed, 48 insertions(+) create mode 100644 tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png create mode 100644 tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png diff --git a/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs b/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs index 8002b572bd..14f5b7c6c7 100644 --- a/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs +++ b/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs @@ -78,6 +78,54 @@ namespace Avalonia.Direct2D1.RenderTests.Controls CompareImages(); } + [Fact] + public async Task GeometryClip_With_Transform() + { + var target = new Border + { + Background = Brushes.White, + Width = 200, + Height = 200, + Child = new CustomRenderer((control, context) => + { + using (var transform = context.PushPreTransform(Matrix.CreateTranslation(100, 100))) + using (var clip = context.PushClip(new Rect(0, 0, 100, 100))) + { + context.FillRectangle(Brushes.Blue, new Rect(0, 0, 200, 200)); + } + + context.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100)); + }), + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task Clip_With_Transform() + { + var target = new Border + { + Background = Brushes.White, + Width = 200, + Height = 200, + Child = new CustomRenderer((control, context) => + { + using (var transform = context.PushPreTransform(Matrix.CreateTranslation(100, 100))) + using (var clip = context.PushClip(new Rect(0, 0, 100, 100))) + { + context.FillRectangle(Brushes.Blue, new Rect(0, 0, 200, 200)); + } + + context.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100)); + }), + }; + + await RenderToFile(target); + CompareImages(); + } + [Fact] public async Task Opacity() { diff --git a/tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png b/tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0bfa6fdc1234d417c98ea416e61ce614e1a8d9 GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|S$ar;B4q z#hkZSH}W1Z;9)VeKJiamJ9ky~+om}W7#}XM%|81`?y-z{r5}I8Y)+OY2L+B1M(oLc q_JytPo88_9d-)OOb!b!{V()cg?|WSD{T`Tp7(8A5T-G@yGywn#5QfqK literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png b/tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0bfa6fdc1234d417c98ea416e61ce614e1a8d9 GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`0|S$ar;B4q z#hkZSH}W1Z;9)VeKJiamJ9ky~+om}W7#}XM%|81`?y-z{r5}I8Y)+OY2L+B1M(oLc q_JytPo88_9d-)OOb!b!{V()cg?|WSD{T`Tp7(8A5T-G@yGywn#5QfqK literal 0 HcmV?d00001 From 572eb355e4e7034c04bc5e1e6bbed3161d04f2d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 19 Oct 2021 14:47:10 +0200 Subject: [PATCH 2/3] Store transform with clip nodes. Clip and geometry clip nodes were not being correctly applied with the current canvas transform from the time of their rendering. Make the nodes store the current canvas transform like the other drawing nodes. Fixes #6359. --- .../Rendering/SceneGraph/ClipNode.cs | 20 +++++++++++++++---- .../SceneGraph/DeferredDrawingContextImpl.cs | 12 +++++------ .../Rendering/SceneGraph/GeometryClipNode.cs | 14 +++++++++++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs index ada04bfefd..90430bed02 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs @@ -11,19 +11,23 @@ namespace Avalonia.Rendering.SceneGraph /// Initializes a new instance of the class that represents a /// clip push. /// + /// The current transform. /// The clip to push. - public ClipNode(Rect clip) + public ClipNode(Matrix transform, Rect clip) { + Transform = transform; Clip = clip; } - + /// /// Initializes a new instance of the class that represents a /// clip push. /// + /// The current transform. /// The clip to push. - public ClipNode(RoundedRect clip) + public ClipNode(Matrix transform, RoundedRect clip) { + Transform = transform; Clip = clip; } @@ -43,23 +47,31 @@ namespace Avalonia.Rendering.SceneGraph /// public RoundedRect? Clip { get; } + /// + /// Gets the transform with which the node will be drawn. + /// + public Matrix Transform { get; } + /// public bool HitTest(Point p) => false; /// /// Determines if this draw operation equals another. /// + /// The transform of the other draw operation. /// The clip of the other draw operation. /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(RoundedRect? clip) => Clip == clip; + public bool Equals(Matrix transform, RoundedRect? clip) => Transform == transform && Clip == clip; /// public void Render(IDrawingContextImpl context) { + context.Transform = Transform; + if (Clip.HasValue) { context.PushClip(Clip.Value); diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 3594cb59ee..f4039dc0bc 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -303,9 +303,9 @@ namespace Avalonia.Rendering.SceneGraph { var next = NextDrawAs(); - if (next == null || !next.Item.Equals(clip)) + if (next == null || !next.Item.Equals(Transform, clip)) { - Add(new ClipNode(clip)); + Add(new ClipNode(Transform, clip)); } else { @@ -318,9 +318,9 @@ namespace Avalonia.Rendering.SceneGraph { var next = NextDrawAs(); - if (next == null || !next.Item.Equals(clip)) + if (next == null || !next.Item.Equals(Transform, clip)) { - Add(new ClipNode(clip)); + Add(new ClipNode(Transform, clip)); } else { @@ -333,9 +333,9 @@ namespace Avalonia.Rendering.SceneGraph { var next = NextDrawAs(); - if (next == null || !next.Item.Equals(clip)) + if (next == null || !next.Item.Equals(Transform, clip)) { - Add(new GeometryClipNode(clip)); + Add(new GeometryClipNode(Transform, clip)); } else { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs index a2ad83d845..16092d4cbb 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs @@ -11,9 +11,11 @@ namespace Avalonia.Rendering.SceneGraph /// Initializes a new instance of the class that represents a /// geometry clip push. /// + /// The current transform. /// The clip to push. - public GeometryClipNode(IGeometryImpl clip) + public GeometryClipNode(Matrix transform, IGeometryImpl clip) { + Transform = transform; Clip = clip; } @@ -33,23 +35,31 @@ namespace Avalonia.Rendering.SceneGraph /// public IGeometryImpl Clip { get; } + /// + /// Gets the transform with which the node will be drawn. + /// + public Matrix Transform { get; } + /// public bool HitTest(Point p) => false; /// /// Determines if this draw operation equals another. /// + /// The transform of the other draw operation. /// The clip of the other draw operation. /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(IGeometryImpl clip) => Clip == clip; + public bool Equals(Matrix transform, IGeometryImpl clip) => Transform == transform && Clip == clip; /// public void Render(IDrawingContextImpl context) { + context.Transform = Transform; + if (Clip != null) { context.PushGeometryClip(Clip); From f8eb073dd77920d2130aa25c2125df0fe450eb8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 19 Oct 2021 22:32:15 +0200 Subject: [PATCH 3/3] Added missing D2D expected images. --- .../CustomRender/Clip_With_Transform.expected.png | Bin 0 -> 634 bytes .../GeometryClip_With_Transform.expected.png | Bin 0 -> 634 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png create mode 100644 tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png diff --git a/tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png b/tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7420223462ad22a619c2ddf03263d82e082643 GIT binary patch literal 634 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggNyXE}F{C2y?bVIE2NZZ198dhS7UnwjB)iKx^3dL|N$U*174$xpF|Rzw zT_eE3(&#YKh#A?|*5Wfh>%<)7XL3{!U>R}5{$mx45$c?sRcu;9!1Tl5>FVdQ&MBb@ E07J=cm;e9( literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png b/tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7420223462ad22a619c2ddf03263d82e082643 GIT binary patch literal 634 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggNyXE}F{C2y?bVIE2NZZ198dhS7UnwjB)iKx^3dL|N$U*174$xpF|Rzw zT_eE3(&#YKh#A?|*5Wfh>%<)7XL3{!U>R}5{$mx45$c?sRcu;9!1Tl5>FVdQ&MBb@ E07J=cm;e9( literal 0 HcmV?d00001