diff --git a/Tests/Perspex.RenderTests/Media/ImageBrushTests.cs b/Tests/Perspex.RenderTests/Media/ImageBrushTests.cs new file mode 100644 index 0000000000..21a3047529 --- /dev/null +++ b/Tests/Perspex.RenderTests/Media/ImageBrushTests.cs @@ -0,0 +1,395 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Perspex.Controls; +using Perspex.Controls.Shapes; +using Perspex.Layout; +using Perspex.Media; +using Perspex.Media.Imaging; +using Xunit; + +#if PERSPEX_CAIRO +namespace Perspex.Cairo.RenderTests.Media +#else +namespace Perspex.Direct2D1.RenderTests.Media +#endif +{ + public class ImageBrushTests : TestBase + { + public ImageBrushTests() + : base(@"Media\ImageBrush") + { + } + + private string BitmapPath + { + get { return System.IO.Path.Combine(OutputPath, "github_icon.png"); } + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_Alignment_TopLeft() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + AlignmentX = AlignmentX.Left, + AlignmentY = AlignmentY.Top, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_Alignment_Center() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + AlignmentX = AlignmentX.Center, + AlignmentY = AlignmentY.Center, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_Alignment_BottomRight() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + AlignmentX = AlignmentX.Right, + AlignmentY = AlignmentY.Bottom, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_Fill_NoTile() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 920, + Height = 920, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.Fill, + TileMode = TileMode.None, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_Uniform_NoTile() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 300, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.Uniform, + TileMode = TileMode.None, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_UniformToFill_NoTile() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 300, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.UniformToFill, + TileMode = TileMode.None, + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_BottomRightQuarterSource() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + SourceRect = new RelativeRect(250, 250, 250, 250, RelativeUnit.Absolute), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_BottomRightQuarterDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + DestinationRect = new RelativeRect(92, 92, 92, 92, RelativeUnit.Absolute), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.None, + SourceRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative), + DestinationRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.Tile, + SourceRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative), + DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, RelativeUnit.Relative), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_FlipX_TopLeftDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipX, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, RelativeUnit.Relative), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_FlipY_TopLeftDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipY, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, RelativeUnit.Relative), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "ImageBrush not yet implemented on Cairo")] +#else + [Fact] +#endif + public void ImageBrush_NoStretch_FlipXY_TopLeftDest() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Rectangle + { + Fill = new ImageBrush + { + Stretch = Stretch.None, + TileMode = TileMode.FlipXY, + DestinationRect = new RelativeRect(0, 0, 0.5, 0.5, RelativeUnit.Relative), + Source = new Bitmap(BitmapPath), + } + } + }; + + RenderToFile(target); + CompareImages(); + } + } +} diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png new file mode 100644 index 0000000000..73d147bf77 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Fill_NoTile.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipXY_TopLeftDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipXY_TopLeftDest.expected.png new file mode 100644 index 0000000000..f99f56ec14 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipXY_TopLeftDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipX_TopLeftDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipX_TopLeftDest.expected.png new file mode 100644 index 0000000000..4d89f995c4 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipX_TopLeftDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipY_TopLeftDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipY_TopLeftDest.expected.png new file mode 100644 index 0000000000..75c53f0c28 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_FlipY_TopLeftDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_BottomRight.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_BottomRight.expected.png new file mode 100644 index 0000000000..a13b34ba2d Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_BottomRight.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_Center.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_Center.expected.png new file mode 100644 index 0000000000..14704f751c Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_Center.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_TopLeft.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_TopLeft.expected.png new file mode 100644 index 0000000000..7ebac13d30 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_Alignment_TopLeft.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png new file mode 100644 index 0000000000..dd41cb98c3 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource.expected.png new file mode 100644 index 0000000000..ab9f2b76e3 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest.expected.png new file mode 100644 index 0000000000..fea4a2a8e9 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest.expected.png new file mode 100644 index 0000000000..afd39c2360 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png new file mode 100644 index 0000000000..e173fa6cee Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_UniformToFill_NoTile.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png new file mode 100644 index 0000000000..de73af8170 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/ImageBrush_Uniform_NoTile.expected.png differ diff --git a/Tests/TestFiles/Direct2D1/Media/ImageBrush/github_icon.png b/Tests/TestFiles/Direct2D1/Media/ImageBrush/github_icon.png new file mode 100644 index 0000000000..cd053c5fe1 Binary files /dev/null and b/Tests/TestFiles/Direct2D1/Media/ImageBrush/github_icon.png differ diff --git a/src/Perspex.SceneGraph/Media/ImageBush.cs b/src/Perspex.SceneGraph/Media/ImageBush.cs new file mode 100644 index 0000000000..1b3fd07d28 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/ImageBush.cs @@ -0,0 +1,44 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Perspex.Media.Imaging; + +namespace Perspex.Media +{ + /// + /// Paints an area with an . + /// + public class ImageBrush : TileBrush + { + /// + /// Defines the property. + /// + public static readonly PerspexProperty SourceProperty = + PerspexProperty.Register("Source"); + + /// + /// Initializes a new instance of the class. + /// + public ImageBrush() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to draw. + public ImageBrush(IBitmap source) + { + Source = source; + } + + /// + /// Gets or sets the image to draw. + /// + public IBitmap Source + { + get { return GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + } +} diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 660cbe38d7..fac8cf2924 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -64,11 +64,11 @@ - + @@ -97,6 +97,7 @@ + diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 53f5ff3d5c..d0c2e4c254 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -303,6 +303,7 @@ namespace Perspex.Direct2D1.Media { var solidColorBrush = brush as Perspex.Media.SolidColorBrush; var linearGradientBrush = brush as Perspex.Media.LinearGradientBrush; + var imageBrush = brush as ImageBrush; var visualBrush = brush as VisualBrush; if (solidColorBrush != null) @@ -313,6 +314,10 @@ namespace Perspex.Direct2D1.Media { return new LinearGradientBrushImpl(linearGradientBrush, _renderTarget, destinationSize); } + else if (imageBrush != null) + { + return new ImageBrushImpl(imageBrush, _renderTarget, destinationSize); + } else if (visualBrush != null) { return new VisualBrushImpl(visualBrush, _renderTarget, destinationSize); diff --git a/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs new file mode 100644 index 0000000000..c1d4c0e0b2 --- /dev/null +++ b/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs @@ -0,0 +1,93 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Perspex.Media; +using SharpDX.Direct2D1; + +namespace Perspex.Direct2D1.Media +{ + public class ImageBrushImpl : TileBrushImpl + { + public ImageBrushImpl( + ImageBrush brush, + RenderTarget target, + Size targetSize) + { + if (brush.Source == null) + { + return; + } + + var image = ((BitmapImpl)brush.Source.PlatformImpl).GetDirect2DBitmap(target); + var imageSize = new Size(brush.Source.PixelWidth, brush.Source.PixelHeight); + var tileMode = brush.TileMode; + var sourceRect = brush.SourceRect.ToPixels(imageSize); + var destinationRect = brush.DestinationRect.ToPixels(targetSize); + var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); + var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); + var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); + var brtOpts = CompatibleRenderTargetOptions.None; + + // TODO: There are times where we don't need to draw an intermediate bitmap. Identify + // them and directly use 'image' in those cases. + using (var intermediate = new BitmapRenderTarget(target, brtOpts, intermediateSize)) + { + SharpDX.RectangleF drawRect; + + intermediate.BeginDraw(); + intermediate.Transform = CalculateIntermediateTransform( + tileMode, + sourceRect, + destinationRect, + scale, + translate, + out drawRect); + intermediate.PushAxisAlignedClip(drawRect, AntialiasMode.Aliased); + intermediate.DrawBitmap(image, 1, BitmapInterpolationMode.Linear); + intermediate.PopAxisAlignedClip(); + intermediate.EndDraw(); + + this.PlatformBrush = new BitmapBrush( + target, + intermediate.Bitmap, + GetBitmapBrushProperties(brush), + GetBrushProperties(brush, destinationRect)); + } + } + + private BitmapBrush CreateDirectBrush( + ImageBrush brush, + RenderTarget target, + Bitmap image, + Rect sourceRect, + Rect destinationRect) + { + var tileMode = brush.TileMode; + var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); + var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); + var transform = Matrix.CreateTranslation(-sourceRect.Position) * + Matrix.CreateScale(scale) * + Matrix.CreateTranslation(translate); + + var opts = new BrushProperties + { + Transform = transform.ToDirect2D(), + Opacity = (float)brush.Opacity, + }; + + var bitmapOpts = new BitmapBrushProperties + { + ExtendModeX = GetExtendModeX(tileMode), + ExtendModeY = GetExtendModeY(tileMode), + }; + + return new BitmapBrush(target, image, bitmapOpts, opts); + } + + private BitmapBrush CreateIndirectBrush() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs new file mode 100644 index 0000000000..edd31ae0b6 --- /dev/null +++ b/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs @@ -0,0 +1,135 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Perspex.Layout; +using Perspex.Media; +using SharpDX; +using SharpDX.Direct2D1; + +namespace Perspex.Direct2D1.Media +{ + public class TileBrushImpl : BrushImpl + { + /// + /// Calculates a translate based on a , a source and destination + /// rectangle and a scale. + /// + /// The brush. + /// The source rectangle. + /// The destination rectangle. + /// The scale factor. + /// A vector with the X and Y translate. + protected static Vector CalculateTranslate( + TileBrush brush, + Rect sourceRect, + Rect destinationRect, + Vector scale) + { + var x = 0.0; + var y = 0.0; + 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; + } + + return new Vector(x, y); + } + + protected static Size2F CalculateIntermediateSize( + TileMode tileMode, + Size targetSize, + Size destinationSize) + { + var result = tileMode == TileMode.None ? targetSize : destinationSize; + return result.ToSharpDX(); + } + + protected static Matrix3x2 CalculateIntermediateTransform( + TileMode tileMode, + Rect sourceRect, + Rect destinationRect, + Vector scale, + Vector translate, + out SharpDX.RectangleF drawRect) + { + var transform = Matrix.CreateTranslation(-sourceRect.Position) * + Matrix.CreateScale(scale) * + Matrix.CreateTranslation(translate); + Rect dr; + + if (tileMode == TileMode.None) + { + dr = destinationRect; + transform *= Matrix.CreateTranslation(destinationRect.Position); + } + else + { + dr = new Rect(destinationRect.Size); + } + + drawRect = (dr * -transform).ToSharpDX(); + + return transform.ToDirect2D(); + } + + protected static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect) + { + var tileMode = brush.TileMode; + + return new BrushProperties + { + Opacity = (float)brush.Opacity, + Transform = brush.TileMode != TileMode.None ? + SharpDX.Matrix3x2.Translation( + (float)destinationRect.X, + (float)destinationRect.Y) : + SharpDX.Matrix3x2.Identity, + }; + } + + protected static BitmapBrushProperties GetBitmapBrushProperties(TileBrush brush) + { + var tileMode = brush.TileMode; + + return new BitmapBrushProperties + { + ExtendModeX = GetExtendModeX(tileMode), + ExtendModeY = GetExtendModeY(tileMode), + }; + } + + protected static ExtendMode GetExtendModeX(TileMode tileMode) + { + return (tileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; + } + + protected static ExtendMode GetExtendModeY(TileMode tileMode) + { + return (tileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; + } + + public override void Dispose() + { + ((BitmapBrush)PlatformBrush)?.Bitmap.Dispose(); + base.Dispose(); + } + } +} diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index f7e12187f6..2e4622b5c0 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -8,7 +8,7 @@ using SharpDX.Direct2D1; namespace Perspex.Direct2D1.Media { - public class VisualBrushImpl : BrushImpl + public class VisualBrushImpl : TileBrushImpl { public VisualBrushImpl( VisualBrush brush, @@ -59,8 +59,8 @@ namespace Perspex.Direct2D1.Media renderer.Render(visual, null, transform, drawRect); var result = new BitmapBrush(brt, brt.Bitmap); - result.ExtendModeX = (brush.TileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; - result.ExtendModeY = (brush.TileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap; + result.ExtendModeX = GetExtendModeX(brush.TileMode); + result.ExtendModeY = GetExtendModeY(brush.TileMode); if (brush.TileMode != TileMode.None) { @@ -72,44 +72,5 @@ namespace Perspex.Direct2D1.Media PlatformBrush = result; } } - - private static Vector CalculateTranslate( - VisualBrush brush, - Rect sourceRect, - Rect destinationRect, - Vector scale) - { - var x = 0.0; - var y = 0.0; - 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; - } - - return new Vector(x, y); - } - - public override void Dispose() - { - ((BitmapBrush)PlatformBrush)?.Bitmap.Dispose(); - base.Dispose(); - } } } diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 922f67391b..0fa382e99d 100644 --- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -82,6 +82,8 @@ + + diff --git a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj index 6a8b105dc5..7956643dca 100644 --- a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -75,6 +75,7 @@ + diff --git a/tests/Perspex.RenderTests/TestBase.cs b/tests/Perspex.RenderTests/TestBase.cs index 36a34610a2..46083fc5b3 100644 Binary files a/tests/Perspex.RenderTests/TestBase.cs and b/tests/Perspex.RenderTests/TestBase.cs differ