diff --git a/src/Perspex.SceneGraph/Media/Brush.cs b/src/Perspex.SceneGraph/Media/Brush.cs
index f86a7d2560..26e5953621 100644
--- a/src/Perspex.SceneGraph/Media/Brush.cs
+++ b/src/Perspex.SceneGraph/Media/Brush.cs
@@ -9,7 +9,15 @@ namespace Perspex.Media
///
/// Describes how an area is painted.
///
- public abstract class Brush
+ public abstract class Brush : PerspexObject
{
+ public static readonly PerspexProperty OpacityProperty =
+ PerspexProperty.Register(nameof(Opacity), 1.0);
+
+ public double Opacity
+ {
+ get { return this.GetValue(OpacityProperty); }
+ set { this.SetValue(OpacityProperty, value); }
+ }
}
}
diff --git a/src/Perspex.SceneGraph/Media/BrushMappingMode.cs b/src/Perspex.SceneGraph/Media/BrushMappingMode.cs
new file mode 100644
index 0000000000..a453f81d85
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/BrushMappingMode.cs
@@ -0,0 +1,14 @@
+namespace Perspex.Media
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public enum BrushMappingMode
+ {
+ Absolute,
+ RelativeToBoundingBox
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/GradientBrush.cs b/src/Perspex.SceneGraph/Media/GradientBrush.cs
new file mode 100644
index 0000000000..5c63b66242
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/GradientBrush.cs
@@ -0,0 +1,38 @@
+namespace Perspex.Media
+{
+ using System.Collections.Generic;
+
+ public abstract class GradientBrush : Brush
+ {
+ public static readonly PerspexProperty MappingModeProperty =
+PerspexProperty.Register(nameof(MappingMode), BrushMappingMode.RelativeToBoundingBox);
+
+ public static readonly PerspexProperty SpreadMethodProperty =
+PerspexProperty.Register(nameof(SpreadMethod), GradientSpreadMethod.Pad);
+
+ public static readonly PerspexProperty> GradientStopsProperty =
+PerspexProperty.Register>(nameof(Opacity), new List());
+
+ public GradientBrush()
+ {
+ }
+
+ public BrushMappingMode MappingMode
+ {
+ get { return this.GetValue(MappingModeProperty); }
+ set { this.SetValue(MappingModeProperty, value); }
+ }
+
+ public GradientSpreadMethod SpreadMethod
+ {
+ get { return this.GetValue(SpreadMethodProperty); }
+ set { this.SetValue(SpreadMethodProperty, value); }
+ }
+
+ public List GradientStops
+ {
+ get { return this.GetValue(GradientStopsProperty); }
+ set { this.SetValue(GradientStopsProperty, value); }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/GradientSpreadMethod.cs b/src/Perspex.SceneGraph/Media/GradientSpreadMethod.cs
new file mode 100644
index 0000000000..78ba87b05a
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/GradientSpreadMethod.cs
@@ -0,0 +1,15 @@
+namespace Perspex.Media
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public enum GradientSpreadMethod
+ {
+ Pad,
+ Reflect,
+ Repeat
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/GradientStop.cs b/src/Perspex.SceneGraph/Media/GradientStop.cs
new file mode 100644
index 0000000000..9dbbe610ef
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/GradientStop.cs
@@ -0,0 +1,36 @@
+namespace Perspex.Media
+{
+ ///
+ /// GradientStop
+ ///
+ public sealed class GradientStop
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GradientStop() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The color
+ /// The offset
+ public GradientStop(Color color, double offset)
+ {
+ this.Color = color;
+ this.Offset = offset;
+ }
+
+ // TODO: Make these dependency properties.
+
+ ///
+ /// The offset
+ ///
+ public double Offset { get; set; }
+
+ ///
+ /// The color
+ ///
+ public Color Color { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Media/LinearGradientBrush.cs b/src/Perspex.SceneGraph/Media/LinearGradientBrush.cs
new file mode 100644
index 0000000000..3a63a39506
--- /dev/null
+++ b/src/Perspex.SceneGraph/Media/LinearGradientBrush.cs
@@ -0,0 +1,23 @@
+namespace Perspex.Media
+{
+ public class LinearGradientBrush : GradientBrush
+ {
+ public static readonly PerspexProperty StartPointProperty =
+PerspexProperty.Register(nameof(StartPoint), new Point(0,0));
+
+ public static readonly PerspexProperty EndPointProperty =
+PerspexProperty.Register(nameof(EndPoint), new Point(0, 0));
+
+ public Point StartPoint
+ {
+ get { return this.GetValue(StartPointProperty); }
+ set { this.SetValue(StartPointProperty, value); }
+ }
+
+ public Point EndPoint
+ {
+ get { return this.GetValue(EndPointProperty); }
+ set { this.SetValue(EndPointProperty, value); }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
index 3b5280b26a..e455b1940a 100644
--- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
+++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
@@ -59,8 +59,13 @@
+
+
+
+
+
diff --git a/src/Windows/Perspex.Direct2D1/Media/BrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/BrushImpl.cs
new file mode 100644
index 0000000000..e6114b3764
--- /dev/null
+++ b/src/Windows/Perspex.Direct2D1/Media/BrushImpl.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Perspex.Direct2D1.Media
+{
+ public abstract class BrushImpl : IDisposable
+ {
+ public SharpDX.Direct2D1.Brush PlatformBrush { get; set; }
+
+ public BrushImpl(Perspex.Media.Brush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+ {
+ }
+
+ public virtual void Dispose()
+ {
+ if (this.PlatformBrush != null)
+ this.PlatformBrush.Dispose();
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
index fce32ace29..c7ae3950dd 100644
--- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
+++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
@@ -89,13 +89,15 @@ namespace Perspex.Direct2D1.Media
{
if (pen != null)
{
- using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
+ 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(
p1.ToSharpDX(),
p2.ToSharpDX(),
- d2dBrush,
+ d2dBrush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
@@ -112,20 +114,20 @@ namespace Perspex.Direct2D1.Media
{
if (brush != null)
{
- using (var d2dBrush = brush.ToDirect2D(this.renderTarget))
+ using (var d2dBrush = this.CreateBrush(brush, geometry.Bounds.Size))
{
GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
- this.renderTarget.FillGeometry(impl.Geometry, d2dBrush);
+ this.renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
}
}
if (pen != null)
{
- using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
+ using (var d2dBrush = this.CreateBrush(pen.Brush, geometry.GetRenderBounds(pen.Thickness).Size))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
{
GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
- this.renderTarget.DrawGeometry(impl.Geometry, d2dBrush, (float)pen.Thickness, d2dStroke);
+ this.renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
}
}
}
@@ -138,12 +140,12 @@ 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, rect.Size))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
{
this.renderTarget.DrawRoundedRectangle(
new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
- brush,
+ brush.PlatformBrush,
(float)pen.Thickness,
d2dStroke);
}
@@ -161,7 +163,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, impl.Measure()))
+ using (var renderer = new PerspexTextRenderer(this, this.renderTarget, brush.PlatformBrush))
{
impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y);
}
@@ -176,7 +179,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, rect.Size))
{
this.renderTarget.FillRoundedRectangle(
new RoundedRectangle
@@ -189,7 +192,7 @@ namespace Perspex.Direct2D1.Media
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
- b);
+ b.PlatformBrush);
}
}
@@ -256,5 +259,24 @@ namespace Perspex.Direct2D1.Media
this.renderTarget.Transform = transform * m3x2;
});
}
+
+ 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;
+
+ if (solidColorBrush != null)
+ {
+ return new SolidColorBrushImpl(solidColorBrush, this.renderTarget, destinationSize);
+ }
+ else if (linearGradientBrush != null)
+ {
+ return new LinearGradientBrushImpl(linearGradientBrush, this.renderTarget, destinationSize);
+ }
+ else
+ {
+ return new SolidColorBrushImpl(null, this.renderTarget, destinationSize);
+ }
+ }
}
}
diff --git a/src/Windows/Perspex.Direct2D1/Media/LinearGradientBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/LinearGradientBrushImpl.cs
new file mode 100644
index 0000000000..81242c9e0b
--- /dev/null
+++ b/src/Windows/Perspex.Direct2D1/Media/LinearGradientBrushImpl.cs
@@ -0,0 +1,43 @@
+namespace Perspex.Direct2D1.Media
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public class LinearGradientBrushImpl : BrushImpl
+ {
+ public LinearGradientBrushImpl(Perspex.Media.LinearGradientBrush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+ : base(brush, target, destinationSize)
+ {
+ if (brush != null)
+ {
+ var gradientStops = brush.GradientStops.Select(s => new SharpDX.Direct2D1.GradientStop { Color = s.Color.ToDirect2D(), Position = (float)s.Offset }).ToArray();
+
+ Point startPoint = new Point(0, 0);
+ Point endPoint = new Point(0, 0);
+
+ switch (brush.MappingMode)
+ {
+ case Perspex.Media.BrushMappingMode.Absolute:
+ // TODO:
+
+ break;
+ case Perspex.Media.BrushMappingMode.RelativeToBoundingBox:
+ startPoint = new Point(brush.StartPoint.X * destinationSize.Width, brush.StartPoint.Y * destinationSize.Height);
+ endPoint = new Point(brush.EndPoint.X * destinationSize.Width, brush.EndPoint.Y * destinationSize.Height);
+
+ break;
+ }
+
+ this.PlatformBrush = new SharpDX.Direct2D1.LinearGradientBrush(
+ target,
+ new SharpDX.Direct2D1.LinearGradientBrushProperties { StartPoint = startPoint.ToSharpDX(), EndPoint = endPoint.ToSharpDX() },
+ new SharpDX.Direct2D1.BrushProperties { Opacity = (float)brush.Opacity, Transform = target.Transform },
+ new SharpDX.Direct2D1.GradientStopCollection(target, gradientStops, brush.SpreadMethod.ToDirect2D())
+ );
+ }
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs b/src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs
index 3e6888fb64..7723d535d6 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(
@@ -46,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 :
- wrapper.Brush.ToDirect2D(this.renderTarget);
+ this.context.CreateBrush(wrapper.Brush, new Size()).PlatformBrush;
this.renderTarget.DrawGlyphRun(
new Vector2(baselineOriginX, baselineOriginY),
@@ -94,4 +99,4 @@ namespace Perspex.Direct2D1.Media
return false;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
new file mode 100644
index 0000000000..949b8e72be
--- /dev/null
+++ b/src/Windows/Perspex.Direct2D1/Media/SolidColorBrushImpl.cs
@@ -0,0 +1,17 @@
+namespace Perspex.Direct2D1.Media
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public class SolidColorBrushImpl : BrushImpl
+ {
+ public SolidColorBrushImpl(Perspex.Media.SolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target, Size destinationSize)
+ : base(brush, target, destinationSize)
+ {
+ this.PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush(target, brush?.Color.ToDirect2D() ?? new SharpDX.Color4());
+ }
+ }
+}
diff --git a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
index 73ce0904a4..c7a62078e8 100644
--- a/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
+++ b/src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
@@ -75,11 +75,14 @@
+
+
+
diff --git a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs
index ef44de77db..18df70c82c 100644
--- a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs
+++ b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs
@@ -34,6 +34,15 @@ namespace Perspex.Direct2D1
return new Size2F((float)p.Width, (float)p.Height);
}
+ public static SharpDX.Direct2D1.ExtendMode ToDirect2D(this Perspex.Media.GradientSpreadMethod spreadMethod)
+ {
+ if (spreadMethod == Perspex.Media.GradientSpreadMethod.Pad)
+ return ExtendMode.Clamp;
+ else if (spreadMethod == Perspex.Media.GradientSpreadMethod.Reflect)
+ return ExtendMode.Mirror;
+ else
+ return ExtendMode.Wrap;
+ }
///
/// Converts a brush to Direct2D.
///