Browse Source

Added IImageBrush and IVisualBrush

And make an immutable `SceneImageBrush` and `SceneVisualBrush` which
clone the mutable `ImageBrush` and `VisualBrush` for use in the scene
graph, as we shouldn't be accessing mutable state from the render
thread.
scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
d92fb0189f
  1. 7
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  2. 15
      src/Avalonia.Visuals/Media/IImageBrush.cs
  3. 39
      src/Avalonia.Visuals/Media/ITileBrush.cs
  4. 15
      src/Avalonia.Visuals/Media/IVisualBrush.cs
  5. 2
      src/Avalonia.Visuals/Media/ImageBush.cs
  6. 2
      src/Avalonia.Visuals/Media/TileBrush.cs
  7. 2
      src/Avalonia.Visuals/Media/VisualBrush.cs
  8. 4
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  9. 4
      src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs
  10. 4
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  11. 48
      src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
  12. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  13. 10
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  14. 37
      src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneImageBrush.cs
  15. 37
      src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneVisualBrush.cs
  16. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  17. 2
      src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs
  18. 4
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  19. 6
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  20. 39
      tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

7
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -69,6 +69,9 @@
<Compile Include="Media\BrushMappingMode.cs" />
<Compile Include="Media\Color.cs" />
<Compile Include="Media\Colors.cs" />
<Compile Include="Media\IImageBrush.cs" />
<Compile Include="Media\ITileBrush.cs" />
<Compile Include="Media\IVisualBrush.cs" />
<Compile Include="Media\TextWrapping.cs" />
<Compile Include="Media\TransformGroup.cs" />
<Compile Include="Media\DashStyle.cs" />
@ -124,12 +127,15 @@
<Compile Include="Rendering\RenderLayer.cs" />
<Compile Include="Rendering\RenderLayers.cs" />
<Compile Include="Rendering\SceneGraph\DeferredDrawingContextImpl.cs" />
<Compile Include="Rendering\SceneGraph\BrushDrawOperation.cs" />
<Compile Include="Rendering\SceneGraph\GeometryNode.cs" />
<Compile Include="Rendering\SceneGraph\IDrawOperation.cs" />
<Compile Include="Rendering\SceneGraph\ImageNode.cs" />
<Compile Include="Rendering\SceneGraph\ISceneBuilder.cs" />
<Compile Include="Rendering\SceneGraph\IVisualNode.cs" />
<Compile Include="Rendering\SceneGraph\LineNode.cs" />
<Compile Include="Rendering\SceneGraph\Media\SceneVisualBrush.cs" />
<Compile Include="Rendering\SceneGraph\Media\SceneImageBrush.cs" />
<Compile Include="Rendering\SceneGraph\RectangleNode.cs" />
<Compile Include="Rendering\SceneGraph\Scene.cs" />
<Compile Include="Rendering\SceneGraph\SceneBuilder.cs" />
@ -185,6 +191,7 @@
<Compile Include="VisualTree\VisualExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="ClassDiagram1.cd" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>

15
src/Avalonia.Visuals/Media/IImageBrush.cs

@ -0,0 +1,15 @@
using Avalonia.Media.Imaging;
namespace Avalonia.Media
{
/// <summary>
/// Paints an area with an <see cref="IBitmap"/>.
/// </summary>
public interface IImageBrush : ITileBrush
{
/// <summary>
/// Gets the image to draw.
/// </summary>
IBitmap Source { get; }
}
}

39
src/Avalonia.Visuals/Media/ITileBrush.cs

@ -0,0 +1,39 @@
namespace Avalonia.Media
{
/// <summary>
/// A brush which displays a repeating image.
/// </summary>
public interface ITileBrush : IBrush
{
/// <summary>
/// Gets the horizontal alignment of a tile in the destination.
/// </summary>
AlignmentX AlignmentX { get; }
/// <summary>
/// Gets the horizontal alignment of a tile in the destination.
/// </summary>
AlignmentY AlignmentY { get; }
/// <summary>
/// Gets the rectangle on the destination in which to paint a tile.
/// </summary>
RelativeRect DestinationRect { get; }
/// <summary>
/// Gets the rectangle of the source image that will be displayed.
/// </summary>
RelativeRect SourceRect { get; }
/// <summary>
/// Gets a value indicating how the source rectangle will be stretched to fill the
/// destination rect.
/// </summary>
Stretch Stretch { get; }
/// <summary>
/// Gets the brush's tile mode.
/// </summary>
TileMode TileMode { get; }
}
}

