From 79422fc45bee242e6da7e839c30a6128a1b0e61a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 02:07:27 +0200 Subject: [PATCH 01/19] Started implementing VisualBrush. --- Perspex.sln | 2 +- src/Perspex.SceneGraph/Media/Brush.cs | 2 +- src/Perspex.SceneGraph/Media/VisualBrush.cs | 29 ++++++++ .../Perspex.SceneGraph.csproj | 1 + src/Windows/Perspex.Direct2D1/Disposable.cs | 39 ++++++++++ .../Perspex.Direct2D1/Media/DrawingContext.cs | 74 +++++++++++++++++-- .../Media/PerspexTextRenderer.cs | 7 +- .../Perspex.Direct2D1.csproj | 5 ++ .../Perspex.Direct2D1/PrimitiveExtensions.cs | 21 ------ .../Brushes/VisualBrushTests.cs | 58 +++++++++++++++ 10 files changed, 207 insertions(+), 31 deletions(-) create mode 100644 src/Perspex.SceneGraph/Media/VisualBrush.cs create mode 100644 src/Windows/Perspex.Direct2D1/Disposable.cs create mode 100644 tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs diff --git a/Perspex.sln b/Perspex.sln index 3c1892f48d..4952c2b257 100644 --- a/Perspex.sln +++ b/Perspex.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22823.1 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Win32", "src\Windows\Perspex.Win32\Perspex.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}" EndProject diff --git a/src/Perspex.SceneGraph/Media/Brush.cs b/src/Perspex.SceneGraph/Media/Brush.cs index f86a7d2560..5a3b6c3493 100644 --- a/src/Perspex.SceneGraph/Media/Brush.cs +++ b/src/Perspex.SceneGraph/Media/Brush.cs @@ -9,7 +9,7 @@ namespace Perspex.Media /// /// Describes how an area is painted. /// - public abstract class Brush + public abstract class Brush : PerspexObject { } } diff --git a/src/Perspex.SceneGraph/Media/VisualBrush.cs b/src/Perspex.SceneGraph/Media/VisualBrush.cs new file mode 100644 index 0000000000..73feade478 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/VisualBrush.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + public class VisualBrush : Brush + { + public static readonly PerspexProperty VisualProperty = + PerspexProperty.Register("Visual"); + + public VisualBrush() + { + } + + public VisualBrush(IVisual visual) + { + this.Visual = visual; + } + + public IVisual Visual + { + get { return this.GetValue(VisualProperty); } + set { this.SetValue(VisualProperty, value); } + } + } +} diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 3b5280b26a..765915b16a 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -86,6 +86,7 @@ + diff --git a/src/Windows/Perspex.Direct2D1/Disposable.cs b/src/Windows/Perspex.Direct2D1/Disposable.cs new file mode 100644 index 0000000000..f42a044686 --- /dev/null +++ b/src/Windows/Perspex.Direct2D1/Disposable.cs @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Direct2D1 +{ + using System; + + public class Disposable : IDisposable where T : IDisposable + { + private IDisposable extra; + + public Disposable(T inner) + { + this.Inner = inner; + } + + public Disposable(T inner, IDisposable extra) + { + this.Inner = inner; + this.extra = extra; + } + + public T Inner { get; } + + public static implicit operator T(Disposable i) + { + return i.Inner; + } + + public void Dispose() + { + this.Inner.Dispose(); + this.extra?.Dispose(); + } + } +} diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index fce32ace29..f695e8335e 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -8,6 +8,7 @@ namespace Perspex.Direct2D1.Media { using System; using System.Reactive.Disposables; + using Layout; using Perspex.Media; using SharpDX; using SharpDX.Direct2D1; @@ -89,7 +90,7 @@ namespace Perspex.Direct2D1.Media { if (pen != null) { - using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget)) + using (var d2dBrush = this.CreateBrush(pen.Brush)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { this.renderTarget.DrawLine( @@ -112,7 +113,7 @@ namespace Perspex.Direct2D1.Media { if (brush != null) { - using (var d2dBrush = brush.ToDirect2D(this.renderTarget)) + using (var d2dBrush = this.CreateBrush(brush)) { GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl; this.renderTarget.FillGeometry(impl.Geometry, d2dBrush); @@ -121,7 +122,7 @@ namespace Perspex.Direct2D1.Media if (pen != null) { - using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget)) + using (var d2dBrush = this.CreateBrush(pen.Brush)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl; @@ -138,7 +139,7 @@ namespace Perspex.Direct2D1.Media /// The corner radius. public void DrawRectange(Pen pen, Rect rect, float cornerRadius) { - using (var brush = pen.Brush.ToDirect2D(this.renderTarget)) + using (var brush = this.CreateBrush(pen.Brush)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { this.renderTarget.DrawRoundedRectangle( @@ -161,7 +162,8 @@ namespace Perspex.Direct2D1.Media { var impl = (FormattedTextImpl)text.PlatformImpl; - using (var renderer = new PerspexTextRenderer(this.renderTarget, foreground.ToDirect2D(this.renderTarget))) + using (var brush = this.CreateBrush(foreground)) + using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush)) { impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y); } @@ -176,7 +178,7 @@ namespace Perspex.Direct2D1.Media /// The corner radius. public void FillRectange(Perspex.Media.Brush brush, Rect rect, float cornerRadius) { - using (var b = brush.ToDirect2D(this.renderTarget)) + using (var b = this.CreateBrush(brush)) { this.renderTarget.FillRoundedRectangle( new RoundedRectangle @@ -256,5 +258,65 @@ namespace Perspex.Direct2D1.Media this.renderTarget.Transform = transform * m3x2; }); } + + /// + /// Creates a Direct2D brush from a Perspex brush. + /// + /// The perspex brush. + /// The Direct2D brush. + public Disposable CreateBrush(Perspex.Media.Brush brush) + { + var solidColorBrush = brush as Perspex.Media.SolidColorBrush; + var visualBrush = brush as Perspex.Media.VisualBrush; + + if (solidColorBrush != null) + { + return new Disposable( + new SharpDX.Direct2D1.SolidColorBrush( + this.renderTarget, + solidColorBrush.Color.ToDirect2D())); + } + else if (visualBrush != null) + { + return this.CreateBrush(visualBrush); + } + else + { + // TODO: Implement other brushes. + return new Disposable( + new SharpDX.Direct2D1.SolidColorBrush(this.renderTarget, new Color4())); + } + } + + /// + /// Creates a Direct2D from a Perspex . + /// + /// The perspex brush. + /// The Direct2D brush. + private Disposable CreateBrush(VisualBrush brush) + { + var visual = brush.Visual; + var layoutable = visual as ILayoutable; + + if (layoutable?.IsArrangeValid == false) + { + layoutable.Measure(Size.Infinity); + layoutable.Arrange(new Rect(layoutable.DesiredSize)); + } + + using (var target = new BitmapRenderTarget( + this.renderTarget, + CompatibleRenderTargetOptions.None, + visual.Bounds.Size.ToSharpDX())) + { + var renderer = new Renderer(target); + renderer.Render(visual, null); + + var result = new BitmapBrush(this.renderTarget, target.Bitmap); + result.ExtendModeX = ExtendMode.Wrap; + result.ExtendModeY = ExtendMode.Wrap; + return new Disposable(result, target); + } + } } } diff --git a/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs b/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs index 3e6888fb64..5566c2ff60 100644 --- a/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs +++ b/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs @@ -13,14 +13,18 @@ namespace Perspex.Direct2D1.Media internal class PerspexTextRenderer : TextRenderer { + private DrawingContext context; + private RenderTarget renderTarget; private Brush foreground; public PerspexTextRenderer( + DrawingContext context, RenderTarget target, Brush foreground) { + this.context = context; this.renderTarget = target; this.foreground = foreground; } @@ -33,7 +37,6 @@ namespace Perspex.Direct2D1.Media public void Dispose() { - this.foreground.Dispose(); } public Result DrawGlyphRun( @@ -48,7 +51,7 @@ namespace Perspex.Direct2D1.Media var wrapper = clientDrawingEffect as BrushWrapper; var brush = (wrapper == null) ? this.foreground : - wrapper.Brush.ToDirect2D(this.renderTarget); + this.context.CreateBrush(wrapper.Brush); this.renderTarget.DrawGlyphRun( new Vector2(baselineOriginX, baselineOriginY), diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 73ce0904a4..99e3b2e643 100644 --- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -74,6 +74,7 @@ Properties\SharedAssemblyInfo.cs + @@ -101,6 +102,10 @@ {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base + + {42472427-4774-4c81-8aff-9f27b8e31721} + Perspex.Layout + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph diff --git a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs index ef44de77db..54c0da9c06 100644 --- a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs @@ -34,27 +34,6 @@ namespace Perspex.Direct2D1 return new Size2F((float)p.Width, (float)p.Height); } - /// - /// Converts a brush to Direct2D. - /// - /// The brush to convert. - /// The render target. - /// The Direct2D brush. - public static SharpDX.Direct2D1.Brush ToDirect2D(this Perspex.Media.Brush brush, RenderTarget target) - { - Perspex.Media.SolidColorBrush solidColorBrush = brush as Perspex.Media.SolidColorBrush; - - if (solidColorBrush != null) - { - return new SharpDX.Direct2D1.SolidColorBrush(target, solidColorBrush.Color.ToDirect2D()); - } - else - { - // TODO: Implement other brushes. - return new SharpDX.Direct2D1.SolidColorBrush(target, new Color4()); - } - } - /// /// Converts a pen to a Direct2D stroke style. /// diff --git a/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs b/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs new file mode 100644 index 0000000000..772d3a4917 --- /dev/null +++ b/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Direct2D1.RenderTests.Controls +{ + using Perspex.Controls; + using Perspex.Controls.Shapes; + using Perspex.Layout; + using Perspex.Media; + using Xunit; + + public class VisualBrushTests : TestBase + { + public VisualBrushTests() + : base(@"Brushes\VisualBrush") + { + } + + [Fact] + public void VisualBrush_Should_Draw_Visual() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + } +} From a1ee3efab8e45d421a850cec8a93a8db744eab9a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 15:58:15 +0200 Subject: [PATCH 02/19] More work on VisualBrush, not entirely correct yet. --- src/Perspex.Controls/Image.cs | 35 +---- src/Perspex.SceneGraph/Media/AlignmentX.cs | 29 +++++ src/Perspex.SceneGraph/Media/AlignmentY.cs | 29 +++++ .../Media/MediaExtensions.cs | 47 +++++++ src/Perspex.SceneGraph/Media/TileBrush.cs | 90 +++++++++++++ src/Perspex.SceneGraph/Media/VisualBrush.cs | 18 ++- .../Perspex.SceneGraph.csproj | 5 + src/Perspex.SceneGraph/RelativeRect.cs | 105 +++++++++++++++ .../Rendering/RendererBase.cs | 15 ++- .../Perspex.Direct2D1/Media/DrawingContext.cs | 32 +++-- .../Media/PerspexTextRenderer.cs | 4 +- .../Brushes/VisualBrushTests.cs | 120 +++++++++++++++++- .../Perspex.Direct2D1.RenderTests.csproj | 1 + 13 files changed, 480 insertions(+), 50 deletions(-) create mode 100644 src/Perspex.SceneGraph/Media/AlignmentX.cs create mode 100644 src/Perspex.SceneGraph/Media/AlignmentY.cs create mode 100644 src/Perspex.SceneGraph/Media/MediaExtensions.cs create mode 100644 src/Perspex.SceneGraph/Media/TileBrush.cs create mode 100644 src/Perspex.SceneGraph/RelativeRect.cs diff --git a/src/Perspex.Controls/Image.cs b/src/Perspex.Controls/Image.cs index 698d4dc206..5e10e5daab 100644 --- a/src/Perspex.Controls/Image.cs +++ b/src/Perspex.Controls/Image.cs @@ -57,7 +57,7 @@ namespace Perspex.Controls { Rect viewPort = new Rect(this.Bounds.Size); Size sourceSize = new Size(source.PixelWidth, source.PixelHeight); - Vector scale = CalculateScaling(this.Bounds.Size, sourceSize, this.Stretch); + Vector scale = this.Stretch.CalculateScaling(this.Bounds.Size, sourceSize); Size scaledSize = sourceSize * scale; Rect destRect = viewPort .CenterIn(new Rect(scaledSize)) @@ -95,41 +95,10 @@ namespace Perspex.Controls availableSize = new Size(availableSize.Width, this.Height); } - scale = CalculateScaling(availableSize, new Size(width, height), this.Stretch); + scale = this.Stretch.CalculateScaling(availableSize, new Size(width, height)); } return new Size(width * scale.X, height * scale.Y); } - - /// - /// Calculates the scaling for the image. - /// - /// The size available to display the image. - /// The pxiel size of the image. - /// The stretch mode of the control. - /// A vector with the X and Y scaling factors. - private static Vector CalculateScaling(Size availableSize, Size imageSize, Stretch stretch) - { - double scaleX = 1; - double scaleY = 1; - - if (stretch != Stretch.None) - { - scaleX = availableSize.Width / imageSize.Width; - scaleY = availableSize.Height / imageSize.Height; - - switch (stretch) - { - case Stretch.Uniform: - scaleX = scaleY = Math.Min(scaleX, scaleY); - break; - case Stretch.UniformToFill: - scaleX = scaleY = Math.Max(scaleX, scaleY); - break; - } - } - - return new Vector(scaleX, scaleY); - } } } \ No newline at end of file diff --git a/src/Perspex.SceneGraph/Media/AlignmentX.cs b/src/Perspex.SceneGraph/Media/AlignmentX.cs new file mode 100644 index 0000000000..9ade324e18 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/AlignmentX.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + /// + /// Describes how content is positioned horizontally in a container. + /// + public enum AlignmentX + { + /// + /// The contents align themselves with the left of the container + /// + Left, + + /// + /// The contents align themselves with the center of the container + /// + Center, + + /// + /// The contents align themselves with the right of the container + /// + Right, + } +} diff --git a/src/Perspex.SceneGraph/Media/AlignmentY.cs b/src/Perspex.SceneGraph/Media/AlignmentY.cs new file mode 100644 index 0000000000..24ef2de60d --- /dev/null +++ b/src/Perspex.SceneGraph/Media/AlignmentY.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + /// + /// Describes how content is positioned vertically in a container. + /// + public enum AlignmentY + { + /// + /// The contents align themselves with the top of the container + /// + Top, + + /// + /// The contents align themselves with the center of the container + /// + Center, + + /// + /// The contents align themselves with the bottom of the container + /// + Bottom, + } +} diff --git a/src/Perspex.SceneGraph/Media/MediaExtensions.cs b/src/Perspex.SceneGraph/Media/MediaExtensions.cs new file mode 100644 index 0000000000..b5f67a7a43 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/MediaExtensions.cs @@ -0,0 +1,47 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + using System; + + /// + /// Provides extension methods for Perspex media. + /// + public static class MediaExtensions + { + /// + /// Calculates scaling based on a value. + /// + /// The stretch mode. + /// The size of the destination viewport. + /// The size of the source. + /// A vector with the X and Y scaling factors. + public static Vector CalculateScaling(this Stretch stretch, Size destinationSize, Size sourceSize) + { + double scaleX = 1; + double scaleY = 1; + + if (stretch != Stretch.None) + { + scaleX = destinationSize.Width / sourceSize.Width; + scaleY = destinationSize.Height / sourceSize.Height; + + switch (stretch) + { + case Stretch.Uniform: + scaleX = scaleY = Math.Min(scaleX, scaleY); + break; + case Stretch.UniformToFill: + scaleX = scaleY = Math.Max(scaleX, scaleY); + break; + } + } + + return new Vector(scaleX, scaleY); + } + } +} diff --git a/src/Perspex.SceneGraph/Media/TileBrush.cs b/src/Perspex.SceneGraph/Media/TileBrush.cs new file mode 100644 index 0000000000..59160d70c0 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/TileBrush.cs @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + /// + /// Base class for brushes which display repeating images. + /// + public abstract class TileBrush : Brush + { + /// + /// Defines the property. + /// + public static readonly PerspexProperty AlignmentXProperty = + PerspexProperty.Register("ALignmentX", AlignmentX.Center); + + /// + /// Defines the property. + /// + public static readonly PerspexProperty AlignmentYProperty = + PerspexProperty.Register("ALignmentY", AlignmentY.Center); + + /// + /// Defines the property. + /// + public static readonly PerspexProperty DestinationRectProperty = + PerspexProperty.Register("DestinationRect", RelativeRect.Fill); + + /// + /// Defines the property. + /// + public static readonly PerspexProperty SourceRectProperty = + PerspexProperty.Register("SourceRect", RelativeRect.Fill); + + /// + /// Defines the property. + /// + public static readonly PerspexProperty StretchProperty = + PerspexProperty.Register(nameof(Stretch), Stretch.Uniform); + + /// + /// Gets or sets the horizontal alignment of a tile in the destination. + /// + public AlignmentX AlignmentX + { + get { return this.GetValue(AlignmentXProperty); } + set { this.SetValue(AlignmentXProperty, value); } + } + + /// + /// Gets or sets the horizontal alignment of a tile in the destination. + /// + public AlignmentY AlignmentY + { + get { return this.GetValue(AlignmentYProperty); } + set { this.SetValue(AlignmentYProperty, value); } + } + + /// + /// Gets or sets the rectangle on the destination in which to paint a tile. + /// + public RelativeRect DestinationRect + { + get { return this.GetValue(DestinationRectProperty); } + set { this.SetValue(DestinationRectProperty, value); } + } + + /// + /// Gets or sets the rectangle of the source image that will be displayed. + /// + public RelativeRect SourceRect + { + get { return this.GetValue(SourceRectProperty); } + set { this.SetValue(SourceRectProperty, value); } + } + + /// + /// Gets or sets a value controlling how the source rectangle will be stretched to fill + /// the destination rect. + /// + public Stretch Stretch + { + get { return (Stretch)this.GetValue(StretchProperty); } + set { this.SetValue(StretchProperty, value); } + } + } +} diff --git a/src/Perspex.SceneGraph/Media/VisualBrush.cs b/src/Perspex.SceneGraph/Media/VisualBrush.cs index 73feade478..d760004db6 100644 --- a/src/Perspex.SceneGraph/Media/VisualBrush.cs +++ b/src/Perspex.SceneGraph/Media/VisualBrush.cs @@ -6,20 +6,36 @@ namespace Perspex.Media { - public class VisualBrush : Brush + /// + /// Paints an area with an . + /// + public class VisualBrush : TileBrush { + /// + /// Defines the property. + /// public static readonly PerspexProperty VisualProperty = PerspexProperty.Register("Visual"); + /// + /// Initializes a new instance of the class. + /// public VisualBrush() { } + /// + /// Initializes a new instance of the class. + /// + /// The visual to draw. public VisualBrush(IVisual visual) { this.Visual = visual; } + /// + /// Gets or sets the visual to draw. + /// public IVisual Visual { get { return this.GetValue(VisualProperty); } diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 765915b16a..ff8e0cf94e 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -57,10 +57,13 @@ + + + @@ -86,6 +89,7 @@ + @@ -98,6 +102,7 @@ + diff --git a/src/Perspex.SceneGraph/RelativeRect.cs b/src/Perspex.SceneGraph/RelativeRect.cs new file mode 100644 index 0000000000..fb688841ae --- /dev/null +++ b/src/Perspex.SceneGraph/RelativeRect.cs @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex +{ + /// + /// Defines a rectangle that may be defined relative to another rectangle. + /// + public struct RelativeRect + { + /// + /// A rectangle that represents 100% of an area. + /// + public static readonly RelativeRect Fill = new RelativeRect(0, 0, 1, 1, OriginUnit.Percent); + + /// + /// Initializes a new instance of the structure. + /// + /// The X position. + /// The Y position. + /// The width. + /// The height. + /// The unit of the rect. + public RelativeRect(double x, double y, double width, double height, OriginUnit unit) + { + this.Rect = new Rect(x, y, width, height); + this.Unit = unit; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The rectangle. + /// The unit of the rect. + public RelativeRect(Rect rect, OriginUnit unit) + { + this.Rect = rect; + this.Unit = unit; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The size of the rectangle. + /// The unit of the rect. + public RelativeRect(Size size, OriginUnit unit) + { + this.Rect = new Rect(size); + this.Unit = unit; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The position of the rectangle. + /// The size of the rectangle. + /// The unit of the rect. + public RelativeRect(Point position, Size size, OriginUnit unit) + { + this.Rect = new Rect(position, size); + this.Unit = unit; + } + + /// + /// Initializes a new instance of the structure. + /// + /// The top left position of the rectangle. + /// The bottom right position of the rectangle. + /// The unit of the rect. + public RelativeRect(Point topLeft, Point bottomRight, OriginUnit unit) + { + this.Rect = new Rect(topLeft, bottomRight); + this.Unit = unit; + } + + /// + /// Gets the unit of the rectangle. + /// + public OriginUnit Unit { get; } + + /// + /// Gets the rectangle. + /// + public Rect Rect { get; } + + /// + /// Converts a into pixels. + /// + /// The size of the visual. + /// The origin point in pixels. + public Rect ToPixels(Size size) + { + return this.Unit == OriginUnit.Pixels ? + this.Rect : + new Rect( + this.Rect.X * size.Width, + this.Rect.Y * size.Height, + this.Rect.Width * size.Width, + this.Rect.Height * size.Height); + } + } +} diff --git a/src/Perspex.SceneGraph/Rendering/RendererBase.cs b/src/Perspex.SceneGraph/Rendering/RendererBase.cs index 6ad42eedb7..43e193dfd3 100644 --- a/src/Perspex.SceneGraph/Rendering/RendererBase.cs +++ b/src/Perspex.SceneGraph/Rendering/RendererBase.cs @@ -33,10 +33,23 @@ namespace Perspex.Rendering /// The visual to render. /// An optional platform-specific handle. public virtual void Render(IVisual visual, IPlatformHandle handle) + { + this.Render(visual, handle, Matrix.Identity, Matrix.Identity); + } + + /// + /// Renders the specified visual with the specified transform. + /// + /// The visual to render. + /// An optional platform-specific handle. + /// The translation. + /// The transform. + public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix translation, Matrix transform) { using (var context = this.CreateDrawingContext(handle)) { - this.Render(visual, context, Matrix.Identity, Matrix.Identity); + //context.PushTransform(translation * transform); + this.Render(visual, context, translation, transform); } ++this.RenderCount; diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index f695e8335e..33571c34fc 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -90,7 +90,9 @@ namespace Perspex.Direct2D1.Media { if (pen != null) { - using (var d2dBrush = this.CreateBrush(pen.Brush)) + var size = new Rect(p1, p2).Size; + + using (var d2dBrush = this.CreateBrush(pen.Brush, size)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { this.renderTarget.DrawLine( @@ -113,7 +115,7 @@ namespace Perspex.Direct2D1.Media { if (brush != null) { - using (var d2dBrush = this.CreateBrush(brush)) + using (var d2dBrush = this.CreateBrush(brush, geometry.Bounds.Size)) { GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl; this.renderTarget.FillGeometry(impl.Geometry, d2dBrush); @@ -122,7 +124,7 @@ namespace Perspex.Direct2D1.Media if (pen != null) { - using (var d2dBrush = this.CreateBrush(pen.Brush)) + using (var d2dBrush = this.CreateBrush(pen.Brush, geometry.GetRenderBounds(pen.Thickness).Size)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl; @@ -139,7 +141,7 @@ namespace Perspex.Direct2D1.Media /// The corner radius. public void DrawRectange(Pen pen, Rect rect, float cornerRadius) { - using (var brush = this.CreateBrush(pen.Brush)) + using (var brush = this.CreateBrush(pen.Brush, rect.Size)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { this.renderTarget.DrawRoundedRectangle( @@ -162,7 +164,7 @@ namespace Perspex.Direct2D1.Media { var impl = (FormattedTextImpl)text.PlatformImpl; - using (var brush = this.CreateBrush(foreground)) + using (var brush = this.CreateBrush(foreground, impl.Measure())) using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush)) { impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y); @@ -178,7 +180,7 @@ namespace Perspex.Direct2D1.Media /// The corner radius. public void FillRectange(Perspex.Media.Brush brush, Rect rect, float cornerRadius) { - using (var b = this.CreateBrush(brush)) + using (var b = this.CreateBrush(brush, rect.Size)) { this.renderTarget.FillRoundedRectangle( new RoundedRectangle @@ -263,8 +265,9 @@ namespace Perspex.Direct2D1.Media /// Creates a Direct2D brush from a Perspex brush. /// /// The perspex brush. + /// The size of the brush's target area. /// The Direct2D brush. - public Disposable CreateBrush(Perspex.Media.Brush brush) + public Disposable CreateBrush(Perspex.Media.Brush brush, Size destinationSize) { var solidColorBrush = brush as Perspex.Media.SolidColorBrush; var visualBrush = brush as Perspex.Media.VisualBrush; @@ -278,7 +281,7 @@ namespace Perspex.Direct2D1.Media } else if (visualBrush != null) { - return this.CreateBrush(visualBrush); + return this.CreateBrush(visualBrush, destinationSize); } else { @@ -292,8 +295,9 @@ namespace Perspex.Direct2D1.Media /// Creates a Direct2D from a Perspex . /// /// The perspex brush. + /// The size of the brush's target area. /// The Direct2D brush. - private Disposable CreateBrush(VisualBrush brush) + private Disposable CreateBrush(VisualBrush brush, Size destinationSize) { var visual = brush.Visual; var layoutable = visual as ILayoutable; @@ -304,17 +308,19 @@ namespace Perspex.Direct2D1.Media layoutable.Arrange(new Rect(layoutable.DesiredSize)); } + var sourceSize = layoutable.Bounds.Size; + var destinationRect = brush.DestinationRect.ToPixels(destinationSize); + var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize); + using (var target = new BitmapRenderTarget( this.renderTarget, CompatibleRenderTargetOptions.None, - visual.Bounds.Size.ToSharpDX())) + destinationRect.Size.ToSharpDX())) { var renderer = new Renderer(target); - renderer.Render(visual, null); + renderer.Render(visual, null, Matrix.Identity, Matrix.CreateScale(scale)); var result = new BitmapBrush(this.renderTarget, target.Bitmap); - result.ExtendModeX = ExtendMode.Wrap; - result.ExtendModeY = ExtendMode.Wrap; return new Disposable(result, target); } } diff --git a/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs b/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs index 5566c2ff60..9319c86328 100644 --- a/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs +++ b/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs @@ -49,9 +49,11 @@ namespace Perspex.Direct2D1.Media ComObject clientDrawingEffect) { var wrapper = clientDrawingEffect as BrushWrapper; + + // TODO: Work out how to get the size below rather than passing new Size(). var brush = (wrapper == null) ? this.foreground : - this.context.CreateBrush(wrapper.Brush); + this.context.CreateBrush(wrapper.Brush, new Size()); this.renderTarget.DrawGlyphRun( new Vector2(baselineOriginX, baselineOriginY), diff --git a/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs b/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs index 772d3a4917..1cb4c526f3 100644 --- a/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs @@ -20,7 +20,7 @@ namespace Perspex.Direct2D1.RenderTests.Controls } [Fact] - public void VisualBrush_Should_Draw_Visual() + public void VisualBrush_Stretch_None() { Decorator target = new Decorator { @@ -54,5 +54,123 @@ namespace Perspex.Direct2D1.RenderTests.Controls this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_Align_Center() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + AlignmentX = AlignmentX.Center, + AlignmentY = AlignmentY.Center, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + + [Fact] + public void VisualBrush_Stretch_Fill_Large() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 920, + Height = 920, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.Fill, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + + ////[Fact] + ////public void VisualBrush_Line_Fill() + ////{ + //// Decorator target = new Decorator + //// { + //// Padding = new Thickness(8), + //// Width = 200, + //// Height = 200, + //// Child = new Line + //// { + //// X1 = 16, + //// Y1 = 16, + //// X2 = 184, + //// Y2 = 184, + //// StrokeThickness = 40, + //// StrokeStartLineCap = "Triangle", + //// StrokeEndLineCap = "Triangle", + //// Fill = new VisualBrush + //// { + //// Visual = new Border + //// { + //// Width = 92, + //// Height = 92, + //// Background = Brushes.Red, + //// BorderBrush = Brushes.Black, + //// BorderThickness = 2, + //// Child = new TextBlock + //// { + //// Text = "Perspex", + //// FontSize = 12, + //// FontFamily = "Arial", + //// HorizontalAlignment = HorizontalAlignment.Center, + //// VerticalAlignment = VerticalAlignment.Center, + //// } + //// } + //// } + //// } + //// }; + + //// this.RenderToFile(target); + //// this.CompareImages(); + ////} } } diff --git a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj index 455d7a993c..dbe991d419 100644 --- a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -77,6 +77,7 @@ + From c056dbaa687d35a7afc9ac7070a182652333788c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 20:47:53 +0200 Subject: [PATCH 03/19] Don't call Draw/FillRoundedRectangle when radius == 0 As D2D was drawing slightly rounded corners. --- .../Perspex.Direct2D1/Media/DrawingContext.cs | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 639bae6049..4dd858ba11 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -144,11 +144,22 @@ namespace Perspex.Direct2D1.Media using (var brush = this.CreateBrush(pen.Brush, rect.Size)) using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget)) { - this.renderTarget.DrawRoundedRectangle( - new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius }, - brush.PlatformBrush, - (float)pen.Thickness, - d2dStroke); + if (cornerRadius == 0) + { + this.renderTarget.DrawRectangle( + rect.ToDirect2D(), + brush.PlatformBrush, + (float)pen.Thickness, + d2dStroke); + } + else + { + this.renderTarget.DrawRoundedRectangle( + new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius }, + brush.PlatformBrush, + (float)pen.Thickness, + d2dStroke); + } } } @@ -182,18 +193,25 @@ namespace Perspex.Direct2D1.Media { using (var b = this.CreateBrush(brush, rect.Size)) { - this.renderTarget.FillRoundedRectangle( - new RoundedRectangle - { - Rect = new RectangleF( - (float)rect.X, - (float)rect.Y, - (float)rect.Width, - (float)rect.Height), - RadiusX = cornerRadius, - RadiusY = cornerRadius - }, - b.PlatformBrush); + if (cornerRadius == 0) + { + this.renderTarget.FillRectangle(rect.ToDirect2D(), b.PlatformBrush); + } + else + { + this.renderTarget.FillRoundedRectangle( + new RoundedRectangle + { + Rect = new RectangleF( + (float)rect.X, + (float)rect.Y, + (float)rect.Width, + (float)rect.Height), + RadiusX = cornerRadius, + RadiusY = cornerRadius + }, + b.PlatformBrush); + } } } From 0d938acbed4b1ad2633189af6f079a89f5c1f930 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 20:52:23 +0200 Subject: [PATCH 04/19] First two VisualBrush tests passing. --- .../Perspex.Direct2D1/Media/DrawingContext.cs | 15 +++++- .../Media/VisualBrushImpl.cs | 51 ++++++++++++++++++ .../Perspex.Direct2D1.csproj | 1 + .../{Brushes => Media}/VisualBrushTests.cs | 5 +- .../Perspex.Direct2D1.RenderTests.csproj | 2 +- ...isualBrush_Stretch_Fill_Large.expected.png | Bin 0 -> 13946 bytes .../VisualBrush_Stretch_None.expected.png | Bin 0 -> 934 bytes 7 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs rename tests/Perspex.RenderTests/{Brushes => Media}/VisualBrushTests.cs (97%) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Fill_Large.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_None.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 4dd858ba11..247789243f 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -279,10 +279,17 @@ namespace Perspex.Direct2D1.Media }); } + /// + /// Creates a Direct2D brush wrapper for a Perspex brush. + /// + /// The perspex brush. + /// The size of the brush's target area. + /// The Direct2D brush wrapper. public BrushImpl CreateBrush(Perspex.Media.Brush brush, Size destinationSize) { - Perspex.Media.SolidColorBrush solidColorBrush = brush as Perspex.Media.SolidColorBrush; - Perspex.Media.LinearGradientBrush linearGradientBrush = brush as Perspex.Media.LinearGradientBrush; + var solidColorBrush = brush as Perspex.Media.SolidColorBrush; + var linearGradientBrush = brush as Perspex.Media.LinearGradientBrush; + var visualBrush = brush as Perspex.Media.VisualBrush; if (solidColorBrush != null) { @@ -292,6 +299,10 @@ namespace Perspex.Direct2D1.Media { return new LinearGradientBrushImpl(linearGradientBrush, this.renderTarget, destinationSize); } + else if (visualBrush != null) + { + return new VisualBrushImpl(visualBrush, this.renderTarget, destinationSize); + } else { return new SolidColorBrushImpl(null, this.renderTarget, destinationSize); diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs new file mode 100644 index 0000000000..5b399fd9b3 --- /dev/null +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Direct2D1.Media +{ + using Perspex.Layout; + using Perspex.Media; + using SharpDX.Direct2D1; + + public class VisualBrushImpl : BrushImpl + { + public VisualBrushImpl( + Perspex.Media.VisualBrush brush, + SharpDX.Direct2D1.RenderTarget target, + Size destinationSize) + : base(brush, target, destinationSize) + { + var visual = brush.Visual; + var layoutable = visual as ILayoutable; + + if (layoutable?.IsArrangeValid == false) + { + layoutable.Measure(Size.Infinity); + layoutable.Arrange(new Rect(layoutable.DesiredSize)); + } + + var sourceSize = layoutable.Bounds.Size; + var destinationRect = brush.DestinationRect.ToPixels(destinationSize); + var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize); + + using (var brt = new BitmapRenderTarget( + target, + CompatibleRenderTargetOptions.None, + destinationRect.Size.ToSharpDX())) + { + var renderer = new Renderer(brt); + renderer.Render(visual, null, Matrix.Identity, Matrix.CreateScale(scale)); + this.PlatformBrush = new BitmapBrush(brt, brt.Bitmap); + } + } + + public override void Dispose() + { + ((BitmapBrush)this.PlatformBrush).Bitmap.Dispose(); + base.Dispose(); + } + } +} diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 11053fdc40..c7271b74bd 100644 --- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -83,6 +83,7 @@ + diff --git a/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs similarity index 97% rename from tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs rename to tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 1cb4c526f3..301d09852e 100644 --- a/tests/Perspex.RenderTests/Brushes/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace Perspex.Direct2D1.RenderTests.Controls +namespace Perspex.Direct2D1.RenderTests.Media { using Perspex.Controls; using Perspex.Controls.Shapes; @@ -15,7 +15,7 @@ namespace Perspex.Direct2D1.RenderTests.Controls public class VisualBrushTests : TestBase { public VisualBrushTests() - : base(@"Brushes\VisualBrush") + : base(@"Media\VisualBrush") { } @@ -31,6 +31,7 @@ namespace Perspex.Direct2D1.RenderTests.Controls { Fill = new VisualBrush { + Stretch = Stretch.None, Visual = new Border { Width = 92, diff --git a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj index b9ff14c875..bc8fd43376 100644 --- a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -77,7 +77,7 @@ - + diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Fill_Large.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Fill_Large.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f1914a404f30ea4e3549e6e34eb74214e5c0cd31 GIT binary patch literal 13946 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_None.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_None.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..905ec5386a1f4e6fb9356ccaa42e78b0e876918b GIT binary patch literal 934 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggIl$A!F{C2y?cI%&bdqIQKRi`cX<4+of@_J=s+KrM504Yl0;`mS9@(+F zs;GPv`E&kE%_`rwGPBR*y}x^Z{i~esCEsg`^L^+4+*RJZlM!e>14D)5-jXP@`uWrD z^zYuIerNJ@fA0rkzfXOi{9L#qWA~mTRr#Ne7k+(q{$yEMq|xabgGl3ZKMVK!DE*#fu6A?Y z^PIJYlR{7B{QkA0YQk}@)2i+K-+mTOO?flZQ7`ZGFaPCV{ng&*U)%nFT~7S*r#sL4 zy?(vJG;UjcY1I9`>3=Qrx_-Vdj66TXQt$S*YT(-E{1J+S~)Tg>6rA z&i)&j=l@RS_3O&pbL6KdUwhoMw`+3F@e|ok3ghzM?oTuOUtg9o{kwv^@1yl6^Cy>b ze!2BzQoee;-?fdeg-(S8rW(!v^k36|li$7K z{eGM5TYno!=NTFwPCRqsQ;=ox`H#Pz9QS;eUoopJc7nP3&nfZdCnBFs{%5w6bH7P; zN?L?^W!lX0{?+aAY&CT;wkK}9-g<9ljoHtnPlaY5RNn4VnCSm!&Kk?g&1W9pt}%|B z?j8BhAo5wv;(_i0{%f0=#r0q3fR=@X70;@RY%M*AcIOm*-6=x)CweZOe8a+&}* h!BUH8xVv-bPxjqcdS7jjFq{E0)zj6_Wt~$(6951jcT@lX literal 0 HcmV?d00001 From 4f200bedb35ab2e7ae3c908c07f3ae93501cdce9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 21:01:03 +0200 Subject: [PATCH 05/19] Implement VisualBrush alignment. --- .../Perspex.Direct2D1/Media/VisualBrushImpl.cs | 4 +++- .../Perspex.RenderTests/Media/VisualBrushTests.cs | 1 + .../VisualBrush_Align_Center.expected.png | Bin 0 -> 924 bytes 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_Center.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 5b399fd9b3..f95ca41138 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -6,6 +6,7 @@ namespace Perspex.Direct2D1.Media { + using System; using Perspex.Layout; using Perspex.Media; using SharpDX.Direct2D1; @@ -30,6 +31,7 @@ namespace Perspex.Direct2D1.Media var sourceSize = layoutable.Bounds.Size; var destinationRect = brush.DestinationRect.ToPixels(destinationSize); var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize); + var translate = new Rect(destinationSize).CenterIn(new Rect(sourceSize * scale)).Position; using (var brt = new BitmapRenderTarget( target, @@ -37,7 +39,7 @@ namespace Perspex.Direct2D1.Media destinationRect.Size.ToSharpDX())) { var renderer = new Renderer(brt); - renderer.Render(visual, null, Matrix.Identity, Matrix.CreateScale(scale)); + renderer.Render(visual, null, Matrix.CreateTranslation(translate), Matrix.CreateScale(scale)); this.PlatformBrush = new BitmapBrush(brt, brt.Bitmap); } } diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 301d09852e..9bd803e71f 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -70,6 +70,7 @@ namespace Perspex.Direct2D1.RenderTests.Media { AlignmentX = AlignmentX.Center, AlignmentY = AlignmentY.Center, + Stretch = Stretch.None, Visual = new Border { Width = 92, diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_Center.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_Center.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..dc6050fec427d190178d9e4ea227cfcec9e4b8cd GIT binary patch literal 924 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(Ggg+0E0%F{C2y?OpqI(xEb}57=ifXq=?Q5yrSB#AQ(m_mlu9PU{9C&Q(+X zM6z;dd1XYf{^@($t#<9RW`WF8-+lg8Ge1_;%zbkAvGrt;eat`$hy!vR;eNMme?8yw z^yiJIF;!o;u)f>)Tl9!gQS@xi6xPP+y-f}a6#_IkM7Wq*Y03N_yzlc5oB6&s#94P9 z(w|(z|NGSBljnuMIXli<$gfr}SN{Irteo)as;kdDJ{eVb?)>jkp-R2V>`9L+&t8*K zj$C?XV}9Rm_R2LIVv2qw%#Y8T)MKlC;%M^C|1Y=vfBEF_*?-@@PksD)&z^4=GwbJW zKX&@#djH?IKYpq?H#_NHdE7VSv~#C7MytKA<>Ei{kbS!RBi&8M?x*=acvDjp2op-bz&V+noEo_Iduyv-9hx$0Wrjs=W+0e)8D!kL}Oe zO?r*@X2g8D`(*V=tMb^Sxu+#0jW^Y%Z;Jc;H>L9Lmbr&M@MxUi-D5V>|L5{klh>TA zez)Cop8rF;Q;pSUP8CQPZ`vDj?oRO@%b$gleoL=gtYH4(o67u8uh}eb*Ic@`CcUEC27P{&BoPALx0>>b=*iC!bf3_p>|6xNebx`6T~8KG%Hi<$V5Oci->Y z)1YnkKGSwiw}^>)x^qg>S|Lq`Cl!-UuPj&BynSbme9Bg@g@5j!DgE?)clgdv7XLmm zq$d40JZXEXJIA=O%yRk~g@6dh9bKX Date: Thu, 3 Sep 2015 21:09:10 +0200 Subject: [PATCH 06/19] Added tests for VisualBrush.Stretch Uniform*. --- .../Media/VisualBrushTests.cs | 74 ++++++++++++++++++ .../VisualBrush_Stretch_Uniform.expected.png | Bin 0 -> 12243 bytes ...alBrush_Stretch_UniformToFill.expected.png | Bin 0 -> 13274 bytes 3 files changed, 74 insertions(+) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Uniform.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_UniformToFill.expected.png diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 9bd803e71f..4d6f19c7e7 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -132,6 +132,80 @@ namespace Perspex.Direct2D1.RenderTests.Media this.CompareImages(); } + [Fact] + public void VisualBrush_Stretch_Uniform() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 920, + Height = 820, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.Uniform, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + + [Fact] + public void VisualBrush_Stretch_UniformToFill() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 920, + Height = 820, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.UniformToFill, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + ////[Fact] ////public void VisualBrush_Line_Fill() ////{ diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Uniform.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_Uniform.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..fb2ae9f8ccbfc5cd2d8f66120f35e4fa0f119bbd GIT binary patch literal 12243 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 XGZ~2b%IM@+- z!sP@A)R?tvBTx|pGQD-~^a+nRzxUJ1t#xu=^C*r->gnKtdw<7eI=8bt z@7529%lDq8pUy`7a?be2oRbF+o^+^E$%c_k{&QO{>yR=&V`DGG*9*}iR%(Y-h{s4* zH^vvchV6t)F`wHeB$~Xh^8B|Sy`n^dEeFjIaUxK}CFl+iC}1eUpEw0w!Ud;oJmUWZ zjP!aKunh$I=VT9X^}+3%${^6MyA=0=K-c%}JPceursfR%UN{QO9`yJ;Q~?CK^w+lm z-?a42G`{)6H`MqBmEYpSx1jj{j5_e_SUQ4Qehjvy#)hD=Z77MF6MI+u$aOJec*DB_ z@4>^sCzh^3OD{0v1Q!AFVlMnl>fhaKLO|#kDL^Pg4#!yIy<(3x<5V$*k=7UU?ky^9SGFTLjpJC$55n| zHUTR?fv(HVHfZP`nWMJ*fMj zIR=U!RS3}Ixv$;iNWJ{d6CDIp64s_%9QLnx0I~DIuX0Qc25D98p@NiuPXd$mkxr9@ zo;;@XQoGbM3IBCo+tr){j!ZSsxp@=)!!!^my!58>(%qL{%PLTdJqKip#pjm zD3H?k^p2F6vVc*TorfEcM(3HcP%Rh0XeM_9$9SCIC^L2LsBtky0W@g@tN~nDpYRf@ zCD=}~P(1|#HJ|O-L3mccr3G~m2#TP9`2W*N{@1h_e(uaZVB`p?sDgP+w|c`zfWgz$ zX~FXk)44Qus29&2et8B#oHvwvuibS@lA|hpYv}A*NsU2d{X+YgltCGIHDyKLnB_EWpl`21mb=izePoxCkgtpNdqa{O~^v5CV6ThbAaq#Cg}A>bAs zMe|yF!Wn}2qu>!+HDVdb<_Lasf}=);H_D6JBkUR!lF{rgCFD%P+7v$qDgN#S1tQ0+ zdD}ssJP$<$$N*#LKid<2?k6te_n!}i4YUiV0;_JqrT8Vpj3-&zq?<4E`n}fff<^yQRs-C~95HVgqeK|lY*&r{*i-&;Au0j)1WNi@x zwj!lF8+lc!W5eXKVd-SMzr1oIp!_?{L6p~GqIQ}f9b)!Fw)Q!z$#&tpo$ws9!fsd2 zbRPKKZNY_2$8x{$mFdeHe>zb=5UyeML$8mguQO~7btg@9$r#mZr}bm6WwwP83!PtX zta5%bh~+6AUeD#AP`H-&p$4MKg|{mPS+!&7B1WR8Kni)8Z+%q~TvoGEIrUila_!fY zf29>OL`LhyPMu1fH91|SQ-PwK%YTz&NL})FSt)o{^x-#>^GNaQzex6)sbR5lC(_b6 zKQq?0id?`$9!ZC3L6~p?Ce3-%xpCty=Sj?#-d$uIEH|n_XKidJf%iT&OXoqPlO7uy? zRZ|S{aza1~{lw5Q1ENC5GQzsA$NE&V?;0;~^7S5u=+K(1)m;!P)w$A-G(gqagshA# z7NemnNH%8BMQc>j#L5> zpg64%uO?T2)}{PvlHla_ZcfjZ^yN5xJue)!D^krJAW%^2v~w zo2hQWJ2%F_6RZy_dmA*rLZcw_xpygo{kUllaU`iCuoa0ra!}tCdAvp6cv;s@<>g)S z@2pJqe=sC-y(=w)={iOr)8>A>Lf}=EEx|sSF+w$Y-aNAlp z{ALhrZGQNRTz@?l!bneW$oNf56Wf`3o8aTIxZ;iu*%`2tt%`zWn8-JM4A73U9w)*M zUZ9LjC=%yPM={0;eBD3zU$98=W}aC=P{Qx}LO1ydY_k8FX)gW{gKB%1j-`sv0^DwO zIf@z8vI4YMHQFcO~wwIp!?J2zQkZABLGVxdO(Bx{^ipb8;^TlFP378Gf zlGsTfj-`L;z?;l^RjJ-}-I8a~eE)*2NOVZ`m17U|gNA$B*+p4(_eH4Rqb`;BkfpQQ z`4SHuMAft7Qpqt+yn$+6Q44UtF3G=cETH@@TnQhJbA6qRde`$Np%z!bX&-Jm>Umt4 z#PgJ3NyM(1hg0ohx|3~T9;7JLv!`b6lT8;QrMajc06Ms##n)#n%+iUavIgHa#P%Qg zAH&zRxF(B<&&nSL+^IE|l;G2_1-nA-OfVsm&gEWlwbSl=*PU!E1el3CTS2};8%rtA zRbQ1>a%CHif*I0*+M-59yfY2M^bxp|+9#6;fV6&6@harhL%@5I!&SCXvLMH&}f&&`!fO zIO}fA^0?LzrR0n{R9xL zEQi%>c4;sTGJAn64b#r&*{3Wl18czxWZ%7mb@E>(2#Lp6@H+XJQf0kEr32JmR3IYo z2G;u>clVCG(;XsBb&2l=S~`uRLu|3U?Mt7qr0^LSc9dj|!g)=bW8b63Z_N-^Mn-7f z>;q$}C|uO0`@YQ;S@qO%@&2|wXbO4yRzGKUw%NBXC&SAw5%5vkdA3ki+u=ALEF7^= z=_R#S!(uvCj8Qr;m6ySihT6Hh9QcJTl@cgm-ji7gSy?2rHtB)X++bvBmu_lVLcCzS z_lH->Uzz2H2F_TJI?od=Fnpp`3>G$HTE!F+*!TrOkN-D@|hk#!6ZA zH(E~{vBy|(C3p>*L#I6*&C_@ZDTFWTV;f|KlfyL&64#IE3=sseuVP@Z&%n84G+-oawJtOdYXu(b8a8JU8q0B#HXXB5%a0HNk!-e+2C5V-m zpGV>{)R&US1sl&adqN}K$>N7st6PR?=?ktLD@-$tP%t*tA!<#r@SG`Pa-reUrzCLX zlLgv}b=n+)Uh}?wPp>;pc!iv7O?6_ouS0#0nrc~P(b%r7$gG2G3>UyJz0DwjEN$-6 za4AM%PZ_{7Ke~~$5T&eN;BJxCo-2N5b}o98Uy$ejV?KA7q%~}ECNsM5T7+MkmIB>1 zXy>A0=tK+!aS?xb?y#V+pniX9Snf89&l6wbud+f?Y{s9BCZ)I6iyH%5z1BK1!3?q!Wi^E&CX?FF zF_e+Z7enqW(y3}(PhKtN%=kHZH=qPj8>>sb1Ve^t+lWr!KQ9L+wdD-My+V!|TD9!^ z`75=>&bgil!kT9G?>Y5=UwA~)>1-f-<}?#%Ws0;LCe!Dfch2`L6o1mXRN_pm*f`1$ z2qm*hlmwaF$tD-L^Ojd{R2f}i#BERS?Ghj62()M+gB1P-_Ouj*lU~BWnj)9}k|!hi ze`^CE*ojh&d<03wcbvw0g71+vRA6}zcXId;$GK3Jnzg!+H26t%t#@LAl1eQaajz~dL-Sljt^lDbOoZE=$` z_(E~tzW2kG8!w^ZB(ommc-10pzGs?gnz2?*YBaTc{gfoybLy4Gi>p1{-Mg*_=^@{E zfeW%-Ec@2i`7xSCe*5DK4jO|j=3X#FZ5g&YdaV4;1P$8rwn2dhv6ql5fJOM7yL><| zHooG3tetDbdkHBpAsqY6f&k`MmsE~h1CWLwu(h1m(*(JI3skF*7IdraJRB7soSPAT zol!lHfa)Pl`v>y=mTRSkHC5dtTR#s<#0tp0AI7||atGTIBui~)L-sqP{fjS8=csi!=%9M4;srtoMlaZJvn^&g^Zu8Us^ z!NTu^mX%AVtp&qI#y_C=4a$}s0F|i_JpH7LT5gqIisAoySaVN?3HHE?HfMfa%=Bn5 zBljJk`6izb$$e)5i zv)6T=3>%!28VEI0LAgi2GI-i;_?=&THr@E|qq4!q!Hl<@=JK)PAPu9(%hrXIw)p+b z6ga_Z@dTe_B)V!!va%kTx%Syoc)LXHrJ*;t1)f5YZ zY;Wqt)|fsujy*2TQe(qY(e-db$E?wdMQyHzPZW~YH?Y;X^l48KN;$&56Jz6a6*0Z?Hy$1Fu?3!m#NoElOdX zUG`h_oiRZ`yDn=)Jf}c{Dgx~rb5`bKI8?VR-Bbfv0Y^XWVAM%VrM=#qXmH`06+ZuB z&hAs4wK0Tgi@ble+}6-1SGdbv5JWIMfVq zA^9^!V%!3zT(M2iF#6QVso33n%C?67J|lkoZ2@G~LS+KtC<|WfhloRXFy!csX<*C$ z8yNv*Sid{Lq~yx&CN{Ma62|0&57Fa0LlV7mnhH2o#WsfSxUy8`T*SKUa)_2Ia*Nv-wLhh*z_l(uwRYCy;f0%6BAwKgN^E0+3; z9#$>7RZbB!O3w1%n`w$`UrG#WYEfNyeIaMtk*u4_hS;xZmBZBV_toJm@`BzEr#V*K zVIT%OgA3j2!o#k;F7=E+5(j6>hB$Bv_&$@t;ANEdsd2R^jLcqH#TO>0BW>LXy)Y~DOvvrW+%J4zOkclhz#byanKxyt=vTJ83Vfxn8F-!>i#LU$lZ-8oC z#T|9V;9#Ec3g^W$M=yGaF|w}ZVE@U4Q~F`w`Dd$!Pa0{Bm))LZXi&O9-80?eGKoGPcYU1AAy zdlvInQcS|aUDqjgy7w^_2P3a7s0>!=AkqC1L;w0pMc)yW>$?3lk#E^#^UqyuZ38~$ z^RR)mI(+^tIs6XpJ%?&OvsTZKq1ZuLnbDp6)uVi^rC$coB#QVwLj~;dg z@>u)CUwyO=p%V|Be+TyDJJsXDwi{qI>!-*xP?$zhY9;B6hV3THP&U{aNu_B%l3$0M&ihw|p@OH_n?{c!=;+IFd z0A4(LGkZm2nT=tGs~jI+@f>H;G%Sf3J}#27&)Ny!7Z;|#BC>h_SI zxjyCCW|DrW@eDnu%&M=XcT(TpaK&215q))P0D@?+I}Y$3hxof;9(F+*nYgK)i*BCu zb7P4R{?T_?5cFQA@+{y}t9SXU52l{BF9G>PBhpg43N-bma*-7h0GIKlwqxk+U#2)g z5c>=4;K!$z{-^ zXKF}m9>Wk>1(%i1?oZVu{fA^PGoem(+yyI|&Ta>|hfgkI?GYyh0`Hadk!s;X(lFeg zPDyTXxh&EEL$4anvnWWaqT8E&ejxF{1rMts&A;E~YN5E-GKSh-m>4X`=#4T!7-r?r zKc+g}xins(9gFtKXXOr8YsK^jYf?HM`TAW0!U_;`F^CBBD;&x(xsu0~@M!72!30c!&|?Nr{_74>qoQelpfg#& zA<_vvgva^BnwYvI)=u#^9tXDOXcgzaokQM*>zX>)I)1vlivNifwmltSD^*v=tYx7b z43~E~A_zMq@D|dXF_fnCzJ1)dCrfSTB8xHVWcpSVS{@5vyKZ#?5Im2sez}{7EXgGw zZp&a5qIVU!ME0%o_%WWySlm!CkBAH!{!~S(O3nlf{oi*0E{x=*$Y=l|@$+DRYHKv# zT&)|7fv6uy-BlhCa_0ke1EU`+?i*MMn%40=H-~Oft9C#(sjfHkW2y%|BJ5Qhng05r z))CM0EE0{pnNOjRafn$TwCN-8Njuo>3s6)@aTiRGd5(J91zrER zI-W-hD$cvPKWJtA(b5&1%j9Utgo(L3bY6!PLz8X*i9F+ePDEMIa*_67%|++z6`4zM zIvKY{2eh7-?7v4MB~?*kk102>_(`jJQ zF3BNlVSI@il7riGFS!TmTjQLu$p^L@%iW8Y$S!dh!5(0@)maWB2q)VgcsG3z#^o;k zY~yAAk}Vxfes6oBv;sv>mw2ON;pK(8i!~t=jY&xm+3?uBAE_wa>da36%<9R!ew6rP zm(P}OML3LPdJzAZTTPvLx{<;`d5kx`bthaZx$qikgqofbMv#gWtj8xjyvXc(T`N`c z%>p1CD*+6>Dt`!Sm#mSQU#!O98ICHk7F>U|6Amxg9$2A~3{-meVmZ5}!@r1QNcQ~r zF=OL)Q~jvgm>G8)XR1NS?;mBKb?Oc$mmmkR1nly)viUFrVj1>Md(S<3Zqr~f&Jh~Q z?F90MmT(;QxqW&qbK`cP<%^)u3D4l_C5JSitU?RNF~G0lzGCMwR{f&Gf(Kf7mG5MJ z0qwbb&$kH{^WKBEJK+`7CK}P)6$fYmakZB-b`h7SrHE_dLiV}W5A@K_kbYrRx*QcZHxTx5y71*?l$`f;C(zWov*h`a0U z#Nzh7n>hV`l_C$-5=_N__A52HZdGtf9@d6|6FE_}I<0gy-ujYcdwT1Lj{2jWk)G_8 zpxRJRaYx)sj#>IqdHTZI22Jj6+bXeVj>fGdZc1tFT@(FrDp5 zeT6B|CpcomLdDs*78g7gPi3Wxs}Sx1^nXmEj6mv_M8Qq z!4_^#8LGq=O*k^1_hIUWe?hylsUUoeDAsC^)<@1UkiY8)_XL$x5M4uxgG#c9ep^^9 zni;?TB(U|df4#GP%@ zFeu_w$*JMWT9~bs@HbbqT!ZPu;vSzdDe)k$;}0oXJ7`WGqZk!Z2exV5v*=m2SBXU| zzlk9Y&L4^$=nU3ael*2IH~G3ouX38@Hs+p2dXcW7+bc57Qbkci99@(bCSF#~5P4hF z)YkZ(AspsCeha2q0JRj#P35Rm-TI~1;ps)aUmgU;RX_566zrO)(5Vd9j2<1`CP|SQ zLy!~iV%SDZG=upkj=8c4-W8|DAGUCHFK^w5%%%U zxFds)%wxQdsD!88RBq_T7{F1+jsd<IyyPme5+~%NPZXN1JV|i=m zT_jsya;v*Lt#MhKw~^H9qR-k}AEZ~L>A6*z!zxG1md23e0NRD;`Rsyje~OU#r7_(%l9tdw5VTbO?M?KP}Fho13x3V zoefXsa2m;(6LC)Zf=F%J1>&#zqIra2Y@qg=cHJrU>V-ejg#NXs+4*^N=os6lL1ZQBTsEdH)f6(~ckKPA=Lzt6#3kaSo(Y`tf@8&C+@0HRTf zOC?&0uDbnspxU1uV}slLa`fzHJlDLj*;;1N8EE=0EDashC&ndVsnMN_zv7~{yy2P> zJcNlXU|obq9L%!70{ihGUdhIJ{jk%?HR1`9gK+<15jRiR&d#pts!*66*;prG)!fD0+gp#DE}r$9?E`wGx2jgKjs*D;Gi zHISBGQq=Z_!HuGA*Mt66jQ|nIrAScJ-7h`6yNx9HouJGg?OPQlu}Q89-6pSwH z1$q$nwIvFodm9F`wGm(6VfhzOx^~G1nry$dtA6Xz{nnWLtsDDWH}~G!J|G#x( k12+HPey;BZ(uZ62uGO?3GHC70ulJpEa6L^s8F2Hz06DN~0ssI2 literal 0 HcmV?d00001 From 5dca21e6b95ef617eea9c74fc8bf17e8b10dd8c4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Sep 2015 21:32:48 +0200 Subject: [PATCH 07/19] Fix/test VisualBrush alignment. --- .../Media/VisualBrushImpl.cs | 30 +++++++++++- .../Media/VisualBrushTests.cs | 43 +++++++++++++++++- ...VisualBrush_Align_BottomRight.expected.png | Bin 0 -> 928 bytes ...=> VisualBrush_Align_TopLeft.expected.png} | Bin 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_BottomRight.expected.png rename tests/TestFiles/Direct2D1/Media/VisualBrush/{VisualBrush_Stretch_None.expected.png => VisualBrush_Align_TopLeft.expected.png} (100%) diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index f95ca41138..2d98e367f8 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -31,7 +31,7 @@ namespace Perspex.Direct2D1.Media var sourceSize = layoutable.Bounds.Size; var destinationRect = brush.DestinationRect.ToPixels(destinationSize); var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize); - var translate = new Rect(destinationSize).CenterIn(new Rect(sourceSize * scale)).Position; + var translate = CalculateTranslate(brush, destinationRect.Size, sourceSize * scale); using (var brt = new BitmapRenderTarget( target, @@ -44,6 +44,34 @@ namespace Perspex.Direct2D1.Media } } + private static Vector CalculateTranslate(VisualBrush brush, Size destinationSize, Size sourceSize) + { + double x = 0; + double y = 0; + + switch (brush.AlignmentX) + { + case AlignmentX.Center: + x = (destinationSize.Width - sourceSize.Width) / 2; + break; + case AlignmentX.Right: + x = destinationSize.Width - sourceSize.Width; + break; + } + + switch (brush.AlignmentY) + { + case AlignmentY.Center: + y = (destinationSize.Height - sourceSize.Height) / 2; + break; + case AlignmentY.Bottom: + y = destinationSize.Height - sourceSize.Height; + break; + } + + return new Vector(x, y); + } + public override void Dispose() { ((BitmapBrush)this.PlatformBrush).Bitmap.Dispose(); diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 4d6f19c7e7..9b7fb7fbdd 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -20,7 +20,7 @@ namespace Perspex.Direct2D1.RenderTests.Media } [Fact] - public void VisualBrush_Stretch_None() + public void VisualBrush_Align_TopLeft() { Decorator target = new Decorator { @@ -31,6 +31,8 @@ namespace Perspex.Direct2D1.RenderTests.Media { Fill = new VisualBrush { + AlignmentX = AlignmentX.Left, + AlignmentY = AlignmentY.Top, Stretch = Stretch.None, Visual = new Border { @@ -95,6 +97,45 @@ namespace Perspex.Direct2D1.RenderTests.Media this.CompareImages(); } + [Fact] + public void VisualBrush_Align_BottomRight() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + AlignmentX = AlignmentX.Right, + AlignmentY = AlignmentY.Bottom, + Stretch = Stretch.None, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + [Fact] public void VisualBrush_Stretch_Fill_Large() { diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_BottomRight.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_BottomRight.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0918b7fd511a807b03c40468527b049a248172d0 GIT binary patch literal 928 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(Ggg*~`<#F{C2y?OoeGo&dpA`H+TBqo|&JE-Y8D~ZrASq>__)${f~;9j@?i5eel-B-zf2O z&9>Vo=cd%}&6|DtbJ4k~IUBT3*=<|C-?i+0b9MCRZx5ak8z}H@kI78`pVd!2uRYoO z<+RHA?QDu5?zHiXBotE~evZgw{efmG`Q}HJKou4fJe&k3^ z`fqm9_SC#wwYs+rqAXn6C;r*I5`D69L(_qJ2bw2`#zWihyzAHBQM<3^v@RsQZz#3+ Y!>exB`>J;H%|{?1Pgg&ebxsLQ00#PR6#xJL literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_None.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_TopLeft.expected.png similarity index 100% rename from tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Stretch_None.expected.png rename to tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Align_TopLeft.expected.png From ec010a1c4d3f16023fe1f636a99bc1b5bff38ab9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 10:25:20 +0200 Subject: [PATCH 08/19] Implemented VisualBrush.SourceRect. --- src/Perspex.SceneGraph/Point.cs | 10 +++ .../Rendering/RendererBase.cs | 8 +- .../Media/VisualBrushImpl.cs | 30 ++++--- .../Media/VisualBrushTests.cs | 76 ++++++++---------- ...sualBrush_SourceRect_Absolute.expected.png | Bin 0 -> 3321 bytes 5 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_Absolute.expected.png diff --git a/src/Perspex.SceneGraph/Point.cs b/src/Perspex.SceneGraph/Point.cs index 917ebc749a..62daee8870 100644 --- a/src/Perspex.SceneGraph/Point.cs +++ b/src/Perspex.SceneGraph/Point.cs @@ -59,6 +59,16 @@ namespace Perspex return new Vector(p.x, p.y); } + /// + /// Negates a point. + /// + /// The point. + /// The negated point. + public static Point operator -(Point a) + { + return new Point(-a.x, -a.y); + } + /// /// Checks for equality between two s. /// diff --git a/src/Perspex.SceneGraph/Rendering/RendererBase.cs b/src/Perspex.SceneGraph/Rendering/RendererBase.cs index 43e193dfd3..2dce69e3d0 100644 --- a/src/Perspex.SceneGraph/Rendering/RendererBase.cs +++ b/src/Perspex.SceneGraph/Rendering/RendererBase.cs @@ -34,7 +34,7 @@ namespace Perspex.Rendering /// An optional platform-specific handle. public virtual void Render(IVisual visual, IPlatformHandle handle) { - this.Render(visual, handle, Matrix.Identity, Matrix.Identity); + this.Render(visual, handle, Matrix.Identity); } /// @@ -42,14 +42,12 @@ namespace Perspex.Rendering /// /// The visual to render. /// An optional platform-specific handle. - /// The translation. /// The transform. - public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix translation, Matrix transform) + public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix transform) { using (var context = this.CreateDrawingContext(handle)) { - //context.PushTransform(translation * transform); - this.Render(visual, context, translation, transform); + this.Render(visual, context, Matrix.Identity, transform); } ++this.RenderCount; diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 2d98e367f8..a17106b6b5 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -28,10 +28,10 @@ namespace Perspex.Direct2D1.Media layoutable.Arrange(new Rect(layoutable.DesiredSize)); } - var sourceSize = layoutable.Bounds.Size; + var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); var destinationRect = brush.DestinationRect.ToPixels(destinationSize); - var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceSize); - var translate = CalculateTranslate(brush, destinationRect.Size, sourceSize * scale); + var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); + var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); using (var brt = new BitmapRenderTarget( target, @@ -39,33 +39,41 @@ namespace Perspex.Direct2D1.Media destinationRect.Size.ToSharpDX())) { var renderer = new Renderer(brt); - renderer.Render(visual, null, Matrix.CreateTranslation(translate), Matrix.CreateScale(scale)); + var transform = Matrix.CreateTranslation(-sourceRect.Position) * + Matrix.CreateScale(scale) * + Matrix.CreateTranslation(translate); + renderer.Render(visual, null, transform); this.PlatformBrush = new BitmapBrush(brt, brt.Bitmap); } } - private static Vector CalculateTranslate(VisualBrush brush, Size destinationSize, Size sourceSize) + private static Vector CalculateTranslate( + VisualBrush brush, + Rect sourceRect, + Rect destinationRect, + Vector scale) { - double x = 0; - double y = 0; + var x = 0.0; + var y = 0.0; + var size = sourceRect.Size * scale; switch (brush.AlignmentX) { case AlignmentX.Center: - x = (destinationSize.Width - sourceSize.Width) / 2; + x += (destinationRect.Width - size.Width) / 2; break; case AlignmentX.Right: - x = destinationSize.Width - sourceSize.Width; + x += destinationRect.Width - size.Width; break; } switch (brush.AlignmentY) { case AlignmentY.Center: - y = (destinationSize.Height - sourceSize.Height) / 2; + y += (destinationRect.Height - size.Height) / 2; break; case AlignmentY.Bottom: - y = destinationSize.Height - sourceSize.Height; + y += destinationRect.Height - size.Height; break; } diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 9b7fb7fbdd..fc74160e74 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -247,47 +247,41 @@ namespace Perspex.Direct2D1.RenderTests.Media this.CompareImages(); } - ////[Fact] - ////public void VisualBrush_Line_Fill() - ////{ - //// Decorator target = new Decorator - //// { - //// Padding = new Thickness(8), - //// Width = 200, - //// Height = 200, - //// Child = new Line - //// { - //// X1 = 16, - //// Y1 = 16, - //// X2 = 184, - //// Y2 = 184, - //// StrokeThickness = 40, - //// StrokeStartLineCap = "Triangle", - //// StrokeEndLineCap = "Triangle", - //// Fill = new VisualBrush - //// { - //// Visual = new Border - //// { - //// Width = 92, - //// Height = 92, - //// Background = Brushes.Red, - //// BorderBrush = Brushes.Black, - //// BorderThickness = 2, - //// Child = new TextBlock - //// { - //// Text = "Perspex", - //// FontSize = 12, - //// FontFamily = "Arial", - //// HorizontalAlignment = HorizontalAlignment.Center, - //// VerticalAlignment = VerticalAlignment.Center, - //// } - //// } - //// } - //// } - //// }; + [Fact] + public void VisualBrush_SourceRect_Absolute() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + SourceRect = new RelativeRect(40, 40, 100, 100, OriginUnit.Pixels), + Visual = new Border + { + Width = 180, + Height = 180, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new Ellipse + { + Width = 100, + Height = 100, + Fill = Brushes.Yellow, + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + } + } + } + } + }; - //// this.RenderToFile(target); - //// this.CompareImages(); - ////} + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_Absolute.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_Absolute.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..e1fcdbc1a6697a32ce517e6457612c1ecf1bb68f GIT binary patch literal 3321 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∓YWw7mj5JWIy=?S7D384 zNCW1$UAjl|x~_g;jK?H4H5C$>ZIG>;CEX$2E7>Sv5G4*u?AItPWwk-h1e>w;OBlt) z{Ps|MACjJwM!ckEPHd16Xg2PXm>y;s%aUOO(-G;gG~+&vsjxx1K>Br~gjpDuvgMa0 zAXFY=ZR#RD zV*4ukNMiPh^l71KQx|NIIict$9m6o_9I5HOU>+s`Xka3q8$ Date: Fri, 4 Sep 2015 10:45:22 +0200 Subject: [PATCH 09/19] Added TileBrush.TileMode. --- src/Perspex.SceneGraph/Media/TileBrush.cs | 54 +++++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Perspex.SceneGraph/Media/TileBrush.cs b/src/Perspex.SceneGraph/Media/TileBrush.cs index 59160d70c0..da2958f6ec 100644 --- a/src/Perspex.SceneGraph/Media/TileBrush.cs +++ b/src/Perspex.SceneGraph/Media/TileBrush.cs @@ -6,6 +6,37 @@ namespace Perspex.Media { + /// + /// Describes how a is tiled. + /// + public enum TileMode + { + /// + /// A single repeat of the content will be displayed. + /// + None, + + /// + /// The content will be repeated horizontally, with alternate tiles mirrored. + /// + FlipX, + + /// + /// The content will be repeated vertically, with alternate tiles mirrored. + /// + FlipY, + + /// + /// The content will be repeated horizontally and vertically, with alternate tiles mirrored. + /// + FlipXY, + + /// + /// The content will be repeated. + /// + Tile + } + /// /// Base class for brushes which display repeating images. /// @@ -15,25 +46,25 @@ namespace Perspex.Media /// Defines the property. /// public static readonly PerspexProperty AlignmentXProperty = - PerspexProperty.Register("ALignmentX", AlignmentX.Center); + PerspexProperty.Register(nameof(AlignmentX), AlignmentX.Center); /// /// Defines the property. /// public static readonly PerspexProperty AlignmentYProperty = - PerspexProperty.Register("ALignmentY", AlignmentY.Center); + PerspexProperty.Register(nameof(AlignmentY), AlignmentY.Center); /// /// Defines the property. /// public static readonly PerspexProperty DestinationRectProperty = - PerspexProperty.Register("DestinationRect", RelativeRect.Fill); + PerspexProperty.Register(nameof(DestinationRect), RelativeRect.Fill); /// /// Defines the property. /// public static readonly PerspexProperty SourceRectProperty = - PerspexProperty.Register("SourceRect", RelativeRect.Fill); + PerspexProperty.Register(nameof(SourceRect), RelativeRect.Fill); /// /// Defines the property. @@ -41,6 +72,12 @@ namespace Perspex.Media public static readonly PerspexProperty StretchProperty = PerspexProperty.Register(nameof(Stretch), Stretch.Uniform); + /// + /// Defines the property. + /// + public static readonly PerspexProperty TileModeProperty = + PerspexProperty.Register(nameof(TileMode)); + /// /// Gets or sets the horizontal alignment of a tile in the destination. /// @@ -86,5 +123,14 @@ namespace Perspex.Media get { return (Stretch)this.GetValue(StretchProperty); } set { this.SetValue(StretchProperty, value); } } + + /// + /// Gets or sets the brush's tile mode. + /// + public TileMode TileMode + { + get { return (TileMode)this.GetValue(TileModeProperty); } + set { this.SetValue(TileModeProperty, value); } + } } } From 8b2b0273b537e9ec966f7a5407c9d7afd85ac70f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 11:13:37 +0200 Subject: [PATCH 10/19] Implemented VisualBrush.DestinationRect. --- .../Perspex.Direct2D1/Media/DrawingContext.cs | 1 - .../Media/VisualBrushImpl.cs | 31 ++++++++++----- .../Media/VisualBrushTests.cs | 37 ++++++++++++++++++ ...rush_DestinationRect_Absolute.expected.png | Bin 0 -> 1426 bytes 4 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_DestinationRect_Absolute.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 1720591c57..22c79d0f72 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -8,7 +8,6 @@ namespace Perspex.Direct2D1.Media { using System; using System.Reactive.Disposables; - using Layout; using Perspex.Media; using SharpDX; using SharpDX.Direct2D1; diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index a17106b6b5..a1a7520fb2 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -16,8 +16,7 @@ namespace Perspex.Direct2D1.Media public VisualBrushImpl( Perspex.Media.VisualBrush brush, SharpDX.Direct2D1.RenderTarget target, - Size destinationSize) - : base(brush, target, destinationSize) + Size targetSize) { var visual = brush.Visual; var layoutable = visual as ILayoutable; @@ -29,21 +28,33 @@ namespace Perspex.Direct2D1.Media } var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); - var destinationRect = brush.DestinationRect.ToPixels(destinationSize); + var destinationRect = brush.DestinationRect.ToPixels(targetSize); + var bitmapSize = CalculateBitmapSize(brush.TileMode, destinationRect.Size, targetSize); var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); + var options = CompatibleRenderTargetOptions.None; - using (var brt = new BitmapRenderTarget( - target, - CompatibleRenderTargetOptions.None, - destinationRect.Size.ToSharpDX())) + using (var brt = new BitmapRenderTarget(target, options, bitmapSize.ToSharpDX())) { var renderer = new Renderer(brt); var transform = Matrix.CreateTranslation(-sourceRect.Position) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(translate); renderer.Render(visual, null, transform); - this.PlatformBrush = new BitmapBrush(brt, brt.Bitmap); + + var result = new BitmapBrush(brt, brt.Bitmap); + this.PlatformBrush = result; + } + } + + private static Size CalculateBitmapSize(TileMode tileMode, Size size, Size targetSize) + { + switch (tileMode) + { + case TileMode.None: + return targetSize; + default: + throw new NotImplementedException(); } } @@ -53,8 +64,8 @@ namespace Perspex.Direct2D1.Media Rect destinationRect, Vector scale) { - var x = 0.0; - var y = 0.0; + var x = destinationRect.X; + var y = destinationRect.Y; var size = sourceRect.Size * scale; switch (brush.AlignmentX) diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index fc74160e74..962ecf01a6 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -283,5 +283,42 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_DestinationRect_Absolute() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + DestinationRect = new RelativeRect(92, 92, 92, 92, OriginUnit.Pixels), + Visual = new Border + { + Width = 180, + Height = 180, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new Ellipse + { + Width = 100, + Height = 100, + Fill = Brushes.Yellow, + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_DestinationRect_Absolute.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_DestinationRect_Absolute.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..9253eee16411dd2a474037f7f1b31ad03c64da65 GIT binary patch literal 1426 zcmb_cdr%Sv7^i|Hj3y^)nkgw!Qj^fqSfF7%H1kou$c(H|>uk0`={TGk&Mv}Cbc&vY z`O4cN?92y3rmb5Il%eUCDL6I3AcOhmO$jlRe4`b`j#QF)iNWj zNx(4;*plb??W>I(Tr-yC1iCn21*sL3tmP4X3wqVp63&>J-U+!Mz37UJJf0brx5u*E zdPIPKbg&!?!bVI4W5~%EYTxil{~Sza0JlA(2Zqe~F3(M|_WJaiMK5*mo$&6d5y8Hh zrt@2xJpFPI-J=g%o`8qMY)UEq&)0Ki;~#hrb=$RF>A_l_&!$HiRefB|bVaVWw zJf7a{H`ifxR|J4+|r#TgyGBQY;bo@ z^(Bd=V9F-ggwb{I$T^1Gd1JVON1IxXEtMWpPODsZgUfX%h-lLVJD__!^Q!=omSoR{ z!1ahhSJ0@k8}uqMe&sZrF=u()AdPiG$(vw|rxM4Qdc-Mt6E}V<)WzO{0Z+Zq^gTO5 ztRjbSQ|B$(*DhYQcM)rd+JV+=@8$j?T#cOIU-dTB?HE>~->;7Pwxh51>zB;pCd8Ug zt*Je6;jexOoN>nRIX=`xmmMwf3e-Pi>}8cMX>z8!+63KSrtpJXyvk~>e?m=HQoqN6 zf3i8ToW~+ z>@++W_b&}mv-4`q<&!aF8Od_7?SMh#8!U14!aY4U%Zx1Rs5@s0v ztQLC@F@w0md?_9P=3?&uc+t4(?oDDQW%)WYpHIEWW>$#!Dg4bpJKs~iQLU;5GcZ*t zBjf0dsExfEw8BYUufMU`RyT3f^1jPM{hsxz4gFuzj+K=!=Y~*2xRd7?rmH*Iu>mnb z8vdP-+xxS>U}`)`#Ia#I@!g)7D;i!dYT-&%8csQVHo7a2I&wP$oq{@4$!&?#fksQQ wpjJ0W2pss)n!?n1CF%Vm9sc=a1%!5;Z9_#@erLmzO)DRu2y!$@7M9BW8^BFwzyJUM literal 0 HcmV?d00001 From b2b50090ba86ff298e650e86295b40a58e849a8b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 11:22:27 +0200 Subject: [PATCH 11/19] Added clipping to VisualBrush.DestinationRect. --- .../Rendering/RendererBase.cs | 4 +- .../Media/VisualBrushImpl.cs | 2 +- .../Media/VisualBrushTests.cs | 38 ++++++++++++++++++ ...Rect_DestinationRect_Absolute.expected.png | Bin 0 -> 2031 bytes 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Absolute.expected.png diff --git a/src/Perspex.SceneGraph/Rendering/RendererBase.cs b/src/Perspex.SceneGraph/Rendering/RendererBase.cs index 2dce69e3d0..d5b593d5e0 100644 --- a/src/Perspex.SceneGraph/Rendering/RendererBase.cs +++ b/src/Perspex.SceneGraph/Rendering/RendererBase.cs @@ -43,9 +43,11 @@ namespace Perspex.Rendering /// The visual to render. /// An optional platform-specific handle. /// The transform. - public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix transform) + /// An optional clip rectangle. + public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix transform, Rect? clip = null) { using (var context = this.CreateDrawingContext(handle)) + using (clip.HasValue ? context.PushClip(clip.Value) : null) { this.Render(visual, context, Matrix.Identity, transform); } diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index a1a7520fb2..d2467bbb86 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -40,7 +40,7 @@ namespace Perspex.Direct2D1.Media var transform = Matrix.CreateTranslation(-sourceRect.Position) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(translate); - renderer.Render(visual, null, transform); + renderer.Render(visual, null, transform, destinationRect); var result = new BitmapBrush(brt, brt.Bitmap); this.PlatformBrush = result; diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 962ecf01a6..7d32014cc3 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -320,5 +320,43 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_SourceRect_DestinationRect_Absolute() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + SourceRect = new RelativeRect(40, 40, 100, 100, OriginUnit.Pixels), + DestinationRect = new RelativeRect(92, 92, 92, 92, OriginUnit.Pixels), + Visual = new Border + { + Width = 180, + Height = 180, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new Ellipse + { + Width = 100, + Height = 100, + Fill = Brushes.Yellow, + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Absolute.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Absolute.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..b2abed4041535e8c15d543fc4bca4951a4fccd9c GIT binary patch literal 2031 zcmcgtiCYrb7I$MP+$h(~94sk!eOfLF28!X9Yif#Hnp!TXnB#5(Hkg?}CWeJ8nU!0` zBwEgp=1PuD)=P6u6ro&Ehcwam$ob~|3GaU2z2|rD?=I)ubI!d5ULI&gkQPWvN=nhi z8Ra99ec$VzA0%z#xuyz#5Zje|L)sj0P?in2gy zi4Ono67FKr;yspMSYlpC^*Sky3T*rORZ6s-0# zGVW)Yz0scmT*645wvUoOe`gAplq{|a_HFX#yP0Z&?wPw8QC@M9c@K)~Az#Gx!Ty6r zrkY>v;S2=mT^v5%*Te8wNx%7}Kp5=B1G+a%b>1Ze57E?c=6|eiFifL93vX+t`-u<5 zS0#e8C1 z*`tsA2(g+why0;0H_A_FkC#vpk4sODz1Ds_+g)gp?3N1O1K96AFC!dUgm!Ce5EszAYh2#6`WMhu-4B!n zZ>*uCnbKWxV?|`@HJJmTFlly^@fIPhavYOM`uDU~vCIJitE9Q)7`oC*{S9l-u=0j| zRIj!kttSQiRohglp`pE+ez5ZXqq})tkdEVqVDc+?!{{}5ukqwUIznhVjZ$?!Uo77> zlj!MVJw!j!9mbJ&<$m!G!J7jOZuU-ua%ycp+%-;#8GY)BFX_{ew(j&hWZVM^ z{idf>i2)hurmJ-BqP|)trFgR%tBSVAy0I?Fu0uLtLwVxr$5V!8i4oa9Wyh{VYFC*! z1Cn_BK6XrUF=ulr$DhBgN;nV!u+)mkXb_V-#f*cw!GFHHesodciEGJPx%Rm?Y2imF zO%Zt1?Cs$yOhJOX1X+DrHxBuY4Gbe)lfjbx=Ng7mbO zHU*vz7+=U+JuzF$q8eyGnQ|oM>iv2Gu)k%U=Q_`Bn*B2i?c!AiX=KX0<5Uy$NUVC`f^1hK zK*wh`p9q})rkeht+~IcWhg>hlNwi|G0Jfbk#x>mozA|CKr%T5iHuK|F4rd$pB`mZw zA@QiojVEjDb}ha9Y!`jER0lpPs{;?RAs4ZADr;nKQ?G93dqcd-n@N4m$T5p9&|HW7 zt4n_t&DaXwjL5#Dl0peD=0o0j=+|>jkG&V1 zr)H4=g$%5c1741qgdef*c6)g+io3M8jjwG&^poUrkmTsp*G^B<)-ScTmuy`h!a0Nn zKn9S+o_Y+hM0e^UukdHZQruV-g4mZuCK)UnIAVSf5w12iZ}fZE*j}OOL0HfqqMd%$;OY%Uhi*Y4pD)EbC%LZL~bOTdRzynR)0j zwXh4pA{Fi5zRgitmafY6zAC9HV2~ayRDSk+K6QrUEqM(b)1e&YVi6O!eUboeX!VBt z5*UPcZ?(yG+M(bcpM2wIUXir*hevKkk^EtyiDyU+0>oUGDVyW2sa*d|r#aRW@U`erqvziGxp8=DaS`xl9qqIg%pI5LrCJRS6X)WyjI J)#MOP_%FKVk5T{t literal 0 HcmV?d00001 From aaed6cab9148eb3e69f3397494415b13daf8f29f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 15:34:36 +0200 Subject: [PATCH 12/19] Add tests for percent rects on VisualBrush --- .../Media/VisualBrushTests.cs | 38 ++++++++++++++++++ ...eRect_DestinationRect_Percent.expected.png | Bin 0 -> 1996 bytes 2 files changed, 38 insertions(+) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Percent.expected.png diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 7d32014cc3..4d404cfefd 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -358,5 +358,43 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_SourceRect_DestinationRect_Percent() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + SourceRect = new RelativeRect(0.22, 0.22, 0.56, 0.56, OriginUnit.Percent), + DestinationRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, OriginUnit.Percent), + Visual = new Border + { + Width = 180, + Height = 180, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new Ellipse + { + Width = 100, + Height = 100, + Fill = Brushes.Yellow, + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Percent.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_SourceRect_DestinationRect_Percent.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1aedd35831100ebfdacbde93529d21e50629d698 GIT binary patch literal 1996 zcmcgt`#%#38)w>RC}|VUXfMi`<M*(l@)lj{GBWZv9dKA;tl!EMx{7oI z#-})@bf%?&tNipu_SVU(z-pl+uqSJcz#Jb!T()6b9aZ2 zU?+R`fG-^4@Z&7Y$G`iH*S^0YY!W*Q)&$0a;)?{sq?R+7eTWHtGC}o8P7kIJSN;GS`Mf`Oc4wUma;nX zpO|6}R`&b97`AkeC#^5PQbYm(= zc_YwOQ%cR^+KO=bX%wXAJ-6&JNn5~q-5Y%WFyn!UYjYP%tG)oDV1H%71RN&cR+urf zVsr+QjYZ@Jgh4afwEU41zt;$7uOYR$yPijYW+n_p9x7d)(?^EYa5bnoVvn2dWS3+U zkcLG}4YU1R|89@vR#+U~I$5buey;EAd;z~L*ICHER!WEBMW>i->bGM@_hWp%BG4$; zcik)u&A=V*cVLs;S-_?~`lA9ykaX1oAU&NBwTvsZTx+Y>ww-6=>R);ryl1)+;dilV zJUPNz9qU}y1=eE^?S4*J876Jc{W3njrgeV2eN6nebukEdx`8ev{pmfrWOXjkYl_%} z^wTe-of`GzvI($}7WzoQ=X^E2f@)O8@s3$9v;?syLfZgU ze|uUmF~^VvZN5h6OZ7`0Dn`hTN4zPcQu-O(^so~pn*Hiq1x6<*us;68nki8AzJGjL z#L6W;0Ai1uR!fdxaQRBZDZL<3;?kYEm8p0PFp`elsgIR$plp`$FhzjOwDFp~JkN{@ z3~SwSXXr3EBx%v_3!4|!jmVWA`(8Mne_k4mvUOV*vgtr+7>n>hKom^mfvRu5MB@}7 z_CGeiJu-z{KWhlqd-%|}45w^9OgRMB(*&2J_bX>%QUGwR0YeY9{OcA<3ekLK_S2;k z;5#d;UG-D#cHuF{t5g%m|H}3Lycu2_hse#4=ux9<<}OrXbaqZ^4HY?xJd*&KRlFto z5TQ37pNyN6e`NY-qzJs756Ju+WWh>+uA+ddBZbkpUBgN5Q7o-xH{5U6=F`T%R%X7) z>xwBBtP91+6QgL73;ceOqg~@dL$pyQAhVzDh5y>b-`=3!CaVEerzS)h^I?;YAW_%k zX?y<{tp8lR6FPZox!-v#DAx+qsPkroo8AcT!e7HKbEzh1TU+fI%fezO!?5q92vy4{ z9$(s4vnuc{HF6e3c!Y+d3=CCTJ#9Ya%nXk_T}`OydU=*C1vbH(uq~D0eH;SsxMkE5 z<#gM_EVU5)q;IU#dIYh5L7(}L-y4H_W{BKUipNrhYhW91c)fl7Lf9u=IY^I69BG~* zfw*xie0rQ81u3caPn!0WKM-U}AerW|FY}6-EgA(2yG(U3Ouv zM=h_CT7*{vUx#UeLyROY!&exGrM&-JwuKMV;wZYngJ-@bI`3;cF9ZwMR+PIN+?9_3 z=qC1uE5+~xwZz+VwXQRlI4LtwsxhFcdBC(8B;6Gmsj_h>;QE8(ebXpsT-Z_LZci^@unq)>QXF>NLLvo40WFm<*UE%9T6$Akg;Msr&8Lq(Motv5NNd{GW>zj8&TNi uXbv%6j0@_dm>%#QH#SuH-`h)~kegT0$On1+YekLT->VL`PB@Nr0PVki{DaW| literal 0 HcmV?d00001 From 4cb1e66a56321a4fb5cc885de870e725ef5cde7a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 15:39:34 +0200 Subject: [PATCH 13/19] Added support for VisualBrush TileMode.Tile. --- .../Media/VisualBrushImpl.cs | 41 ++++++++++-------- .../Media/VisualBrushTests.cs | 38 ++++++++++++++++ .../VisualBrush/VisualBrush_Tile.expected.png | Bin 0 -> 1628 bytes 3 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index d2467bbb86..41d63c2beb 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -29,7 +29,7 @@ namespace Perspex.Direct2D1.Media var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); var destinationRect = brush.DestinationRect.ToPixels(targetSize); - var bitmapSize = CalculateBitmapSize(brush.TileMode, destinationRect.Size, targetSize); + var bitmapSize = CalculateBitmapSize(brush.TileMode, sourceRect.Size, targetSize); var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); var options = CompatibleRenderTargetOptions.None; @@ -43,6 +43,8 @@ namespace Perspex.Direct2D1.Media renderer.Render(visual, null, transform, destinationRect); var result = new BitmapBrush(brt, brt.Bitmap); + result.ExtendModeX = ExtendMode.Wrap; + result.ExtendModeY = ExtendMode.Wrap; this.PlatformBrush = result; } } @@ -53,6 +55,8 @@ namespace Perspex.Direct2D1.Media { case TileMode.None: return targetSize; + case TileMode.Tile: + return size; default: throw new NotImplementedException(); } @@ -68,24 +72,27 @@ namespace Perspex.Direct2D1.Media var y = destinationRect.Y; var size = sourceRect.Size * scale; - switch (brush.AlignmentX) + if (brush.TileMode == TileMode.None) { - case AlignmentX.Center: - x += (destinationRect.Width - size.Width) / 2; - break; - case AlignmentX.Right: - x += destinationRect.Width - size.Width; - break; - } + switch (brush.AlignmentX) + { + case AlignmentX.Center: + x += (destinationRect.Width - size.Width) / 2; + break; + case AlignmentX.Right: + x += destinationRect.Width - size.Width; + break; + } - switch (brush.AlignmentY) - { - case AlignmentY.Center: - y += (destinationRect.Height - size.Height) / 2; - break; - case AlignmentY.Bottom: - y += destinationRect.Height - size.Height; - break; + switch (brush.AlignmentY) + { + case AlignmentY.Center: + y += (destinationRect.Height - size.Height) / 2; + break; + case AlignmentY.Bottom: + y += destinationRect.Height - size.Height; + break; + } } return new Vector(x, y); diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 4d404cfefd..ac8c39f113 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -396,5 +396,43 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_Tile() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.None, + TileMode = TileMode.Tile, + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..ab0cf7e1d05de7a6f83c0007b3bf4098af64031d GIT binary patch literal 1628 zcmchX`BTyf7{uG?L_G9sibvolb28x;rm_P8bJwuPyI4IFMCITLrN5%_2$e{;S-P5N zL+h*Ygu}zE=2NnI0n;8vu_OT>M&j2_)E~CUQha-~pOS)iSvub4=ljpj5@o>%7`I7+ zC8Q6Cm}L((x8|}LBw*ZbBT)2KINd|85J!cPi+^5CLfLjR8vZ)ca9TekT9Y{Y<|iff zz&dPDdvPv~rIyi^O5r)!q=*`dv5)?|~QZkZy=}hRruQ!n1?wgrb(B^AQxBsU`vOLYY+yxMd z0qDXj1|Pa`1>$O7`UM~=uvevaC9sv>P^$jr^{ol=l03j34!CTWOBy(#&#PK=4pVg zJ<*vdB9L%pC2FoMaJ96$i0CiVQ<}xmxQQ)Nqxh@=+S+Pz7bpPLt`UBDx<4McoFm0q9wou^qu3Dw|(8n~bjA3zHd}9uQKc%$O&5 z8xg)hr$`??`mrab+1SYlJ}>*5e*U--)2;oGG$yey^Dd^{N^TVzdn)Mob$erusf$lK zC=8or0j@R`CV2I68gCT4n{xc^_sw`5>UHbE2~{mUpkb`-`EphM&uc%OagF@oInP9+coaesjl}-sE}6Coz3v!+fE| z$rie2x}@)iyN58L&$4PN)!PcU_u+kb`x(KZ_3{v|_{EDE3|CBk&KfotMYKdJns{f0 zSBd?iAj)G)pvx1zpe9*mK(Q$)!h$LVdZ%7$+$F|jzyxa<=a7D~#2UHc{@^R>wO8bu zl-gVm>zX3xl{Umwj_S8Z83|Dii>QCIR5KD>y7kI literal 0 HcmV?d00001 From fcf151e2ae81f11e53ded4cc7bd23a925c23b75c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 15:50:56 +0200 Subject: [PATCH 14/19] Implemented DestinationRect with TileMode.Tile. --- .../Media/VisualBrushImpl.cs | 38 ++++++++++++++---- .../Media/VisualBrushTests.cs | 39 +++++++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 41d63c2beb..da28f1039c 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -40,11 +40,31 @@ namespace Perspex.Direct2D1.Media var transform = Matrix.CreateTranslation(-sourceRect.Position) * Matrix.CreateScale(scale) * Matrix.CreateTranslation(translate); - renderer.Render(visual, null, transform, destinationRect); + + Rect drawRect; + + if (brush.TileMode == TileMode.None) + { + drawRect = destinationRect; + } + else + { + drawRect = new Rect(0, 0, destinationRect.Width, destinationRect.Height); + } + + renderer.Render(visual, null, transform, drawRect); var result = new BitmapBrush(brt, brt.Bitmap); result.ExtendModeX = ExtendMode.Wrap; result.ExtendModeY = ExtendMode.Wrap; + + if (brush.TileMode != TileMode.None) + { + result.Transform = SharpDX.Matrix3x2.Translation( + (float)destinationRect.X, + (float)destinationRect.Y); + } + this.PlatformBrush = result; } } @@ -68,12 +88,12 @@ namespace Perspex.Direct2D1.Media Rect destinationRect, Vector scale) { - var x = destinationRect.X; - var y = destinationRect.Y; - var size = sourceRect.Size * scale; - if (brush.TileMode == TileMode.None) { + var x = destinationRect.X; + var y = destinationRect.Y; + var size = sourceRect.Size * scale; + switch (brush.AlignmentX) { case AlignmentX.Center: @@ -93,9 +113,13 @@ namespace Perspex.Direct2D1.Media y += destinationRect.Height - size.Height; break; } - } - return new Vector(x, y); + return new Vector(x, y); + } + else + { + return new Vector(); + } } public override void Dispose() diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index ac8c39f113..da403607d7 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -434,5 +434,44 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_Tile_DestinationRect() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.None, + TileMode = TileMode.Tile, + DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, OriginUnit.Percent), + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } From 2dc6cfe0ef7cb0b827f4a76fd73a4918277faaf5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 16:19:58 +0200 Subject: [PATCH 15/19] Simplify some code. --- .../Perspex.Direct2D1/Media/VisualBrushImpl.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index da28f1039c..8e79d61f82 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -29,7 +29,7 @@ namespace Perspex.Direct2D1.Media var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); var destinationRect = brush.DestinationRect.ToPixels(targetSize); - var bitmapSize = CalculateBitmapSize(brush.TileMode, sourceRect.Size, targetSize); + var bitmapSize = brush.TileMode == TileMode.None ? targetSize : sourceRect.Size; var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); var options = CompatibleRenderTargetOptions.None; @@ -69,19 +69,6 @@ namespace Perspex.Direct2D1.Media } } - private static Size CalculateBitmapSize(TileMode tileMode, Size size, Size targetSize) - { - switch (tileMode) - { - case TileMode.None: - return targetSize; - case TileMode.Tile: - return size; - default: - throw new NotImplementedException(); - } - } - private static Vector CalculateTranslate( VisualBrush brush, Rect sourceRect, From 73737c6fc479820be54709295451b8e8b41ac8ce Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 16:20:05 +0200 Subject: [PATCH 16/19] Added expected output. --- ...VisualBrush_Tile_DestinationRect.expected.png | Bin 0 -> 1723 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..64695d4bb16c46f040557676c8a781de4f4be10e GIT binary patch literal 1723 zcmcJQdsNZ~6vvr`m8I_VSkw91vRs;)Z$&~)UD89Fj|>$pC47*NB(>iZnAx#;W~m^W z<)m$vX^M%7qE1e;r2>jQ#0-VXfKusKkSGE=^56C6`s3bv?)RU2?>V3Exg|$K4;UMn z8$lotV`NBBxV{d4jP-{4+;W^=r!QaN!w>9-&_^uZ=$p040Y?HLkhZdo+LKTS#PAd{ zC?N7&ynL#SEW~Vup6EX`>W`#ufW?&s8B*etTCTY$OQi#I>78A@L6i<^pxfoCGbVz(u75=d6KwB9AhFRrb0-|HA-&Sp3!gj_r*@>TQ zIl^czBF&=ME%)0w4OBbo_(YZ=q6xE03D{aktJHszHEw~*zV2C`ro0GRqv>!hUQUwS zmd{AzB^vq6tD=LUkp(Cx+RH=cjMujHB zET{2VLHH0wW_yVpRz&r z;SNq)_=16qgxgNREb}8pM3j1`mnez5-Q7~V!4+INU`Y}dIuP!k$K&5P3^sN-Mwdst zFfvF!9B}=2o6)0RrhLENed}6tBLib-VextY!2eq|S>~j_4{V#n5+z?jo73q4Vic1!=>wJ@@b3|BgwT&n`C2| z7qyv;^hR#8@eG(fI&dfb-#s|kWu z=+^>oe5DloEZ!p5@MIOGUf?5Z>LktV>cSX0S#&Y(4XI60owjM8qc*JvMnEkQdtgYn zf$Dx|2XPef7JRVS?Hy_->7gF}-q)>BeHRkWj_n1xtVRID-?3qXRX@_iH@VSuaJy9P z7-vJt>9A~I%<9z^v2#jkQsfh}&qh*rvA%gi;=OTR#6(nose&&1Mg8vfq4cI3)up!`#eS!k)k zmlqRR!&;7`#i3PSc`sVl_x)V^B(GeuQ2|XuHy}V5Q5^96_-C`oI5i*p?aK~Ll$k z`3CA-bvkYre_t50r2?+H@=sqnijNyvDnKM%Mujm5bJZlANJANjPZH&b=S$bs`K6L6 z9vQNFo+&~&FvJ_;$l*`ANW-U1)nbg$9EAvTjmKkMM6Z$9a!q^O$Qi_KfTxk`9l2uKuPh1Ql;EZd&qDLI#Hh(f6M?|1WorE8_qF literal 0 HcmV?d00001 From 4787e24880de03c10bb6a36fe1ac38db70524690 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 16:54:32 +0200 Subject: [PATCH 17/19] Trying to make tiling work right... --- .../Media/VisualBrushImpl.cs | 2 +- .../Media/VisualBrushTests.cs | 6 ++++-- .../VisualBrush/VisualBrush_Tile.expected.png | Bin 1628 -> 1723 bytes ...ualBrush_Tile_DestinationRect.expected.png | Bin 1723 -> 0 bytes 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 8e79d61f82..13b4d07533 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -29,7 +29,7 @@ namespace Perspex.Direct2D1.Media var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); var destinationRect = brush.DestinationRect.ToPixels(targetSize); - var bitmapSize = brush.TileMode == TileMode.None ? targetSize : sourceRect.Size; + var bitmapSize = brush.TileMode == TileMode.None ? targetSize : destinationRect.Size; var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); var options = CompatibleRenderTargetOptions.None; diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index da403607d7..6a9bc51c3c 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -411,6 +411,7 @@ namespace Perspex.Direct2D1.RenderTests.Media { Stretch = Stretch.None, TileMode = TileMode.Tile, + DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, OriginUnit.Percent), Visual = new Border { Width = 92, @@ -436,7 +437,7 @@ namespace Perspex.Direct2D1.RenderTests.Media } [Fact] - public void VisualBrush_Tile_DestinationRect() + public void VisualBrush_Tile_Alignment_BottomRight() { Decorator target = new Decorator { @@ -449,7 +450,8 @@ namespace Perspex.Direct2D1.RenderTests.Media { Stretch = Stretch.None, TileMode = TileMode.Tile, - DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, OriginUnit.Percent), + AlignmentX = AlignmentX.Right, + AlignmentY = AlignmentY.Bottom, Visual = new Border { Width = 92, diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile.expected.png index ab0cf7e1d05de7a6f83c0007b3bf4098af64031d..64695d4bb16c46f040557676c8a781de4f4be10e 100644 GIT binary patch literal 1723 zcmcJQdsNZ~6vvr`m8I_VSkw91vRs;)Z$&~)UD89Fj|>$pC47*NB(>iZnAx#;W~m^W z<)m$vX^M%7qE1e;r2>jQ#0-VXfKusKkSGE=^56C6`s3bv?)RU2?>V3Exg|$K4;UMn z8$lotV`NBBxV{d4jP-{4+;W^=r!QaN!w>9-&_^uZ=$p040Y?HLkhZdo+LKTS#PAd{ zC?N7&ynL#SEW~Vup6EX`>W`#ufW?&s8B*etTCTY$OQi#I>78A@L6i<^pxfoCGbVz(u75=d6KwB9AhFRrb0-|HA-&Sp3!gj_r*@>TQ zIl^czBF&=ME%)0w4OBbo_(YZ=q6xE03D{aktJHszHEw~*zV2C`ro0GRqv>!hUQUwS zmd{AzB^vq6tD=LUkp(Cx+RH=cjMujHB zET{2VLHH0wW_yVpRz&r z;SNq)_=16qgxgNREb}8pM3j1`mnez5-Q7~V!4+INU`Y}dIuP!k$K&5P3^sN-Mwdst zFfvF!9B}=2o6)0RrhLENed}6tBLib-VextY!2eq|S>~j_4{V#n5+z?jo73q4Vic1!=>wJ@@b3|BgwT&n`C2| z7qyv;^hR#8@eG(fI&dfb-#s|kWu z=+^>oe5DloEZ!p5@MIOGUf?5Z>LktV>cSX0S#&Y(4XI60owjM8qc*JvMnEkQdtgYn zf$Dx|2XPef7JRVS?Hy_->7gF}-q)>BeHRkWj_n1xtVRID-?3qXRX@_iH@VSuaJy9P z7-vJt>9A~I%<9z^v2#jkQsfh}&qh*rvA%gi;=OTR#6(nose&&1Mg8vfq4cI3)up!`#eS!k)k zmlqRR!&;7`#i3PSc`sVl_x)V^B(GeuQ2|XuHy}V5Q5^96_-C`oI5i*p?aK~Ll$k z`3CA-bvkYre_t50r2?+H@=sqnijNyvDnKM%Mujm5bJZlANJANjPZH&b=S$bs`K6L6 z9vQNFo+&~&FvJ_;$l*`ANW-U1)nbg$9EAvTjmKkMM6Z$9a!q^O$Qi_KfTxk`9l2uKuPh1Ql;EZd&qDLI#Hh(f6M?|1WorE8_qF literal 1628 zcmchX`BTyf7{uG?L_G9sibvolb28x;rm_P8bJwuPyI4IFMCITLrN5%_2$e{;S-P5N zL+h*Ygu}zE=2NnI0n;8vu_OT>M&j2_)E~CUQha-~pOS)iSvub4=ljpj5@o>%7`I7+ zC8Q6Cm}L((x8|}LBw*ZbBT)2KINd|85J!cPi+^5CLfLjR8vZ)ca9TekT9Y{Y<|iff zz&dPDdvPv~rIyi^O5r)!q=*`dv5)?|~QZkZy=}hRruQ!n1?wgrb(B^AQxBsU`vOLYY+yxMd z0qDXj1|Pa`1>$O7`UM~=uvevaC9sv>P^$jr^{ol=l03j34!CTWOBy(#&#PK=4pVg zJ<*vdB9L%pC2FoMaJ96$i0CiVQ<}xmxQQ)Nqxh@=+S+Pz7bpPLt`UBDx<4McoFm0q9wou^qu3Dw|(8n~bjA3zHd}9uQKc%$O&5 z8xg)hr$`??`mrab+1SYlJ}>*5e*U--)2;oGG$yey^Dd^{N^TVzdn)Mob$erusf$lK zC=8or0j@R`CV2I68gCT4n{xc^_sw`5>UHbE2~{mUpkb`-`EphM&uc%OagF@oInP9+coaesjl}-sE}6Coz3v!+fE| z$rie2x}@)iyN58L&$4PN)!PcU_u+kb`x(KZ_3{v|_{EDE3|CBk&KfotMYKdJns{f0 zSBd?iAj)G)pvx1zpe9*mK(Q$)!h$LVdZ%7$+$F|jzyxa<=a7D~#2UHc{@^R>wO8bu zl-gVm>zX3xl{Umwj_S8Z83|Dii>QCIR5KD>y7kI diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_DestinationRect.expected.png deleted file mode 100644 index 64695d4bb16c46f040557676c8a781de4f4be10e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1723 zcmcJQdsNZ~6vvr`m8I_VSkw91vRs;)Z$&~)UD89Fj|>$pC47*NB(>iZnAx#;W~m^W z<)m$vX^M%7qE1e;r2>jQ#0-VXfKusKkSGE=^56C6`s3bv?)RU2?>V3Exg|$K4;UMn z8$lotV`NBBxV{d4jP-{4+;W^=r!QaN!w>9-&_^uZ=$p040Y?HLkhZdo+LKTS#PAd{ zC?N7&ynL#SEW~Vup6EX`>W`#ufW?&s8B*etTCTY$OQi#I>78A@L6i<^pxfoCGbVz(u75=d6KwB9AhFRrb0-|HA-&Sp3!gj_r*@>TQ zIl^czBF&=ME%)0w4OBbo_(YZ=q6xE03D{aktJHszHEw~*zV2C`ro0GRqv>!hUQUwS zmd{AzB^vq6tD=LUkp(Cx+RH=cjMujHB zET{2VLHH0wW_yVpRz&r z;SNq)_=16qgxgNREb}8pM3j1`mnez5-Q7~V!4+INU`Y}dIuP!k$K&5P3^sN-Mwdst zFfvF!9B}=2o6)0RrhLENed}6tBLib-VextY!2eq|S>~j_4{V#n5+z?jo73q4Vic1!=>wJ@@b3|BgwT&n`C2| z7qyv;^hR#8@eG(fI&dfb-#s|kWu z=+^>oe5DloEZ!p5@MIOGUf?5Z>LktV>cSX0S#&Y(4XI60owjM8qc*JvMnEkQdtgYn zf$Dx|2XPef7JRVS?Hy_->7gF}-q)>BeHRkWj_n1xtVRID-?3qXRX@_iH@VSuaJy9P z7-vJt>9A~I%<9z^v2#jkQsfh}&qh*rvA%gi;=OTR#6(nose&&1Mg8vfq4cI3)up!`#eS!k)k zmlqRR!&;7`#i3PSc`sVl_x)V^B(GeuQ2|XuHy}V5Q5^96_-C`oI5i*p?aK~Ll$k z`3CA-bvkYre_t50r2?+H@=sqnijNyvDnKM%Mujm5bJZlANJANjPZH&b=S$bs`K6L6 z9vQNFo+&~&FvJ_;$l*`ANW-U1)nbg$9EAvTjmKkMM6Z$9a!q^O$Qi_KfTxk`9l2uKuPh1Ql;EZd&qDLI#Hh(f6M?|1WorE8_qF From ec55baf12d714eec861bf092f37e2d5331429563 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 18:57:25 +0200 Subject: [PATCH 18/19] Fix tile alignment. --- .../Media/VisualBrushImpl.cs | 50 ++++++++---------- ...sh_Tile_Alignment_BottomRight.expected.png | Bin 0 -> 928 bytes 2 files changed, 22 insertions(+), 28 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_Alignment_BottomRight.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 13b4d07533..69bcf97737 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -46,6 +46,7 @@ namespace Perspex.Direct2D1.Media if (brush.TileMode == TileMode.None) { drawRect = destinationRect; + transform *= Matrix.CreateTranslation(destinationRect.Position); } else { @@ -75,38 +76,31 @@ namespace Perspex.Direct2D1.Media Rect destinationRect, Vector scale) { - if (brush.TileMode == TileMode.None) - { - var x = destinationRect.X; - var y = destinationRect.Y; - var size = sourceRect.Size * scale; - - switch (brush.AlignmentX) - { - case AlignmentX.Center: - x += (destinationRect.Width - size.Width) / 2; - break; - case AlignmentX.Right: - x += destinationRect.Width - size.Width; - break; - } - - switch (brush.AlignmentY) - { - case AlignmentY.Center: - y += (destinationRect.Height - size.Height) / 2; - break; - case AlignmentY.Bottom: - y += destinationRect.Height - size.Height; - break; - } + var x = 0.0; + var y = 0.0; + var size = sourceRect.Size * scale; - return new Vector(x, y); + switch (brush.AlignmentX) + { + case AlignmentX.Center: + x += (destinationRect.Width - size.Width) / 2; + break; + case AlignmentX.Right: + x += destinationRect.Width - size.Width; + break; } - else + + switch (brush.AlignmentY) { - return new Vector(); + case AlignmentY.Center: + y += (destinationRect.Height - size.Height) / 2; + break; + case AlignmentY.Bottom: + y += destinationRect.Height - size.Height; + break; } + + return new Vector(x, y); } public override void Dispose() diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_Alignment_BottomRight.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Tile_Alignment_BottomRight.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0918b7fd511a807b03c40468527b049a248172d0 GIT binary patch literal 928 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(Ggg*~`<#F{C2y?OoeGo&dpA`H+TBqo|&JE-Y8D~ZrASq>__)${f~;9j@?i5eel-B-zf2O z&9>Vo=cd%}&6|DtbJ4k~IUBT3*=<|C-?i+0b9MCRZx5ak8z}H@kI78`pVd!2uRYoO z<+RHA?QDu5?zHiXBotE~evZgw{efmG`Q}HJKou4fJe&k3^ z`fqm9_SC#wwYs+rqAXn6C;r*I5`D69L(_qJ2bw2`#zWihyzAHBQM<3^v@RsQZz#3+ Y!>exB`>J;H%|{?1Pgg&ebxsLQ00#PR6#xJL literal 0 HcmV?d00001 From c69686142cdb78a13ff32def9b8d3f929ebdb6cb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 4 Sep 2015 19:03:41 +0200 Subject: [PATCH 19/19] Added VisualBrush flips. --- .../Media/VisualBrushImpl.cs | 4 +- .../Media/VisualBrushTests.cs | 117 ++++++++++++++++++ .../VisualBrush_FlipX.expected.png | Bin 0 -> 2152 bytes .../VisualBrush_FlipXY.expected.png | Bin 0 -> 2176 bytes .../VisualBrush_FlipY.expected.png | Bin 0 -> 1653 bytes 5 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipX.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipXY.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipY.expected.png diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index 69bcf97737..8638162ff5 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -56,8 +56,8 @@ namespace Perspex.Direct2D1.Media renderer.Render(visual, null, transform, drawRect); var result = new BitmapBrush(brt, brt.Bitmap); - result.ExtendModeX = ExtendMode.Wrap; - result.ExtendModeY = ExtendMode.Wrap; + result.ExtendModeX = (brush.TileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; + result.ExtendModeY = (brush.TileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; if (brush.TileMode != TileMode.None) { diff --git a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs index 6a9bc51c3c..7b32a223ce 100644 --- a/tests/Perspex.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Perspex.RenderTests/Media/VisualBrushTests.cs @@ -475,5 +475,122 @@ namespace Perspex.Direct2D1.RenderTests.Media this.RenderToFile(target); this.CompareImages(); } + + [Fact] + public void VisualBrush_FlipX() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipX, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent), + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + + [Fact] + public void VisualBrush_FlipY() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipY, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent), + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + + [Fact] + public void VisualBrush_FlipXY() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new VisualBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipXY, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, OriginUnit.Percent), + Visual = new Border + { + Width = 92, + Height = 92, + Background = Brushes.Red, + BorderBrush = Brushes.Black, + BorderThickness = 2, + Child = new TextBlock + { + Text = "Perspex", + FontSize = 12, + FontFamily = "Arial", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + } + } + } + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipX.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipX.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..da7003b4193d8dddf07ea6a7e6eaa6600c0fac97 GIT binary patch literal 2152 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggL&(#`F{C2y?cI%i(y1bC4_jw@2zzM-?C5HlrsXl^4pVASo6*kZ042w% zMvbc#ytKSBibM2l_Tt2*ZiRkrxO%;-sYo9vZcz;>y?>8=`z52{btZXh^JRKqu0-6R2K?x2^ z4m5Z)GEQWookYQ7#)tJE^j;@UwbNVk^VZIS%VveFKNolX=f3xK&8zQPwK5Ue*J|c5 z&k>H^aI!z*e#qag_xJqTo%L3>wR}$g>Kn(uecCZS^nu~4(^}s?&slkVfz^~Zzccsm zJp9+2OQ(2dZNj?R`Nwz3f0ihi|M$zL-&|F9&o7MNmV0gZ%R;B0Is5Fa`Kx1Bygu&P zwej`cM=$KQ)m!J-uh?b3?fLto|Lppn*A?D)_qfXDo4sw$`HvNI9%pU5CwJT|xk%=> zU*w)|I^X7Qnju`A+;>-dTjkq#qI+NNyu8_NUG>%ejr->xo^dxgOV0fG;~n$T`;V7h zzqOA)vZr>%{~U9H=6Q>%{zTR?U0xC=vPyhHw3MxQ)7!dxYn~R$T>k$2P*5>XT=VZe zarSkmW_|u|A3xdBDE!^|djXsOZ#J*p@Bc0GwBfHEJInswcw>Kl_UHGjR*%oL|!%AA7Z=mRq}7;YeLBeYCHa}S~dT3cIfWf9cx>6N7!zkr~Sh5 zd-J_Bak8)f|LHg?wL$E-ft&p48EXt9K3DP;_~jg2_Ww-v>Q(nr?`6uEikDP{UOoG_ zPgeVtY>v6w4qGulyYBk9uZvdMYsU*cJ91}6a{c}&YtQucFYZcJ-K|aGy|y8(IH$`n z^0Dsi5{GvoAN4MC--uVZ{^97y8==kRN9|5lw*Ri$zw>v}kH>Yz?Hhk@eHUuGsw(Cy zv$Y;iaZ=uyi93^S##ddApZ8L7FZ13x;kP?Y6Lhbg`B!xM)}~|8+g>&_l@ zbEbB2+Pg0cemA}ov|U{OX8H6lTQ0u7S$!?(Z0YqP?_K5piFORM%OTiv;f`9i T=j8yl78pET{an^LB{Ts5Q26?0 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipXY.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_FlipXY.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfcdb2dd3cafac0231e49782abfdfe0a9ac0eea GIT binary patch literal 2176 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 literal 0 HcmV?d00001