Merge pull request #1181 from AvaloniaUI/fixes/1178-rendering-artifacts

Fix rendering artifacts
This commit is contained in:
Nikita Tsukanov 2017-10-05 18:10:35 +03:00 коммит произвёл GitHub
Родитель 2d34764d9e 1888b04ee2
Коммит 9b22a5e26c
10 изменённых файлов: 97 добавлений и 37 удалений

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
@ -12,20 +11,16 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// Base class for draw operations that can use a brush.
/// </summary>
internal abstract class BrushDrawOperation : IDrawOperation
internal abstract class BrushDrawOperation : DrawOperation
{
/// <inheritdoc/>
public abstract Rect Bounds { get; }
/// <inheritdoc/>
public abstract bool HitTest(Point p);
public BrushDrawOperation(Rect bounds, Matrix transform, Pen pen)
: base(bounds, transform, pen)
{
}
/// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes.
/// </summary>
public abstract IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public abstract void Render(IDrawingContextImpl context);
}
}

Просмотреть файл

@ -0,0 +1,26 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Base class for draw operations that have bounds.
/// </summary>
internal abstract class DrawOperation : IDrawOperation
{
public DrawOperation(Rect bounds, Matrix transform, Pen pen)
{
bounds = bounds.Inflate((pen?.Thickness ?? 0) / 2).TransformToAABB(transform);
Bounds = new Rect(
new Point(Math.Floor(bounds.X), Math.Floor(bounds.Y)),
new Point(Math.Ceiling(bounds.Right), Math.Ceiling(bounds.Bottom)));
}
public Rect Bounds { get; }
public abstract bool HitTest(Point p);
public abstract void Render(IDrawingContextImpl context);
}
}

Просмотреть файл

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Pen pen,
IGeometryImpl geometry,
IDictionary<IVisual, Scene> childScenes = null)
: base(geometry.GetRenderBounds(pen?.Thickness ?? 0), transform, null)
{
Bounds = geometry.GetRenderBounds(pen?.Thickness ?? 0).TransformToAABB(transform);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

Просмотреть файл

@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph
public interface IDrawOperation
{
/// <summary>
/// Gets the bounds of the visible content in the node.
/// Gets the bounds of the visible content in the node in global coordinates.
/// </summary>
Rect Bounds { get; }

Просмотреть файл

@ -9,7 +9,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// A node in the scene graph which represents an image draw.
/// </summary>
internal class ImageNode : IDrawOperation
internal class ImageNode : DrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageNode"/> class.
@ -20,8 +20,8 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="sourceRect">The source rect.</param>
/// <param name="destRect">The destination rect.</param>
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
: base(destRect, transform, null)
{
Bounds = destRect.TransformToAABB(transform);
Transform = transform;
Source = source;
Opacity = opacity;
@ -29,9 +29,6 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect;
}
/// <inheritdoc/>
public Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
@ -80,7 +77,7 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
public override void Render(IDrawingContextImpl context)
{
// TODO: Probably need to introduce some kind of locking mechanism in the case of
// WriteableBitmap.
@ -89,6 +86,6 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public bool HitTest(Point p) => Bounds.Contains(p);
public override bool HitTest(Point p) => Bounds.Contains(p);
}
}

Просмотреть файл

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point p1,
Point p2,
IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(p1, p2), transform, pen)
{
Bounds = new Rect(p1, p2).TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Pen = pen?.ToImmutable();
P1 = p1;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

Просмотреть файл

@ -19,6 +19,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="bounds">The bounds of the mask.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene> childScenes = null)
: base(Rect.Empty, Matrix.Identity, null)
{
Mask = mask?.ToImmutable();
MaskBounds = bounds;
@ -30,12 +31,10 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop.
/// </summary>
public OpacityMaskNode()
: base(Rect.Empty, Matrix.Identity, null)
{
}
/// <inheritdoc/>
public override Rect Bounds => Rect.Empty;
/// <summary>
/// Gets the mask to be pushed or null if the operation represents a pop.
/// </summary>

Просмотреть файл

@ -30,8 +30,8 @@ namespace Avalonia.Rendering.SceneGraph
Rect rect,
float cornerRadius,
IDictionary<IVisual, Scene> childScenes = null)
: base(rect, transform, pen)
{
Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@ -40,9 +40,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

Просмотреть файл

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point origin,
IFormattedTextImpl text,
IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(origin, text.Size), transform, null)
{
Bounds = new Rect(origin, text.Size).TransformToAABB(transform);
Transform = transform;
Foreground = foreground?.ToImmutable();
Origin = origin;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

Просмотреть файл

@ -0,0 +1,55 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{
public class DrawOperationTests
{
[Fact]
public void Empty_Bounds_Remain_Empty()
{
var target = new TestDrawOperation(Rect.Empty, Matrix.Identity, null);
Assert.Equal(Rect.Empty, target.Bounds);
}
[Theory]
[InlineData(10, 10, 10, 10, 1, 1, 1, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1, 1, 2, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1.5, 1.5, 1, 14, 14, 17, 17)]
public void Rectangle_Bounds_Are_Snapped_To_Pixels(
double x,
double y,
double width,
double height,
double scaleX,
double scaleY,
double? penThickness,
double expectedX,
double expectedY,
double expectedWidth,
double expectedHeight)
{
var target = new TestDrawOperation(
new Rect(x, y, width, height),
Matrix.CreateScale(scaleX, scaleY),
penThickness.HasValue ? new Pen(Brushes.Black, penThickness.Value) : null);
Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), target.Bounds);
}
private class TestDrawOperation : DrawOperation
{
public TestDrawOperation(Rect bounds, Matrix transform, Pen pen)
:base(bounds, transform, pen)
{
}
public override bool HitTest(Point p) => false;
public override void Render(IDrawingContextImpl context) { }
}
}
}