Skip to content

Commit

Permalink
Merge pull request #6764 from AvaloniaUI/fixes/6359-clip-transform
Browse files Browse the repository at this point in the history
Fix clips with transforms in deferred renderer.
  • Loading branch information
grokys authored Oct 20, 2021
2 parents 17ede51 + 3b2f9ee commit ec482c0
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 12 deletions.
20 changes: 16 additions & 4 deletions src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,23 @@ internal class ClipNode : IDrawOperation
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
/// clip push.
/// </summary>
/// <param name="transform">The current transform.</param>
/// <param name="clip">The clip to push.</param>
public ClipNode(Rect clip)
public ClipNode(Matrix transform, Rect clip)
{
Transform = transform;
Clip = clip;
}

/// <summary>
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
/// clip push.
/// </summary>
/// <param name="transform">The current transform.</param>
/// <param name="clip">The clip to push.</param>
public ClipNode(RoundedRect clip)
public ClipNode(Matrix transform, RoundedRect clip)
{
Transform = transform;
Clip = clip;
}

Expand All @@ -43,23 +47,31 @@ public ClipNode()
/// </summary>
public RoundedRect? Clip { get; }

/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }

/// <inheritdoc/>
public bool HitTest(Point p) => false;

/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="clip">The clip of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(RoundedRect? clip) => Clip == clip;
public bool Equals(Matrix transform, RoundedRect? clip) => Transform == transform && Clip == clip;

/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
{
context.Transform = Transform;

if (Clip.HasValue)
{
context.PushClip(Clip.Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ public void PushClip(Rect clip)
{
var next = NextDrawAs<ClipNode>();

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
{
Expand All @@ -318,9 +318,9 @@ public void PushClip(RoundedRect clip)
{
var next = NextDrawAs<ClipNode>();

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
{
Expand All @@ -333,9 +333,9 @@ public void PushGeometryClip(IGeometryImpl clip)
{
var next = NextDrawAs<GeometryClipNode>();

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
{
Expand Down
14 changes: 12 additions & 2 deletions src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ internal class GeometryClipNode : IDrawOperation
/// Initializes a new instance of the <see cref="GeometryClipNode"/> class that represents a
/// geometry clip push.
/// </summary>
/// <param name="transform">The current transform.</param>
/// <param name="clip">The clip to push.</param>
public GeometryClipNode(IGeometryImpl clip)
public GeometryClipNode(Matrix transform, IGeometryImpl clip)
{
Transform = transform;
Clip = clip;
}

Expand All @@ -33,23 +35,31 @@ public GeometryClipNode()
/// </summary>
public IGeometryImpl Clip { get; }

/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }

/// <inheritdoc/>
public bool HitTest(Point p) => false;

/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="clip">The clip of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(IGeometryImpl clip) => Clip == clip;
public bool Equals(Matrix transform, IGeometryImpl clip) => Transform == transform && Clip == clip;

/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
{
context.Transform = Transform;

if (Clip != null)
{
context.PushGeometryClip(Clip);
Expand Down
48 changes: 48 additions & 0 deletions tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,54 @@ public async Task GeometryClip()
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()
{
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ec482c0

Please sign in to comment.