diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index 21978720e9..9282290177 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -69,6 +69,9 @@ + + + @@ -124,12 +127,15 @@ + + + @@ -185,6 +191,7 @@ + diff --git a/src/Avalonia.Visuals/Media/IImageBrush.cs b/src/Avalonia.Visuals/Media/IImageBrush.cs new file mode 100644 index 0000000000..aaa481bd28 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IImageBrush.cs @@ -0,0 +1,15 @@ +using Avalonia.Media.Imaging; + +namespace Avalonia.Media +{ + /// + /// Paints an area with an . + /// + public interface IImageBrush : ITileBrush + { + /// + /// Gets the image to draw. + /// + IBitmap Source { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/ITileBrush.cs b/src/Avalonia.Visuals/Media/ITileBrush.cs new file mode 100644 index 0000000000..8e2349f506 --- /dev/null +++ b/src/Avalonia.Visuals/Media/ITileBrush.cs @@ -0,0 +1,39 @@ +namespace Avalonia.Media +{ + /// + /// A brush which displays a repeating image. + /// + public interface ITileBrush : IBrush + { + /// + /// Gets the horizontal alignment of a tile in the destination. + /// + AlignmentX AlignmentX { get; } + + /// + /// Gets the horizontal alignment of a tile in the destination. + /// + AlignmentY AlignmentY { get; } + + /// + /// Gets the rectangle on the destination in which to paint a tile. + /// + RelativeRect DestinationRect { get; } + + /// + /// Gets the rectangle of the source image that will be displayed. + /// + RelativeRect SourceRect { get; } + + /// + /// Gets a value indicating how the source rectangle will be stretched to fill the + /// destination rect. + /// + Stretch Stretch { get; } + + /// + /// Gets the brush's tile mode. + /// + TileMode TileMode { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/IVisualBrush.cs b/src/Avalonia.Visuals/Media/IVisualBrush.cs new file mode 100644 index 0000000000..e74892b218 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IVisualBrush.cs @@ -0,0 +1,15 @@ +using Avalonia.VisualTree; + +namespace Avalonia.Media +{ + /// + /// Paints an area with an . + /// + public interface IVisualBrush : ITileBrush + { + /// + /// Gets the visual to draw. + /// + IVisual Visual { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/ImageBush.cs b/src/Avalonia.Visuals/Media/ImageBush.cs index caf43d19a2..85c1f6c71a 100644 --- a/src/Avalonia.Visuals/Media/ImageBush.cs +++ b/src/Avalonia.Visuals/Media/ImageBush.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media /// /// Paints an area with an . /// - public class ImageBrush : TileBrush + public class ImageBrush : TileBrush, IImageBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/TileBrush.cs b/src/Avalonia.Visuals/Media/TileBrush.cs index 1708c7e9f6..3a7f9d9920 100644 --- a/src/Avalonia.Visuals/Media/TileBrush.cs +++ b/src/Avalonia.Visuals/Media/TileBrush.cs @@ -37,7 +37,7 @@ namespace Avalonia.Media /// /// Base class for brushes which display repeating images. /// - public abstract class TileBrush : Brush + public abstract class TileBrush : Brush, ITileBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/VisualBrush.cs b/src/Avalonia.Visuals/Media/VisualBrush.cs index 41a61f6022..85ccf7afc9 100644 --- a/src/Avalonia.Visuals/Media/VisualBrush.cs +++ b/src/Avalonia.Visuals/Media/VisualBrush.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media /// /// Paints an area with an . /// - public class VisualBrush : TileBrush + public class VisualBrush : TileBrush, IVisualBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index a17a5119a5..9f49d48da0 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/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) { diff --git a/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs index 6c410587fe..0eeb608955 100644 --- a/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs @@ -17,7 +17,7 @@ namespace Avalonia.Rendering /// /// The visual brush. /// The size of the intermediate render target to create. - Size GetRenderTargetSize(VisualBrush brush); + Size GetRenderTargetSize(IVisualBrush brush); /// /// Renders a visual brush to a bitmap. @@ -25,6 +25,6 @@ namespace Avalonia.Rendering /// The drawing context to render to. /// The visual brush. /// A bitmap containing the rendered brush. - void RenderVisualBrush(IDrawingContextImpl context, VisualBrush brush); + void RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush); } } diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index bcd84682be..da95645571 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/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); diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs new file mode 100644 index 0000000000..bc003563e1 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs index fbc7be6e67..f4dbdbecf6 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs +++ b/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)) || diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs index 61ffada7d2..16e7c65cad 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs +++ b/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; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneImageBrush.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneImageBrush.cs new file mode 100644 index 0000000000..d17ebdbfe4 --- /dev/null +++ b/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; } + } +} diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneVisualBrush.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Media/SceneVisualBrush.cs new file mode 100644 index 0000000000..67407d01be --- /dev/null +++ b/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; } + } +} diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs index e73684afa6..88756b34ab 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs +++ b/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); } } diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs index 5a8adc5c45..52a8fb5ab7 100644 --- a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs @@ -18,7 +18,7 @@ namespace Avalonia.Rendering.Utilities /// The brush to be rendered. /// The size of the content of the tile brush. /// The size of the control to which the brush is being rendered. - public TileBrushCalculator(TileBrush brush, Size contentSize, Size targetSize) + public TileBrushCalculator(ITileBrush brush, Size contentSize, Size targetSize) : this( brush.TileMode, brush.Stretch, diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 9f3360155b..19d90d15f6 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/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) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 6840d2327a..ed3d78b4fd 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/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 ? diff --git a/tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject b/tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject index 5ca3dbf2e3..f4340c8042 100644 --- a/tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject +++ b/tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject @@ -2,51 +2,12 @@ 1000 - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipX_TopLeftDest - Avalonia.Direct2D1.RenderTests.Media.LinearGradientBrushTests - - Avalonia.Direct2D1.RenderTests.Media.VisualBrushTests - Avalonia.Direct2D1.RenderTests.Controls.BorderTests - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipXY_TopLeftDest - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipY_TopLeftDest - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_BottomRight - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_Center - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_TopLeft - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterDest - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_Uniform_NoTile - - - Avalonia.Direct2D1.RenderTests.Media.ImageBrushTests.ImageBrush_UniformToFill_NoTile - Avalonia.Direct2D1.RenderTests.Controls.ImageTests