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); 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/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png b/tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png new file mode 100644 index 0000000000..ce74202234 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png differ 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 0000000000..ce74202234 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png differ 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 0000000000..7c0bfa6fdc Binary files /dev/null and b/tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png differ 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 0000000000..7c0bfa6fdc Binary files /dev/null and b/tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png differ