From 141ce8796056e3ee5645b75f5e519e90df770932 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 3 Nov 2015 19:27:31 +0300 Subject: [PATCH] Extracted ImageBrush/VisualBrush parts from Direct2D backend --- Perspex.sln | 5 + Shared/RenderHelpers/RenderHelpers.projitems | 14 ++ Shared/RenderHelpers/RenderHelpers.shproj | 13 ++ Shared/RenderHelpers/TileBrushImplHelper.cs | 174 ++++++++++++++++++ .../Perspex.Direct2D1/Media/DrawingContext.cs | 4 +- .../Perspex.Direct2D1/Media/ImageBrushImpl.cs | 94 ---------- .../Perspex.Direct2D1/Media/TileBrushImpl.cs | 92 ++------- .../Media/VisualBrushImpl.cs | 73 -------- .../Perspex.Direct2D1.csproj | 3 +- 9 files changed, 227 insertions(+), 245 deletions(-) create mode 100644 Shared/RenderHelpers/RenderHelpers.projitems create mode 100644 Shared/RenderHelpers/RenderHelpers.shproj create mode 100644 Shared/RenderHelpers/TileBrushImplHelper.cs delete mode 100644 src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs delete mode 100644 src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs diff --git a/Perspex.sln b/Perspex.sln index 9ddce7d51e..fdba885ee8 100644 --- a/Perspex.sln +++ b/Perspex.sln @@ -104,12 +104,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindingTest", "samples\Bind EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlTestApplicationPcl", "samples\XamlTestApplicationPcl\XamlTestApplicationPcl.csproj", "{EA113F1A-D8D7-4142-9948-353270E7EBAE}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RenderHelpers", "Shared\RenderHelpers\RenderHelpers.shproj", "{3C4C0CB4-0C0F-4450-A37B-148C84FF905F}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13 src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4 src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4 + Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4 + Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -289,5 +293,6 @@ Global {8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098} {EA113F1A-D8D7-4142-9948-353270E7EBAE} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {3C4C0CB4-0C0F-4450-A37B-148C84FF905F} = {A689DEF5-D50F-4975-8B72-124C9EB54066} EndGlobalSection EndGlobal diff --git a/Shared/RenderHelpers/RenderHelpers.projitems b/Shared/RenderHelpers/RenderHelpers.projitems new file mode 100644 index 0000000000..a34993265f --- /dev/null +++ b/Shared/RenderHelpers/RenderHelpers.projitems @@ -0,0 +1,14 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 3c4c0cb4-0c0f-4450-a37b-148c84ff905f + + + Perspex.RenderHelpers + + + + + \ No newline at end of file diff --git a/Shared/RenderHelpers/RenderHelpers.shproj b/Shared/RenderHelpers/RenderHelpers.shproj new file mode 100644 index 0000000000..00bfd089b9 --- /dev/null +++ b/Shared/RenderHelpers/RenderHelpers.shproj @@ -0,0 +1,13 @@ + + + + 3c4c0cb4-0c0f-4450-a37b-148c84ff905f + 14.0 + + + + + + + + diff --git a/Shared/RenderHelpers/TileBrushImplHelper.cs b/Shared/RenderHelpers/TileBrushImplHelper.cs new file mode 100644 index 0000000000..bf5d3e96bf --- /dev/null +++ b/Shared/RenderHelpers/TileBrushImplHelper.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Perspex; +using Perspex.Media; +using Perspex.Rendering; +using Perspex.Layout; + + +namespace Perspex.RenderHelpers +{ + class TileBrushImplHelper + { + public Size IntermediateSize { get; } + public Rect DestinationRect { get; } + private readonly TileMode _tileMode; + private readonly Rect _sourceRect; + private readonly Vector _scale; + private readonly Vector _translate; + private readonly Size _imageSize; + private readonly VisualBrush _visualBrush; + private readonly ImageBrush _imageBrush; + + public bool IsValid { get; } + + + public TileBrushImplHelper(TileBrush brush, Size targetSize) + { + _imageBrush = brush as ImageBrush; + _visualBrush = brush as VisualBrush; + if (_imageBrush != null) + { + if (_imageBrush.Source == null) + return; + _imageSize = new Size(_imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight); + IsValid = true; + } + else if (_visualBrush != null) + { + var visual = _visualBrush.Visual; + if (visual == null) + return; + var layoutable = visual as ILayoutable; + + if (layoutable?.IsArrangeValid == false) + { + layoutable.Measure(Size.Infinity); + layoutable.Arrange(new Rect(layoutable.DesiredSize)); + } + //I have no idea why are we using layoutable after `as` cast, but it was in original VisualBrush code by @grokys + _imageSize = layoutable.Bounds.Size; + IsValid = true; + } + else + return; + + _tileMode = brush.TileMode; + _sourceRect = brush.SourceRect.ToPixels(_imageSize); + DestinationRect = brush.DestinationRect.ToPixels(targetSize); + _scale = brush.Stretch.CalculateScaling(DestinationRect.Size, _sourceRect.Size); + _translate = CalculateTranslate(brush, _sourceRect, DestinationRect, _scale); + IntermediateSize = CalculateIntermediateSize(_tileMode, targetSize, DestinationRect.Size); + } + + public void DrawIntermediate(DrawingContext ctx) + { + Rect drawRect; + var transform = CalculateIntermediateTransform( + _tileMode, + _sourceRect, + DestinationRect, + _scale, + _translate, + out drawRect); + using (ctx.PushClip(drawRect)) + using (ctx.PushPostTransform(transform)) + { + if (_imageBrush != null) + { + var bmpRc = new Rect(0, 0, _imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight); + ctx.DrawImage(_imageBrush.Source, 1, bmpRc, bmpRc); + } + else if (_visualBrush != null) + { + ctx.FillRectangle(Brushes.Black, new Rect(new Point(0, 0), IntermediateSize)); + ctx.Render(_visualBrush.Visual); + } + } + } + + + /// + /// 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. + + public 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); + } + + + public static Matrix CalculateIntermediateTransform( + TileMode tileMode, + Rect sourceRect, + Rect destinationRect, + Vector scale, + Vector translate, + out Rect 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; + + return transform; + } + + + + + static Size CalculateIntermediateSize( + TileMode tileMode, + Size targetSize, + Size destinationSize) => tileMode == TileMode.None ? targetSize : destinationSize; + + } +} \ No newline at end of file diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index b10208d7e0..fc4418f682 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -312,11 +312,11 @@ namespace Perspex.Direct2D1.Media } else if (imageBrush != null) { - return new ImageBrushImpl(imageBrush, _renderTarget, destinationSize); + return new TileBrushImpl(imageBrush, _renderTarget, destinationSize); } else if (visualBrush != null) { - return new VisualBrushImpl(visualBrush, _renderTarget, destinationSize); + return new TileBrushImpl(visualBrush, _renderTarget, destinationSize); } else { diff --git a/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs deleted file mode 100644 index c518b6438f..0000000000 --- a/src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs +++ /dev/null @@ -1,94 +0,0 @@ -// 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, - SharpDX.Direct2D1.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)) - { - Rect drawRect; - var transform = CalculateIntermediateTransform( - tileMode, - sourceRect, - destinationRect, - scale, - translate, - out drawRect); - - intermediate.BeginDraw(); - intermediate.PushAxisAlignedClip(drawRect.ToDirect2D(), AntialiasMode.Aliased); - intermediate.Transform = transform.ToDirect2D(); - 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, - SharpDX.Direct2D1.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 index fd9b938b2d..0f0d929b9e 100644 --- a/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/TileBrushImpl.cs @@ -6,94 +6,38 @@ using Perspex.Layout; using Perspex.Media; using SharpDX; using SharpDX.Direct2D1; +using Perspex.RenderHelpers; + namespace Perspex.Direct2D1.Media { - public class TileBrushImpl : BrushImpl + public sealed 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( + public TileBrushImpl( TileBrush brush, - Rect sourceRect, - Rect destinationRect, - Vector scale) + SharpDX.Direct2D1.RenderTarget target, + Size targetSize) { - var x = 0.0; - var y = 0.0; - var size = sourceRect.Size * scale; + var helper = new TileBrushImplHelper(brush, targetSize); + if (!helper.IsValid) + return; - switch (brush.AlignmentX) + using (var intermediate = new BitmapRenderTarget(target, CompatibleRenderTargetOptions.None, helper.IntermediateSize.ToSharpDX())) { - case AlignmentX.Center: - x += (destinationRect.Width - size.Width) / 2; - break; - case AlignmentX.Right: - x += destinationRect.Width - size.Width; - break; + using (var ctx = new RenderTarget(intermediate).CreateDrawingContext()) + helper.DrawIntermediate(ctx); + + PlatformBrush = new BitmapBrush( + target, + intermediate.Bitmap, + GetBitmapBrushProperties(brush), + GetBrushProperties(brush, helper.DestinationRect)); } - - 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 Matrix CalculateIntermediateTransform( - TileMode tileMode, - Rect sourceRect, - Rect destinationRect, - Vector scale, - Vector translate, - out Rect 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; - - return transform; - } protected static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect) { - var tileMode = brush.TileMode; - return new BrushProperties { Opacity = (float)brush.Opacity, diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs deleted file mode 100644 index b3eaa81b50..0000000000 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ /dev/null @@ -1,73 +0,0 @@ -// 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 Perspex.Rendering; -using SharpDX; -using SharpDX.Direct2D1; - -namespace Perspex.Direct2D1.Media -{ - public class VisualBrushImpl : TileBrushImpl - { - public VisualBrushImpl( - VisualBrush brush, - SharpDX.Direct2D1.RenderTarget target, - Size targetSize) - { - var visual = brush.Visual; - - if (visual == null) - { - return; - } - - var layoutable = visual as ILayoutable; - - if (layoutable?.IsArrangeValid == false) - { - layoutable.Measure(Size.Infinity); - layoutable.Arrange(new Rect(layoutable.DesiredSize)); - } - - var tileMode = brush.TileMode; - var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); - 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)) - { - Rect drawRect; - var transform = CalculateIntermediateTransform( - tileMode, - sourceRect, - destinationRect, - scale, - translate, - out drawRect); - var renderer = new RenderTarget(intermediate); - - using (var ctx = renderer.CreateDrawingContext()) - using (ctx.PushClip(drawRect)) - using (ctx.PushPostTransform(transform)) - { - intermediate.Clear(new Color4(0)); - ctx.Render(visual); - } - - this.PlatformBrush = new BitmapBrush( - target, - intermediate.Bitmap, - GetBitmapBrushProperties(brush), - GetBrushProperties(brush, destinationRect)); - } - } - } -} diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 0f4ec68289..7d2091403c 100644 --- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -79,9 +79,7 @@ - - @@ -113,6 +111,7 @@ Perspex.SceneGraph +