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();
+ }
+ }
+}