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