15
src/Avalonia.Visuals/Media/IVisualBrush.cs

@ -0,0 +1,15 @@
using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// </summary>
public interface IVisualBrush : ITileBrush
{
/// <summary>
/// Gets the visual to draw.
/// </summary>
IVisual Visual { get; }
}
}

2
src/Avalonia.Visuals/Media/ImageBush.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media
/// <summary>
/// Paints an area with an <see cref="IBitmap"/>.
/// </summary>
public class ImageBrush : TileBrush
public class ImageBrush : TileBrush, IImageBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.

2
src/Avalonia.Visuals/Media/TileBrush.cs

@ -37,7 +37,7 @@ namespace Avalonia.Media
/// <summary>
/// Base class for brushes which display repeating images.
/// </summary>
public abstract class TileBrush : Brush
public abstract class TileBrush : Brush, ITileBrush
{
/// <summary>
/// Defines the <see cref="AlignmentX"/> property.

2
src/Avalonia.Visuals/Media/VisualBrush.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// </summary>
public class VisualBrush : TileBrush
public class VisualBrush : TileBrush, IVisualBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.

4
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -105,7 +105,7 @@ namespace Avalonia.Rendering
Render(_scene);
}
Size IVisualBrushRenderer.GetRenderTargetSize(VisualBrush brush)
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{
if (brush.Visual != null)
{
@ -115,7 +115,7 @@ namespace Avalonia.Rendering
return Size.Empty;
}
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, VisualBrush brush)
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
if (brush.Visual != null)
{

4
src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs

@ -17,7 +17,7 @@ namespace Avalonia.Rendering
/// </summary>
/// <param name="brush">The visual brush.</param>
/// <returns>The size of the intermediate render target to create.</returns>
Size GetRenderTargetSize(VisualBrush brush);
Size GetRenderTargetSize(IVisualBrush brush);
/// <summary>
/// Renders a visual brush to a bitmap.
@ -25,6 +25,6 @@ namespace Avalonia.Rendering
/// <param name="context">The drawing context to render to.</param>
/// <param name="brush">The visual brush.</param>
/// <returns>A bitmap containing the rendered brush.</returns>
void RenderVisualBrush(IDrawingContextImpl context, VisualBrush brush);
void RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush);
}
}

4
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -79,13 +79,13 @@ namespace Avalonia.Rendering
{
}
Size IVisualBrushRenderer.GetRenderTargetSize(VisualBrush brush)
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{
(brush.Visual as IVisualBrushInitialize)?.EnsureInitialized();
return brush.Visual?.Bounds.Size ?? Size.Empty;
}
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, VisualBrush brush)
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
var visual = brush.Visual;
Render(new DrawingContext(context), visual, visual.Bounds);

48
src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs

@ -0,0 +1,48 @@
using System;
using Avalonia.Media;
using Avalonia.Rendering.SceneGraph.Media;
namespace Avalonia.Rendering.SceneGraph
{
public abstract class BrushDrawOperation : IDrawOperation
{
public abstract Rect Bounds { get; }
public abstract bool HitTest(Point p);
public abstract void Render(IDrawingContextImpl context);
protected IBrush Convert(IBrush brush)
{
var imageBrush = brush as ImageBrush;
var visualBrush = brush as VisualBrush;
if (imageBrush != null)
{
return new SceneImageBrush(imageBrush);
}
else if (visualBrush != null)
{
return new SceneVisualBrush(visualBrush);
}
else
{
return brush;
}
}
protected Pen Convert(Pen pen)
{
var brush = pen?.Brush != null ? Convert(pen.Brush) : null;
return ReferenceEquals(pen?.Brush, brush) ?
pen :
new Pen(
pen.Brush,
thickness: pen.Thickness,
dashStyle: pen.DashStyle,
dashCap: pen.DashCap,
startLineCap: pen.StartLineCap,
endLineCap: pen.EndLineCap,
lineJoin: pen.LineJoin,
miterLimit: pen.MiterLimit);
}
}
}

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

