Browse Source

Merge pull request #398 from KvanTTT/master

Added Polygon, Polyline. Line fixed.
Nikita Tsukanov 9 years ago
parent
commit
e60fef22c6
42 changed files with 502 additions and 30 deletions
  1. 5 4
      samples/ControlCatalog/Pages/CanvasPage.paml
  2. 38 4
      samples/TestApplicationShared/MainWindow.cs
  3. 2 0
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  4. 37 0
      src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs
  5. 1 0
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  6. 2 0
      src/Perspex.Controls/Perspex.Controls.csproj
  7. 29 11
      src/Perspex.Controls/Shapes/Line.cs
  8. 23 0
      src/Perspex.Controls/Shapes/Polygon.cs
  9. 28 0
      src/Perspex.Controls/Shapes/Polyline.cs
  10. 3 1
      src/Perspex.Controls/Shapes/Shape.cs
  11. 32 4
      src/Perspex.SceneGraph/Media/LineGeometry.cs
  12. 13 1
      src/Perspex.SceneGraph/Media/PathMarkupParser.cs
  13. 79 0
      src/Perspex.SceneGraph/Media/PolylineGeometry.cs
  14. 2 1
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  15. 1 3
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  16. 2 0
      tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj
  17. 2 0
      tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj
  18. 2 0
      tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj
  19. 42 1
      tests/Perspex.RenderTests/Shapes/LineTests.cs
  20. 76 0
      tests/Perspex.RenderTests/Shapes/PolygonTests.cs
  21. 83 0
      tests/Perspex.RenderTests/Shapes/PolylineTests.cs
  22. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png
  23. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  24. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  25. BIN
      tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  26. BIN
      tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  27. BIN
      tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  28. BIN
      tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png
  29. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png
  30. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  31. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  32. BIN
      tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  33. BIN
      tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  34. BIN
      tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  35. BIN
      tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png
  36. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png
  37. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  38. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  39. BIN
      tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  40. BIN
      tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  41. BIN
      tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  42. BIN
      tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png

+ 5 - 4
samples/ControlCatalog/Pages/CanvasPage.paml

@@ -2,12 +2,13 @@
   <StackPanel Orientation="Vertical" Gap="4">
     <TextBlock Classes="h1">Canvas</TextBlock>
     <TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>
-
-    <Canvas Background="Yellow" Width="300" Height="200">
+    <Canvas Background="Yellow" Width="300" Height="400">
       <Rectangle Fill="Blue" Width="63" Height="41" Canvas.Left="40" Canvas.Top="31"/>
       <Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="160" Canvas.Top="79"/>
-      <Path Fill="Red" Data="M50,0 L0,50 100,50 Z" Canvas.Left="50" Canvas.Top="140"/>
+      <Path Fill="Orange" Data="M 0,0 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
+      <Line StartPoint="120,185" EndPoint="30,115" Stroke="Red" StrokeThickness="2"/>
+      <Polygon Points="75,0 120,120 0,45 150,45 30,120" Stroke="DarkBlue" StrokeThickness="1" Fill="Violet" Canvas.Left="150" Canvas.Top="180"/>
+      <Polyline Points="0,0 65,0 78,-26 91,39 104,-39 117,13 130,0 195,0" Stroke="Brown" Canvas.Left="30" Canvas.Top="350"/>
     </Canvas>
-    
   </StackPanel>
 </UserControl>

+ 38 - 4
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,
+                                    },
                                 }
                             },
                         }

+ 2 - 0
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<double>), new PerspexListTypeConverter<double>()),
                 new TypeConverterRegistration(typeof(IMemberSelector), new MemberSelectorTypeConverter()),
                 new TypeConverterRegistration(typeof(Point), new PointTypeConverter()),
+                new TypeConverterRegistration(typeof(IList<Point>), new PointsListTypeConverter()),
                 new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyTypeConverter()),
                 new TypeConverterRegistration(typeof(RelativePoint), new RelativePointTypeConverter()),
                 new TypeConverterRegistration(typeof(RelativeRect), new RelativeRectTypeConverter()),

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

+ 1 - 0
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@@ -41,6 +41,7 @@
     <Compile Include="Context\NameScopeWrapper.cs" />
     <Compile Include="Converters\CursorTypeConverter.cs" />
     <Compile Include="Converters\PerspexListTypeConverter.cs" />
+    <Compile Include="Converters\PointsListTypeConverter.cs" />
     <Compile Include="Converters\RelativeRectTypeConverter.cs" />
     <Compile Include="Data\MultiBinding.cs" />
     <Compile Include="Data\RelativeSource.cs" />

+ 2 - 0
src/Perspex.Controls/Perspex.Controls.csproj

@@ -68,6 +68,8 @@
     <Compile Include="SelectionMode.cs" />
     <Compile Include="Separator.cs" />
     <Compile Include="Shapes\Line.cs" />
+    <Compile Include="Shapes\Polygon.cs" />
+    <Compile Include="Shapes\Polyline.cs" />
     <Compile Include="SystemDialog.cs" />
     <Compile Include="Generators\ITreeItemContainerGenerator.cs" />
     <Compile Include="Generators\ItemContainerEventArgs.cs" />

