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
+