Browse Source

Merge pull request #6764 from AvaloniaUI/fixes/6359-clip-transform

Fix clips with transforms in deferred renderer.
release/0.10.9
Steven Kirk 4 years ago
committed by Dan Walmsley
parent
commit
fc3633c192
  1. 20
      src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
  2. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  3. 14
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs
  4. 48
      tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs
  5. BIN
      tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png
  6. BIN
      tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png
  7. BIN
      tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png
  8. BIN
      tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png

20
src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs

@ -11,19 +11,23 @@ namespace Avalonia.Rendering.SceneGraph
/// 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;
}
@ -43,23 +47,31 @@ namespace Avalonia.Rendering.SceneGraph
/// </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);

12
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -303,9 +303,9 @@ namespace Avalonia.Rendering.SceneGraph
{
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
{
@ -318,9 +318,9 @@ namespace Avalonia.Rendering.SceneGraph
{
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
{
@ -333,9 +333,9 @@ namespace Avalonia.Rendering.SceneGraph
{
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
{

14
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs

@ -11,9 +11,11 @@ namespace Avalonia.Rendering.SceneGraph
/// 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;
}
@ -33,23 +35,31 @@ namespace Avalonia.Rendering.SceneGraph
/// </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);

48
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()
{

BIN
tests/TestFiles/Direct2D1/Controls/CustomRender/Clip_With_Transform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
tests/TestFiles/Direct2D1/Controls/CustomRender/GeometryClip_With_Transform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
tests/TestFiles/Skia/Controls/CustomRender/Clip_With_Transform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
tests/TestFiles/Skia/Controls/CustomRender/GeometryClip_With_Transform.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Loading…
Cancel
Save