@ -7,18 +7,18 @@ using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
public class GeometryNode : IDrawOperation
public class GeometryNode : BrushDrawOperation
{
public GeometryNode(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry)
{
Bounds = geometry.GetRenderBounds(pen.Thickness).TransformToAABB(transform);
Transform = transform;
Brush = brush;
Pen = pen;
Brush = Convert(brush);
Pen = Convert(pen);
Geometry = geometry;
}
public Rect Bounds { get; }
public override Rect Bounds { get; }
public Matrix Transform { get; }
public IBrush Brush { get; }
public Pen Pen { get; }
@ -32,13 +32,13 @@ namespace Avalonia.Rendering.SceneGraph
Equals(geometry, Geometry);
}
public void Render(IDrawingContextImpl context)
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawGeometry(Brush, Pen, Geometry);
}
public bool HitTest(Point p)
public override bool HitTest(Point p)
{
p *= Transform.Invert();
return (Brush != null && Geometry.FillContains(p)) ||

10
src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs

@ -6,18 +6,18 @@ using Avalonia.Media;
namespace Avalonia.Rendering.SceneGraph
{
public class LineNode : IDrawOperation
public class LineNode : BrushDrawOperation
{
public LineNode(Matrix transform, Pen pen, Point p1, Point p2)
{
Bounds = new Rect(P1, P2);
Transform = transform;
Pen = pen;
Pen = Convert(pen);
P1 = p1;
P2 = p2;
}
public Rect Bounds { get; }
public override Rect Bounds { get; }
public Matrix Transform { get; }
public Pen Pen { get; }
public Point P1 { get; }
@ -28,13 +28,13 @@ namespace Avalonia.Rendering.SceneGraph
return transform == Transform && pen == Pen && p1 == P1 && p2 == P2;
}
public void Render(IDrawingContextImpl context)
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawLine(Pen, P1, P2);
}
public bool HitTest(Point p)
public override bool HitTest(Point p)
{
// TODO: Implement line hit testing.
return false;

37
src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneImageBrush.cs

@ -0,0 +1,37 @@
using System;
using Avalonia.Media;
using Avalonia.Media.Imaging;
namespace Avalonia.Rendering.SceneGraph.Media
{
internal class SceneImageBrush : IImageBrush
{
public SceneImageBrush(IImageBrush source)
{
AlignmentX = source.AlignmentX;
AlignmentY = source.AlignmentY;
DestinationRect = source.DestinationRect;
Opacity = source.Opacity;
Source = source.Source;
SourceRect = source.SourceRect;
Stretch = source.Stretch;
TileMode = source.TileMode;
}
public AlignmentX AlignmentX { get; }
public AlignmentY AlignmentY { get; }
public RelativeRect DestinationRect { get; }
public double Opacity { get; }
public IBitmap Source { get; }
public RelativeRect SourceRect { get; }
public Stretch Stretch { get; }
public TileMode TileMode { get; }
}
}

37
src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneVisualBrush.cs

@ -0,0 +1,37 @@
using System;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph.Media
{
internal class SceneVisualBrush : IVisualBrush
{
public SceneVisualBrush(IVisualBrush source)
{
AlignmentX = source.AlignmentX;
AlignmentY = source.AlignmentY;
DestinationRect = source.DestinationRect;
Opacity = source.Opacity;
SourceRect = source.SourceRect;
Stretch = source.Stretch;
TileMode = source.TileMode;
Visual = source.Visual;
}
public AlignmentX AlignmentX { get; }
public AlignmentY AlignmentY { get; }
public RelativeRect DestinationRect { get; }
public double Opacity { get; }
public RelativeRect SourceRect { get; }
public Stretch Stretch { get; }
public TileMode TileMode { get; }
public IVisual Visual { get; }
}
}

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

@ -6,19 +6,19 @@ using Avalonia.Media;
namespace Avalonia.Rendering.SceneGraph
{
public class RectangleNode : IDrawOperation
public class RectangleNode : BrushDrawOperation
{
public RectangleNode(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius)
{
Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Brush = brush;
Pen = pen;
Brush = Convert(brush);
Pen = Convert(pen);
Rect = rect;
CornerRadius = cornerRadius;
}
public Rect Bounds { get; }
public override Rect Bounds { get; }
public Matrix Transform { get; }
public IBrush Brush { get; }
public Pen Pen { get; }
@ -34,7 +34,7 @@ namespace Avalonia.Rendering.SceneGraph
cornerRadius == CornerRadius;
}
public void Render(IDrawingContextImpl context)
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
@ -49,6 +49,6 @@ namespace Avalonia.Rendering.SceneGraph
}
}
public bool HitTest(Point p) => Bounds.Contains(p);
public override bool HitTest(Point p) => Bounds.Contains(p);
}
}

