diff --git a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs index 5fc767ef57..c01d1f1ac3 100644 --- a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs @@ -2,13 +2,14 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using System.Reactive.Disposables; using Perspex.Cairo.Media.Imaging; using Perspex.Media; -using IBitmap = Perspex.Media.Imaging.IBitmap; namespace Perspex.Cairo.Media { + using Perspex.Media.Imaging; using Cairo = global::Cairo; /// @@ -77,9 +78,9 @@ namespace Perspex.Cairo.Media public void DrawLine(Pen pen, Point p1, Point p2) { var size = new Rect(p1, p2).Size; + + SetPen(pen, size); - SetBrush(pen.Brush, size); - _context.LineWidth = pen.Thickness; _context.MoveTo(p1.ToCairo()); _context.LineTo(p2.ToCairo()); _context.Stroke(); @@ -139,9 +140,9 @@ namespace Perspex.Cairo.Media public void DrawText(Brush foreground, Point origin, FormattedText text) { var layout = ((FormattedTextImpl)text.PlatformImpl).Layout; - SetBrush(foreground, new Size(0, 0)); - _context.MoveTo(origin.X, origin.Y); + + SetBrush(foreground, new Size(0, 0)); Pango.CairoHelper.ShowLayout(_context, layout); } @@ -195,7 +196,7 @@ namespace Perspex.Cairo.Media _context.Transform(matrix.Invert().ToCairo()); }); } - + private void SetBrush(Brush brush, Size destinationSize) { var solid = brush as SolidColorBrush; @@ -225,7 +226,23 @@ namespace Perspex.Cairo.Media private void SetPen(Pen pen, Size destinationSize) { SetBrush(pen.Brush, destinationSize); + if (pen.DashStyle != null) + { + if (pen.DashStyle.Dashes != null && pen.DashStyle.Dashes.Count > 0) + { + var cray = pen.DashStyle.Dashes.ToArray(); + _context.SetDash(cray, pen.DashStyle.Offset); + } + } + _context.LineWidth = pen.Thickness; + _context.MiterLimit = pen.MiterLimit; + + // Line caps and joins are currently broken on Cairo. I've defaulted them to sensible defaults for now. + // Cairo does not have StartLineCap, EndLineCap, and DashCap properties, whereas Direct2D does. + // TODO: Figure out a solution for this. + _context.LineJoin = Cairo.LineJoin.Miter; + _context.LineCap = Cairo.LineCap.Butt; } } } diff --git a/src/Perspex.Controls/Shapes/Shape.cs b/src/Perspex.Controls/Shapes/Shape.cs index 0ce5b08096..5f5b989602 100644 --- a/src/Perspex.Controls/Shapes/Shape.cs +++ b/src/Perspex.Controls/Shapes/Shape.cs @@ -96,7 +96,7 @@ namespace Perspex.Controls.Shapes if (geometry != null) { - var pen = new Pen(Stroke, StrokeThickness, StrokeDashArray); + var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray)); context.DrawGeometry(Fill, pen, geometry); } } diff --git a/src/Perspex.SceneGraph/Media/DashStyle.cs b/src/Perspex.SceneGraph/Media/DashStyle.cs new file mode 100644 index 0000000000..8ec4d67e32 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/DashStyle.cs @@ -0,0 +1,84 @@ +namespace Perspex.Media +{ + using Perspex.Animation; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + public class DashStyle : Animatable + { + private static DashStyle dash; + public static DashStyle Dash + { + get + { + if (dashDotDot == null) + { + dash = new DashStyle(new double[] { 2, 2 }, 1); + } + + return dash; + } + } + + + + private static DashStyle dot; + public static DashStyle Dot + { + get + { + if (dot == null) + { + dot = new DashStyle(new double[] { 0, 2 }, 0); + } + + return dot; + } + } + + private static DashStyle dashDot; + public static DashStyle DashDot + { + get + { + if (dashDot == null) + { + dashDot = new DashStyle(new double[] { 2, 2, 0, 2 }, 1); + } + + return dashDot; + } + } + + private static DashStyle dashDotDot; + public static DashStyle DashDotDot + { + get + { + if (dashDotDot == null) + { + dashDotDot = new DashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1); + } + + return dashDotDot; + } + } + + + public DashStyle(IReadOnlyList dashes = null, double offset = 0.0) + { + this.Dashes = dashes; + this.Offset = offset; + } + + /// + /// Gets and sets the length of alternating dashes and gaps. + /// + public IReadOnlyList Dashes { get; } + + public double Offset { get; } + } +} diff --git a/src/Perspex.SceneGraph/Media/Pen.cs b/src/Perspex.SceneGraph/Media/Pen.cs index 2d200c8a59..b813392ccd 100644 --- a/src/Perspex.SceneGraph/Media/Pen.cs +++ b/src/Perspex.SceneGraph/Media/Pen.cs @@ -15,12 +15,20 @@ namespace Perspex.Media /// /// The brush used to draw. /// The stroke thickness. - /// The length of alternating dashes and gaps. - public Pen(Brush brush, double thickness, IReadOnlyList dashArray = null) + public Pen( + Brush brush, + double thickness = 1.0, + DashStyle dashStyle = null, PenLineCap dashCap = PenLineCap.Flat, PenLineCap startLineCap = PenLineCap.Flat, + PenLineCap endLineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) { Brush = brush; Thickness = thickness; - DashArray = dashArray; + StartLineCap = startLineCap; + EndLineCap = endLineCap; + LineJoin = lineJoin; + MiterLimit = miterLimit; + DashStyle = dashStyle; + DashCap = dashCap; } /// @@ -28,12 +36,20 @@ namespace Perspex.Media /// /// The stroke color. /// The stroke thickness. - /// The length of alternating dashes and gaps. - public Pen(uint color, double thickness, IReadOnlyList dashArray = null) + public Pen( + uint color, + double thickness = 1.0, + DashStyle dashStyle = null, PenLineCap dashCap = PenLineCap.Flat, PenLineCap startLineCap = PenLineCap.Flat, + PenLineCap endLineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) { Brush = new SolidColorBrush(color); Thickness = thickness; - DashArray = dashArray; + StartLineCap = startLineCap; + EndLineCap = endLineCap; + LineJoin = lineJoin; + MiterLimit = miterLimit; + DashStyle = dashStyle; + DashCap = dashCap; } /// @@ -41,14 +57,21 @@ namespace Perspex.Media /// public Brush Brush { get; } - /// - /// Gets the length of alternating dashes and gaps. - /// - public IReadOnlyList DashArray { get; } - /// /// Gets the stroke thickness. /// - public double Thickness { get; } + public double Thickness { get; } = 1.0; + + public DashStyle DashStyle { get; } + + public PenLineCap DashCap { get; } + + public PenLineCap StartLineCap { get; } = PenLineCap.Flat; + + public PenLineCap EndLineCap { get; } = PenLineCap.Flat; + + public PenLineJoin LineJoin { get; } = PenLineJoin.Miter; + + public double MiterLimit { get; } = 10.0; } } diff --git a/src/Perspex.SceneGraph/Media/PenLineCap.cs b/src/Perspex.SceneGraph/Media/PenLineCap.cs new file mode 100644 index 0000000000..4e1a360e0d --- /dev/null +++ b/src/Perspex.SceneGraph/Media/PenLineCap.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Media +{ + public enum PenLineCap + { + Flat, + Round, + Square, + Triangle + } +} diff --git a/src/Perspex.SceneGraph/Media/PenLineJoin.cs b/src/Perspex.SceneGraph/Media/PenLineJoin.cs new file mode 100644 index 0000000000..9d135fe63f --- /dev/null +++ b/src/Perspex.SceneGraph/Media/PenLineJoin.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Media +{ + public enum PenLineJoin + { + Bevel, + Miter, + Round + } +} diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 0f75fdb7e1..8841e9a7de 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -63,11 +63,14 @@ + + + diff --git a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs index 04dd597f38..2364521ed0 100644 --- a/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs @@ -40,6 +40,29 @@ namespace Perspex.Direct2D1 else return ExtendMode.Wrap; } + + public static SharpDX.Direct2D1.LineJoin ToDirect2D(this Perspex.Media.PenLineJoin lineJoin) + { + if (lineJoin == Perspex.Media.PenLineJoin.Round) + return LineJoin.Round; + else if (lineJoin == Perspex.Media.PenLineJoin.Miter) + return LineJoin.Miter; + else + return LineJoin.Bevel; + } + + public static SharpDX.Direct2D1.CapStyle ToDirect2D(this Perspex.Media.PenLineCap lineCap) + { + if (lineCap == Perspex.Media.PenLineCap.Flat) + return CapStyle.Flat; + else if (lineCap == Perspex.Media.PenLineCap.Round) + return CapStyle.Round; + else if (lineCap == Perspex.Media.PenLineCap.Square) + return CapStyle.Square; + else + return CapStyle.Triangle; + } + /// /// Converts a pen to a Direct2D stroke style. /// @@ -48,19 +71,26 @@ namespace Perspex.Direct2D1 /// The Direct2D brush. public static StrokeStyle ToDirect2DStrokeStyle(this Perspex.Media.Pen pen, RenderTarget target) { - if (pen.DashArray != null && pen.DashArray.Count > 0) + if (pen.DashStyle != null) { - var properties = new StrokeStyleProperties + if (pen.DashStyle.Dashes != null && pen.DashStyle.Dashes.Count > 0) { - DashStyle = DashStyle.Custom, - }; + var properties = new StrokeStyleProperties + { + DashStyle = DashStyle.Custom, + DashOffset = (float)pen.DashStyle.Offset, + MiterLimit = (float)pen.MiterLimit, + LineJoin = pen.LineJoin.ToDirect2D(), + StartCap = pen.StartLineCap.ToDirect2D(), + EndCap = pen.EndLineCap.ToDirect2D(), + DashCap = pen.DashCap.ToDirect2D() + }; - return new StrokeStyle(target.Factory, properties, pen.DashArray.Select(x => (float)x).ToArray()); - } - else - { - return null; + return new StrokeStyle(target.Factory, properties, pen.DashStyle?.Dashes.Select(x => (float)x).ToArray()); + } } + + return null; } ///