diff --git a/samples/ControlCatalog/Pages/CanvasPage.paml b/samples/ControlCatalog/Pages/CanvasPage.paml index 4a3f682d17..8aaa8176bd 100644 --- a/samples/ControlCatalog/Pages/CanvasPage.paml +++ b/samples/ControlCatalog/Pages/CanvasPage.paml @@ -2,12 +2,13 @@ Canvas A panel which lays out its children by explicit coordinates - - + - + + + + - \ No newline at end of file diff --git a/samples/TestApplicationShared/MainWindow.cs b/samples/TestApplicationShared/MainWindow.cs index 0a6ebeae2c..30490f774b 100644 --- a/samples/TestApplicationShared/MainWindow.cs +++ b/samples/TestApplicationShared/MainWindow.cs @@ -543,6 +543,17 @@ namespace TestApplication private static TabItem LayoutTab() { + var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3), + new Point(9, 1), new Point(10, 0), new Point(15, 0) }; + var polygonPoints = new Point[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) }; + for (int i = 0; i < polylinePoints.Length; i++) + { + polylinePoints[i] = polylinePoints[i] * 13; + } + for (int i = 0; i < polygonPoints.Length; i++) + { + polygonPoints[i] = polygonPoints[i] * 15; + } return new TabItem { Header = "Layout", @@ -691,13 +702,36 @@ namespace TestApplication }, new Line { - Width = 90, - Height = 70, Stroke = Brushes.Red, StrokeThickness = 2, + StartPoint = new Point(120, 185), + EndPoint = new Point(30, 115) + }, + new Perspex.Controls.Shapes.Path + { + Fill = Brushes.Orange, + Data = StreamGeometry.Parse("M 30,250 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z"), + }, + new Polygon + { + Stroke = Brushes.DarkBlue, + Fill = Brushes.Violet, + Points = polygonPoints, + StrokeThickness = 1, + [Canvas.LeftProperty] = 150, + [Canvas.TopProperty] = 180, + }, + new Polyline + { + Stroke = Brushes.Brown, + Points = polylinePoints, + StrokeThickness = 5, + StrokeJoin = PenLineJoin.Round, + StrokeStartLineCap = PenLineCap.Triangle, + StrokeEndLineCap = PenLineCap.Triangle, [Canvas.LeftProperty] = 30, - [Canvas.TopProperty] = 120 - } + [Canvas.TopProperty] = 350, + }, } }, } diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs b/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs index e42df57d59..45e3e7285d 100644 --- a/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs +++ b/src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs @@ -21,6 +21,7 @@ using Perspex.Media.Imaging; using Perspex.Metadata; using Perspex.Platform; using Perspex.Styling; +using System.Collections.Generic; namespace Perspex.Markup.Xaml.Context { @@ -101,6 +102,7 @@ namespace Perspex.Markup.Xaml.Context new TypeConverterRegistration(typeof(PerspexList), new PerspexListTypeConverter()), new TypeConverterRegistration(typeof(IMemberSelector), new MemberSelectorTypeConverter()), new TypeConverterRegistration(typeof(Point), new PointTypeConverter()), + new TypeConverterRegistration(typeof(IList), new PointsListTypeConverter()), new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyTypeConverter()), new TypeConverterRegistration(typeof(RelativePoint), new RelativePointTypeConverter()), new TypeConverterRegistration(typeof(RelativeRect), new RelativeRectTypeConverter()), diff --git a/src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs b/src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs new file mode 100644 index 0000000000..cff7443eff --- /dev/null +++ b/src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs @@ -0,0 +1,37 @@ +using OmniXaml.TypeConversion; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Perspex.Markup.Xaml.Converters +{ + public class PointsListTypeConverter : ITypeConverter + { + public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType) + { + return false; + } + + public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value) + { + string strValue = (string)value; + string[] pointStrs = strValue.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + var result = new List(pointStrs.Length); + foreach (var pointStr in pointStrs) + { + result.Add(Point.Parse(pointStr, culture)); + } + return result; + } + + public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj index d9cd4c6f63..74cf64f3ec 100644 --- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj +++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj @@ -41,6 +41,7 @@ + diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj index cf44ab2997..1c2d72190e 100644 --- a/src/Perspex.Controls/Perspex.Controls.csproj +++ b/src/Perspex.Controls/Perspex.Controls.csproj @@ -68,6 +68,8 @@ + + diff --git a/src/Perspex.Controls/Shapes/Line.cs b/src/Perspex.Controls/Shapes/Line.cs index 785ea7bbd1..0d659b126f 100644 --- a/src/Perspex.Controls/Shapes/Line.cs +++ b/src/Perspex.Controls/Shapes/Line.cs @@ -9,28 +9,46 @@ namespace Perspex.Controls.Shapes { public class Line : Shape { - private Geometry _geometry; + public static readonly PerspexProperty StartPointProperty = + PerspexProperty.Register("StartPoint"); - private Size _geometrySize; + public static readonly PerspexProperty EndPointProperty = + PerspexProperty.Register("EndPoint"); + + private LineGeometry _geometry; + private Point _startPoint; + private Point _endPoint; + + static Line() + { + StrokeThicknessProperty.OverrideDefaultValue(1); + } + + public Point StartPoint + { + get { return GetValue(StartPointProperty); } + set { SetValue(StartPointProperty, value); } + } + + public Point EndPoint + { + get { return GetValue(EndPointProperty); } + set { SetValue(EndPointProperty, value); } + } public override Geometry DefiningGeometry { get { - if (_geometry == null || _geometrySize != Bounds.Size) + if (_geometry == null || StartPoint != _startPoint || EndPoint != _endPoint) { - var rect = new Rect(Bounds.Size).Deflate(StrokeThickness); - _geometry = new LineGeometry(rect.TopLeft, rect.BottomRight); - _geometrySize = Bounds.Size; + _startPoint = StartPoint; + _endPoint = EndPoint; + _geometry = new LineGeometry(_startPoint, _endPoint); } return _geometry; } } - - protected override Size MeasureOverride(Size availableSize) - { - return new Size(StrokeThickness, StrokeThickness); - } } } diff --git a/src/Perspex.Controls/Shapes/Polygon.cs b/src/Perspex.Controls/Shapes/Polygon.cs new file mode 100644 index 0000000000..9536d710a5 --- /dev/null +++ b/src/Perspex.Controls/Shapes/Polygon.cs @@ -0,0 +1,23 @@ +using Perspex.Media; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls.Shapes +{ + public class Polygon : Shape + { + public static readonly PerspexProperty> PointsProperty = + PerspexProperty.Register>("Points"); + + public IList Points + { + get { return GetValue(PointsProperty); } + set { SetValue(PointsProperty, value); } + } + + public override Geometry DefiningGeometry => new PolylineGeometry(Points, true); + } +} diff --git a/src/Perspex.Controls/Shapes/Polyline.cs b/src/Perspex.Controls/Shapes/Polyline.cs new file mode 100644 index 0000000000..75b1353f6f --- /dev/null +++ b/src/Perspex.Controls/Shapes/Polyline.cs @@ -0,0 +1,28 @@ +using Perspex.Media; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls.Shapes +{ + public class Polyline: Shape + { + public static readonly PerspexProperty> PointsProperty = + PerspexProperty.Register>("Points"); + + static Polyline() + { + StrokeThicknessProperty.OverrideDefaultValue(1); + } + + public IList Points + { + get { return GetValue(PointsProperty); } + set { SetValue(PointsProperty, value); } + } + + public override Geometry DefiningGeometry => new PolylineGeometry(Points, false); + } +} diff --git a/src/Perspex.Controls/Shapes/Shape.cs b/src/Perspex.Controls/Shapes/Shape.cs index 6e7934b0e7..eb07be6a78 100644 --- a/src/Perspex.Controls/Shapes/Shape.cs +++ b/src/Perspex.Controls/Shapes/Shape.cs @@ -96,6 +96,8 @@ namespace Perspex.Controls.Shapes public PenLineCap StrokeEndLineCap { get; set; } = PenLineCap.Flat; + public PenLineJoin StrokeJoin { get; set; } = PenLineJoin.Miter; + public override void Render(DrawingContext context) { var geometry = RenderedGeometry; @@ -103,7 +105,7 @@ namespace Perspex.Controls.Shapes if (geometry != null) { var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray), - StrokeDashCap, StrokeStartLineCap, StrokeEndLineCap); + StrokeDashCap, StrokeStartLineCap, StrokeEndLineCap, StrokeJoin); context.DrawGeometry(Fill, pen, geometry); } } diff --git a/src/Perspex.SceneGraph/Media/LineGeometry.cs b/src/Perspex.SceneGraph/Media/LineGeometry.cs index 196fdc40eb..d9f24422b1 100644 --- a/src/Perspex.SceneGraph/Media/LineGeometry.cs +++ b/src/Perspex.SceneGraph/Media/LineGeometry.cs @@ -27,8 +27,8 @@ namespace Perspex.Media using (IStreamGeometryContextImpl context = impl.Open()) { - context.BeginFigure(startPoint, false); - context.LineTo(endPoint); + context.BeginFigure(_startPoint, false); + context.LineTo(_endPoint); context.EndFigure(false); } @@ -36,12 +36,40 @@ namespace Perspex.Media } /// - public override Rect Bounds => new Rect(_startPoint, _endPoint); + public override Rect Bounds + { + get + { + double xMin, yMin, xMax, yMax; + if (_startPoint.X <= _endPoint.X) + { + xMin = _startPoint.X; + xMax = _endPoint.X; + } + else + { + xMin = _endPoint.X; + xMax = _startPoint.X; + } + if (_startPoint.Y <= _endPoint.Y) + { + yMin = _startPoint.Y; + yMax = _endPoint.Y; + } + else + { + yMin = _endPoint.Y; + yMax = _startPoint.Y; + } + + return new Rect(xMin, yMin, xMax - xMin, yMax - yMin); + } + } /// public override Geometry Clone() { - return new LineGeometry(Bounds.TopLeft, Bounds.BottomRight); + return new LineGeometry(_startPoint, _endPoint); } } } diff --git a/src/Perspex.SceneGraph/Media/PathMarkupParser.cs b/src/Perspex.SceneGraph/Media/PathMarkupParser.cs index 17539c924b..4d08d4ecf6 100644 --- a/src/Perspex.SceneGraph/Media/PathMarkupParser.cs +++ b/src/Perspex.SceneGraph/Media/PathMarkupParser.cs @@ -147,6 +147,16 @@ namespace Perspex.Media _context.CubicBezierTo(point1, point2, point); break; } + + case Command.CubicBezierCurveRelative: + { + Point point1 = ReadRelativePoint(reader, point); + Point point2 = ReadRelativePoint(reader, point); + _context.CubicBezierTo(point, point1, point2); + point = point2; + break; + } + case Command.Arc: { //example: A10,10 0 0,0 10,20 @@ -216,8 +226,10 @@ namespace Perspex.Media } } - private static double ReadDouble(TextReader reader) + private static double ReadDouble(StringReader reader) { + ReadWhitespace(reader); + // TODO: Handle Infinity, NaN and scientific notation. StringBuilder b = new StringBuilder(); bool readSign = false; diff --git a/src/Perspex.SceneGraph/Media/PolylineGeometry.cs b/src/Perspex.SceneGraph/Media/PolylineGeometry.cs new file mode 100644 index 0000000000..0b431d6ae2 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/PolylineGeometry.cs @@ -0,0 +1,79 @@ +using Perspex.Platform; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Media +{ + /// + /// Represents the geometry of an polyline or polygon. + /// + public class PolylineGeometry : Geometry + { + private IList _points; + private bool _isFilled; + + public PolylineGeometry(IList points, bool isFilled) + { + _points = points; + _isFilled = isFilled; + IPlatformRenderInterface factory = PerspexLocator.Current.GetService(); + IStreamGeometryImpl impl = factory.CreateStreamGeometry(); + + using (IStreamGeometryContextImpl context = impl.Open()) + { + if (points.Count > 0) + { + context.BeginFigure(points[0], isFilled); + for (int i = 1; i < points.Count; i++) + { + context.LineTo(points[i]); + } + context.EndFigure(isFilled); + } + } + + PlatformImpl = impl; + } + + /// + public override Rect Bounds + { + get + { + double xMin = double.MaxValue, yMin = double.MaxValue; + double xMax = double.MinValue, yMax = double.MinValue; + foreach (var point in _points) + { + if (point.X < xMin) + { + xMin = point.X; + } + else if (point.X > xMax) + { + xMax = point.X; + } + + if (point.Y < yMin) + { + yMin = point.Y; + } + else if (point.Y > yMax) + { + yMax = point.Y; + } + } + + return new Rect(xMin, yMin, xMax - xMin, yMax - yMin); + } + } + + /// + public override Geometry Clone() + { + return new PolylineGeometry(new List(_points), _isFilled); + } + } +} diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 05f22c4ccf..6578b9c7b5 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -72,11 +72,12 @@ + + - diff --git a/src/Skia/Perspex.Skia/DrawingContextImpl.cs b/src/Skia/Perspex.Skia/DrawingContextImpl.cs index cce05bd1ae..8e7d481433 100644 --- a/src/Skia/Perspex.Skia/DrawingContextImpl.cs +++ b/src/Skia/Perspex.Skia/DrawingContextImpl.cs @@ -51,7 +51,6 @@ namespace Perspex.Skia var rv = NativeBrushPool.Instance.Get(); rv.Brush->Opacity = brush.Opacity; - var solid = brush as SolidColorBrush; if (solid != null) { @@ -103,8 +102,6 @@ namespace Perspex.Skia rv.Brush->Bitmap = bitmap.Handle; rv.Brush->BitmapTileMode = tileBrush.TileMode; rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y); - - } return rv; @@ -116,6 +113,7 @@ namespace Perspex.Skia brush.Brush->Stroke = true; brush.Brush->StrokeThickness = (float)pen.Thickness; brush.Brush->StrokeLineCap = pen.StartLineCap; + brush.Brush->StrokeLineJoin = pen.LineJoin; brush.Brush->StrokeMiterLimit = (float)pen.MiterLimit; if (pen.DashStyle?.Dashes != null) diff --git a/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj index dd6953d355..18d7aeffcf 100644 --- a/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj @@ -74,6 +74,8 @@ + + diff --git a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj index 2abf92fdb5..a922029365 100644 --- a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -79,6 +79,8 @@ + + diff --git a/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj index 08bba41fce..e82de9eff2 100644 --- a/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj @@ -70,6 +70,8 @@ + + diff --git a/tests/Perspex.RenderTests/Shapes/LineTests.cs b/tests/Perspex.RenderTests/Shapes/LineTests.cs index 80eb7c75ca..6cd40db424 100644 --- a/tests/Perspex.RenderTests/Shapes/LineTests.cs +++ b/tests/Perspex.RenderTests/Shapes/LineTests.cs @@ -26,13 +26,54 @@ namespace Perspex.Direct2D1.RenderTests.Shapes { Decorator target = new Decorator { - Padding = new Thickness(8), Width = 200, Height = 200, Child = new Line { Stroke = Brushes.Black, StrokeThickness = 1, + StartPoint = new Point(0, 0), + EndPoint = new Point(200, 200) + } + }; + + RenderToFile(target); + CompareImages(); + } + + [Fact] + public void Line_1px_Stroke_Reversed() + { + Decorator target = new Decorator + { + Width = 200, + Height = 200, + Child = new Line + { + Stroke = Brushes.Black, + StrokeThickness = 1, + StartPoint = new Point(200, 0), + EndPoint = new Point(0, 200) + } + }; + + RenderToFile(target); + CompareImages(); + } + + [Fact] + public void Line_1px_Stroke_Vertical() + { + Decorator target = new Decorator + { + Width = 200, + Height = 200, + Child = new Line + { + Stroke = Brushes.Black, + StrokeThickness = 1, + StartPoint = new Point(100, 200), + EndPoint = new Point(100, 0) } }; diff --git a/tests/Perspex.RenderTests/Shapes/PolygonTests.cs b/tests/Perspex.RenderTests/Shapes/PolygonTests.cs new file mode 100644 index 0000000000..94f6bf584a --- /dev/null +++ b/tests/Perspex.RenderTests/Shapes/PolygonTests.cs @@ -0,0 +1,76 @@ +// 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 Perspex.Controls; +using Perspex.Controls.Shapes; +using Perspex.Media; +using Xunit; + +#if PERSPEX_CAIRO +namespace Perspex.Cairo.RenderTests.Shapes +#elif PERSPEX_SKIA +namespace Perspex.Skia.RenderTests +#else +namespace Perspex.Direct2D1.RenderTests.Shapes +#endif +{ + public class PolygonTests : TestBase + { + public PolygonTests() + : base(@"Shapes\Polygon") + { + } + +#if PERSPEX_CAIRO + [Fact(Skip = "Caused by cairo bug")] +#else + [Fact] +#endif + public void Polygon_1px_Stroke() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Polygon + { + Stroke = Brushes.DarkBlue, + Stretch = Stretch.Uniform, + Fill = Brushes.Violet, + Points = new [] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) }, + StrokeThickness = 1 + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "Caused by cairo bug")] +#else + [Fact] +#endif + public void Polygon_NonUniformFill() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 400, + Height = 200, + Child = new Polygon + { + Stroke = Brushes.DarkBlue, + Stretch = Stretch.Fill, + Fill = Brushes.Violet, + Points = new[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) }, + StrokeThickness = 5, + } + }; + + RenderToFile(target); + CompareImages(); + } + } +} diff --git a/tests/Perspex.RenderTests/Shapes/PolylineTests.cs b/tests/Perspex.RenderTests/Shapes/PolylineTests.cs new file mode 100644 index 0000000000..ca91be8e4b --- /dev/null +++ b/tests/Perspex.RenderTests/Shapes/PolylineTests.cs @@ -0,0 +1,83 @@ +// 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 Perspex.Controls; +using Perspex.Controls.Shapes; +using Perspex.Media; +using Xunit; + +#if PERSPEX_CAIRO +namespace Perspex.Cairo.RenderTests.Shapes +#elif PERSPEX_SKIA +namespace Perspex.Skia.RenderTests +#else +namespace Perspex.Direct2D1.RenderTests.Shapes +#endif +{ + public class PolylineTests : TestBase + { + public PolylineTests() + : base(@"Shapes\Polyline") + { + } + +#if PERSPEX_CAIRO + [Fact(Skip = "Caused by cairo bug")] +#else + [Fact] +#endif + public void Polyline_1px_Stroke() + { + var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3), + new Point(9, 1), new Point(10, 0), new Point(15, 0) }; + + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 400, + Height = 200, + Child = new Polyline + { + Stroke = Brushes.Brown, + Points = polylinePoints, + Stretch = Stretch.Uniform, + StrokeThickness = 1 + } + }; + + RenderToFile(target); + CompareImages(); + } + +#if PERSPEX_CAIRO + [Fact(Skip = "Caused by cairo bug")] +#else + [Fact] +#endif + public void Polyline_10px_Stroke_PenLineJoin() + { + var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3), + new Point(9, 1), new Point(10, 0), new Point(15, 0) }; + + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 400, + Height = 200, + Child = new Polyline + { + Stroke = Brushes.Brown, + Points = polylinePoints, + Stretch = Stretch.Uniform, + StrokeJoin = PenLineJoin.Round, + StrokeStartLineCap = PenLineCap.Round, + StrokeEndLineCap = PenLineCap.Round, + StrokeThickness = 10 + } + }; + + RenderToFile(target); + CompareImages(); + } + } +} diff --git a/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png index 18ade2da0f..f0a6a9b109 100644 Binary files a/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png and b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png new file mode 100644 index 0000000000..9ed7e713a9 Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png new file mode 100644 index 0000000000..845961351c Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..f62be2d4bf Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png new file mode 100644 index 0000000000..0888c74c30 Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png new file mode 100644 index 0000000000..d908a572cb Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..7cbe87fa6c Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png index 18ade2da0f..f0a6a9b109 100644 Binary files a/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png and b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png new file mode 100644 index 0000000000..9ed7e713a9 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png new file mode 100644 index 0000000000..845961351c Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..f62be2d4bf Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png new file mode 100644 index 0000000000..0888c74c30 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png new file mode 100644 index 0000000000..d908a572cb Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..544d9e2320 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png index 18ade2da0f..f0a6a9b109 100644 Binary files a/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png and b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png new file mode 100644 index 0000000000..9ed7e713a9 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png new file mode 100644 index 0000000000..845961351c Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..f62be2d4bf Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png new file mode 100644 index 0000000000..0888c74c30 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png new file mode 100644 index 0000000000..d908a572cb Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..544d9e2320 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