2
src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs

@ -18,7 +18,7 @@ namespace Avalonia.Rendering.Utilities
/// <param name="brush">The brush to be rendered.</param>
/// <param name="contentSize">The size of the content of the tile brush.</param>
/// <param name="targetSize">The size of the control to which the brush is being rendered.</param>
public TileBrushCalculator(TileBrush brush, Size contentSize, Size targetSize)
public TileBrushCalculator(ITileBrush brush, Size contentSize, Size targetSize)
: this(
brush.TileMode,
brush.Stretch,

4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -321,8 +321,8 @@ namespace Avalonia.Direct2D1.Media
var solidColorBrush = brush as Avalonia.Media.ISolidColorBrush;
var linearGradientBrush = brush as Avalonia.Media.LinearGradientBrush;
var radialGradientBrush = brush as Avalonia.Media.RadialGradientBrush;
var imageBrush = brush as Avalonia.Media.ImageBrush;
var visualBrush = brush as Avalonia.Media.VisualBrush;
var imageBrush = brush as Avalonia.Media.IImageBrush;
var visualBrush = brush as Avalonia.Media.IVisualBrush;
if (solidColorBrush != null)
{

6
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@ -11,7 +11,7 @@ namespace Avalonia.Direct2D1.Media
public sealed class ImageBrushImpl : BrushImpl
{
public ImageBrushImpl(
TileBrush brush,
ITileBrush brush,
SharpDX.Direct2D1.RenderTarget target,
BitmapImpl bitmap,
Size targetSize)
@ -45,7 +45,7 @@ namespace Avalonia.Direct2D1.Media
base.Dispose();
}
private static BitmapBrushProperties GetBitmapBrushProperties(TileBrush brush)
private static BitmapBrushProperties GetBitmapBrushProperties(ITileBrush brush)
{
var tileMode = brush.TileMode;
@ -56,7 +56,7 @@ namespace Avalonia.Direct2D1.Media
};
}
private static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect)
private static BrushProperties GetBrushProperties(ITileBrush brush, Rect destinationRect)
{
var tileTransform =
brush.TileMode != TileMode.None ?

39
tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

@ -2,51 +2,12 @@
<Settings>
<DefaultTestTimeout>1000</DefaultTestTimeout>
<IgnoredTests>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipX_TopLeftDest</TestName>
</NamedTestSelector>
<FixtureTestSelector>
<FixtureName>Avalonia.Direct2D1.RenderTests.Media.LinearGradientBrushTests</FixtureName>
</FixtureTestSelector>
<FixtureTestSelector>
<FixtureName>Avalonia.Direct2D1.RenderTests.Media.VisualBrushTests</FixtureName>
</FixtureTestSelector>
<FixtureTestSelector>
<FixtureName>Avalonia.Direct2D1.RenderTests.Controls.BorderTests</FixtureName>
</FixtureTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipXY_TopLeftDest</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipY_TopLeftDest</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_BottomRight</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_Center</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_TopLeft</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterDest</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_Uniform_NoTile</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_UniformToFill_NoTile</TestName>
</NamedTestSelector>
<FixtureTestSelector>
<FixtureName>Avalonia.Direct2D1.RenderTests.Controls.ImageTests</FixtureName>
</FixtureTestSelector>

Loading…
Cancel
Save