+ 29 - 11
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<Point> StartPointProperty =
+            PerspexProperty.Register<Line, Point>("StartPoint");
 
-        private Size _geometrySize;
+        public static readonly PerspexProperty<Point> EndPointProperty =
+            PerspexProperty.Register<Line, Point>("EndPoint");
+
+        private LineGeometry _geometry;
+        private Point _startPoint;
+        private Point _endPoint;
+
+        static Line()
+        {
+            StrokeThicknessProperty.OverrideDefaultValue<Line>(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);
-        }
     }
 }

+ 23 - 0
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<IList<Point>> PointsProperty =
+            PerspexProperty.Register<Polygon, IList<Point>>("Points");
+
+        public IList<Point> Points
+        {
+            get { return GetValue(PointsProperty); }
+            set { SetValue(PointsProperty, value); }
+        }
+
+        public override Geometry DefiningGeometry => new PolylineGeometry(Points, true);
+    }
+}

+ 28 - 0
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<IList<Point>> PointsProperty =
+            PerspexProperty.Register<Polyline, IList<Point>>("Points");
+
+        static Polyline()
+        {
+            StrokeThicknessProperty.OverrideDefaultValue<Polyline>(1);
+        }
+
+        public IList<Point> Points
+        {
+            get { return GetValue(PointsProperty); }
+            set { SetValue(PointsProperty, value); }
+        }
+
+        public override Geometry DefiningGeometry => new PolylineGeometry(Points, false);
+    }
+}

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

+ 32 - 4
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
         }
 
         /// <inheritdoc/>
-        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);
+            }
+        }
 
         /// <inheritdoc/>
         public override Geometry Clone()
         {
-            return new LineGeometry(Bounds.TopLeft, Bounds.BottomRight);
+            return new LineGeometry(_startPoint, _endPoint);
         }
     }
 }

+ 13 - 1
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;

+ 79 - 0
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
+{
+    /// <summary>
+    /// Represents the geometry of an polyline or polygon.
+    /// </summary>
+    public class PolylineGeometry : Geometry
+    {
+        private IList<Point> _points;
+        private bool _isFilled;
+
+        public PolylineGeometry(IList<Point> points, bool isFilled)
+        {
+            _points = points;
+            _isFilled = isFilled;
+            IPlatformRenderInterface factory = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
+            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;
+        }
+
+        /// <inheritdoc/>
+        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);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override Geometry Clone()
+        {
+            return new PolylineGeometry(new List<Point>(_points), _isFilled);
+        }
+    }
+}

+ 2 - 1
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@@ -72,11 +72,12 @@
     <Compile Include="Media\GradientSpreadMethod.cs" />
     <Compile Include="Media\GradientStop.cs" />
     <Compile Include="Media\LineGeometry.cs" />
+    <Compile Include="Media\PenLineJoin.cs" />
+    <Compile Include="Media\PolylineGeometry.cs" />
     <Compile Include="Media\RadialGradientBrush.cs" />
     <Compile Include="Media\LinearGradientBrush.cs" />
     <Compile Include="Media\MediaExtensions.cs" />
     <Compile Include="Media\PenLineCap.cs" />
-    <Compile Include="Media\PenLineJoin.cs" />
     <Compile Include="Media\TextAlignment.cs" />
     <Compile Include="Media\FontWeight.cs" />
     <Compile Include="Media\FontStyle.cs" />

+ 1 - 3
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)

+ 2 - 0
tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj

@@ -74,6 +74,8 @@
     <Compile Include="Shapes\LineTests.cs" />
     <Compile Include="Shapes\PathTests.cs" />
     <Compile Include="Shapes\EllipseTests.cs" />
+    <Compile Include="Shapes\PolygonTests.cs" />
+    <Compile Include="Shapes\PolylineTests.cs" />
     <Compile Include="Shapes\RectangleTests.cs" />
     <Compile Include="TestBase.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 2 - 0
tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj

@@ -79,6 +79,8 @@
     <Compile Include="Shapes\LineTests.cs" />
     <Compile Include="Shapes\PathTests.cs" />
     <Compile Include="Shapes\EllipseTests.cs" />
+    <Compile Include="Shapes\PolygonTests.cs" />
+    <Compile Include="Shapes\PolylineTests.cs" />
     <Compile Include="Shapes\RectangleTests.cs" />
     <Compile Include="TestBase.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 2 - 0
tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj

@@ -70,6 +70,8 @@
     <Compile Include="Shapes\EllipseTests.cs" />
     <Compile Include="Shapes\LineTests.cs" />
     <Compile Include="Shapes\PathTests.cs" />
+    <Compile Include="Shapes\PolygonTests.cs" />
+    <Compile Include="Shapes\PolylineTests.cs" />
     <Compile Include="Shapes\RectangleTests.cs" />
     <Compile Include="TestBase.cs" />
   </ItemGroup>

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

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

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

BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png


BIN
tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png


BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png


BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png


BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png


BIN
tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png


BIN
tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png


BIN
tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png


BIN
tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png