瀏覽代碼

Remove unsupported line caps

Benedikt Schroeder 6 年之前
父節點
當前提交
4413de463e

+ 42 - 30
src/Avalonia.Controls/Shapes/Shape.cs

@@ -21,13 +21,19 @@ namespace Avalonia.Controls.Shapes
 
         public static readonly StyledProperty<AvaloniaList<double>> StrokeDashArrayProperty =
             AvaloniaProperty.Register<Shape, AvaloniaList<double>>(nameof(StrokeDashArray));
-            
+
         public static readonly StyledProperty<double> StrokeDashOffsetProperty =
             AvaloniaProperty.Register<Shape, double>(nameof(StrokeDashOffset));
 
         public static readonly StyledProperty<double> StrokeThicknessProperty =
             AvaloniaProperty.Register<Shape, double>(nameof(StrokeThickness));
 
+        public static readonly StyledProperty<PenLineCap> StrokeLineCapProperty =
+            AvaloniaProperty.Register<Shape, PenLineCap>(nameof(StrokeLineCap), PenLineCap.Flat);
+
+        public static readonly StyledProperty<PenLineJoin> StrokeJoinProperty =
+            AvaloniaProperty.Register<Shape, PenLineJoin>(nameof(StrokeJoin), PenLineJoin.Miter);
+
         private Matrix _transform = Matrix.Identity;
         private Geometry _definingGeometry;
         private Geometry _renderedGeometry;
@@ -36,7 +42,9 @@ namespace Avalonia.Controls.Shapes
         static Shape()
         {
             AffectsMeasure<Shape>(StretchProperty, StrokeThicknessProperty);
-            AffectsRender<Shape>(FillProperty, StrokeProperty, StrokeDashArrayProperty);
+
+            AffectsRender<Shape>(FillProperty, StrokeProperty, StrokeDashArrayProperty, StrokeDashOffsetProperty,
+                StrokeThicknessProperty, StrokeLineCapProperty, StrokeJoinProperty);
         }
 
         public Geometry DefiningGeometry
@@ -106,7 +114,7 @@ namespace Avalonia.Controls.Shapes
             get { return GetValue(StrokeDashArrayProperty); }
             set { SetValue(StrokeDashArrayProperty, value); }
         }
-        
+
         public double StrokeDashOffset
         {
             get { return GetValue(StrokeDashOffsetProperty); }
@@ -119,13 +127,17 @@ namespace Avalonia.Controls.Shapes
             set { SetValue(StrokeThicknessProperty, value); }
         }
 
-        public PenLineCap StrokeDashCap { get; set; } = PenLineCap.Flat;
-
-        public PenLineCap StrokeStartLineCap { get; set; } = PenLineCap.Flat;
-
-        public PenLineCap StrokeEndLineCap { get; set; } = PenLineCap.Flat;
+        public PenLineCap StrokeLineCap
+        {
+            get { return GetValue(StrokeLineCapProperty); }
+            set { SetValue(StrokeLineCapProperty, value); }
+        }
 
-        public PenLineJoin StrokeJoin { get; set; } = PenLineJoin.Miter;
+        public PenLineJoin StrokeJoin
+        {
+            get { return GetValue(StrokeJoinProperty); }
+            set { SetValue(StrokeJoinProperty, value); }
+        }
 
         public override void Render(DrawingContext context)
         {
@@ -133,8 +145,8 @@ namespace Avalonia.Controls.Shapes
 
             if (geometry != null)
             {
-                var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray, StrokeDashOffset), 
-                    StrokeDashCap, StrokeStartLineCap, StrokeEndLineCap, StrokeJoin);
+                var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray, StrokeDashOffset),
+                     StrokeLineCap, StrokeJoin);
                 context.DrawGeometry(Fill, pen, geometry);
             }
         }
@@ -169,11 +181,11 @@ namespace Avalonia.Controls.Shapes
 
         protected void InvalidateGeometry()
         {
-            this._renderedGeometry = null;
-            this._definingGeometry = null;
+            _renderedGeometry = null;
+            _definingGeometry = null;
             InvalidateMeasure();
         }
-        
+
         protected override Size MeasureOverride(Size availableSize)
         {
             bool deferCalculateTransform;
@@ -203,10 +215,10 @@ namespace Avalonia.Controls.Shapes
                 return CalculateShapeSizeAndSetTransform(availableSize);
             }
         }
-        
+
         protected override Size ArrangeOverride(Size finalSize)
         {
-            if(_calculateTransformOnArrange)
+            if (_calculateTransformOnArrange)
             {
                 _calculateTransformOnArrange = false;
                 CalculateShapeSizeAndSetTransform(finalSize);
@@ -312,25 +324,25 @@ namespace Avalonia.Controls.Shapes
 
         private static void AffectsGeometryInvalidate(AvaloniaPropertyChangedEventArgs e)
         {
-            var control = e.Sender as Shape;
+            if (!(e.Sender is Shape control))
+            {
+                return;
+            }
 
-            if (control != null)
+            // If the geometry is invalidated when Bounds changes, only invalidate when the Size
+            // portion changes.
+            if (e.Property == BoundsProperty)
             {
-                // If the geometry is invalidated when Bounds changes, only invalidate when the Size
-                // portion changes.
-                if (e.Property == BoundsProperty)
-                {
-                    var oldBounds = (Rect)e.OldValue;
-                    var newBounds = (Rect)e.NewValue;
+                var oldBounds = (Rect)e.OldValue;
+                var newBounds = (Rect)e.NewValue;
 
-                    if (oldBounds.Size == newBounds.Size)
-                    {
-                        return;
-                    }
+                if (oldBounds.Size == newBounds.Size)
+                {
+                    return;
                 }
-
-                control.InvalidateGeometry();
             }
+
+            control.InvalidateGeometry();
         }
     }
 }

+ 4 - 6
src/Avalonia.Visuals/Media/BrushExtensions.cs

@@ -34,16 +34,14 @@ namespace Avalonia.Media
         {
             Contract.Requires<ArgumentNullException>(pen != null);
 
-            var brush = pen?.Brush?.ToImmutable();
-            return pen == null || ReferenceEquals(pen?.Brush, brush) ?
+            var brush = pen.Brush?.ToImmutable();
+            return ReferenceEquals(pen.Brush, brush) ?
                 pen :
                 new Pen(
                     brush,
                     thickness: pen.Thickness,
-                    dashStyle: pen.DashStyle,
-                    dashCap: pen.DashCap,
-                    startLineCap: pen.StartLineCap,
-                    endLineCap: pen.EndLineCap,
+                    dashStyle: pen.DashStyle,                   
+                    lineCap: pen.LineCap,
                     lineJoin: pen.LineJoin,
                     miterLimit: pen.MiterLimit);
         }

+ 31 - 41
src/Avalonia.Visuals/Media/Pen.cs

@@ -11,63 +11,45 @@ namespace Avalonia.Media
         /// <summary>
         /// Initializes a new instance of the <see cref="Pen"/> class.
         /// </summary>
-        /// <param name="brush">The brush used to draw.</param>
+        /// <param name="color">The stroke color.</param>
         /// <param name="thickness">The stroke thickness.</param>
         /// <param name="dashStyle">The dash style.</param>
-        /// <param name="dashCap">The dash cap.</param>
-        /// <param name="startLineCap">The start line cap.</param>
-        /// <param name="endLineCap">The end line cap.</param>
+        /// <param name="lineCap">Specifies the type of graphic shape to use on both ends of a line.</param>
         /// <param name="lineJoin">The line join.</param>
         /// <param name="miterLimit">The miter limit.</param>
         public Pen(
-            IBrush brush, 
+            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)
+            DashStyle dashStyle = null,
+            PenLineCap lineCap = PenLineCap.Flat,
+            PenLineJoin lineJoin = PenLineJoin.Miter,
+            double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit)
         {
-            Brush = brush;
-            Thickness = thickness;
-            DashCap = dashCap;
-            StartLineCap = startLineCap;
-            EndLineCap = endLineCap;
-            LineJoin = lineJoin;
-            MiterLimit = miterLimit;
-            DashStyle = dashStyle;
         }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="Pen"/> class.
         /// </summary>
-        /// <param name="color">The stroke color.</param>
+        /// <param name="brush">The brush used to draw.</param>
         /// <param name="thickness">The stroke thickness.</param>
         /// <param name="dashStyle">The dash style.</param>
-        /// <param name="dashCap">The dash cap.</param>
-        /// <param name="startLineCap">The start line cap.</param>
-        /// <param name="endLineCap">The end line cap.</param>
+        /// <param name="lineCap">The line cap.</param>
         /// <param name="lineJoin">The line join.</param>
         /// <param name="miterLimit">The miter limit.</param>
         public Pen(
-            uint color, 
+            IBrush brush,
             double thickness = 1.0,
-            DashStyle dashStyle = null, 
-            PenLineCap dashCap = PenLineCap.Flat, 
-            PenLineCap startLineCap = PenLineCap.Flat,
-            PenLineCap endLineCap = PenLineCap.Flat, 
-            PenLineJoin lineJoin = PenLineJoin.Miter, 
+            DashStyle dashStyle = null,
+            PenLineCap lineCap = PenLineCap.Flat,
+            PenLineJoin lineJoin = PenLineJoin.Miter,
             double miterLimit = 10.0)
         {
-            Brush = new SolidColorBrush(color);
+            Brush = brush;
             Thickness = thickness;
-            StartLineCap = startLineCap;
-            EndLineCap = endLineCap;
+            LineCap = lineCap;
             LineJoin = lineJoin;
             MiterLimit = miterLimit;
             DashStyle = dashStyle;
-            DashCap = dashCap;
         }
 
         /// <summary>
@@ -78,18 +60,26 @@ namespace Avalonia.Media
         /// <summary>
         /// Gets the stroke thickness.
         /// </summary>
-        public double Thickness { get; } = 1.0;
+        public double Thickness { get; }
 
+        /// <summary>
+        /// Specifies the style of dashed lines drawn with a <see cref="Pen"/> object.
+        /// </summary>
         public DashStyle DashStyle { get; }
 
-        public PenLineCap DashCap { get; }
-
-        public PenLineCap StartLineCap { get; } = PenLineCap.Flat;
-
-        public PenLineCap EndLineCap { get; } = PenLineCap.Flat;
+        /// <summary>
+        /// Specifies the type of graphic shape to use on both ends of a line.
+        /// </summary>
+        public PenLineCap LineCap { get; }
 
-        public PenLineJoin LineJoin { get; } = PenLineJoin.Miter;
+        /// <summary>
+        /// Specifies how to join consecutive line or curve segments in a <see cref="PathFigure"/> (subpath) contained in a <see cref="PathGeometry"/> object.
+        /// </summary>
+        public PenLineJoin LineJoin { get; }
 
-        public double MiterLimit { get; } = 10.0;
+        /// <summary>
+        /// The limit on the ratio of the miter length to half this pen's Thickness.
+        /// </summary>
+        public double MiterLimit { get; }
     }
 }

+ 1 - 2
src/Avalonia.Visuals/Media/PenLineCap.cs

@@ -4,7 +4,6 @@ namespace Avalonia.Media
     {
         Flat,
         Round,
-        Square,
-        Triangle
+        Square
     }
 }

+ 5 - 14
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -573,25 +573,17 @@ namespace Avalonia.Skia
             // Need to modify dashes due to Skia modifying their lengths
             // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/paths/dots
             // TODO: Still something is off, dashes are now present, but don't look the same as D2D ones.
-            float dashLengthModifier;
-            float gapLengthModifier;
 
-            switch (pen.StartLineCap)
+            switch (pen.LineCap)
             {
                 case PenLineCap.Round:
                     paint.StrokeCap = SKStrokeCap.Round;
-                    dashLengthModifier = -paint.StrokeWidth;
-                    gapLengthModifier = paint.StrokeWidth;
                     break;
                 case PenLineCap.Square:
                     paint.StrokeCap = SKStrokeCap.Square;
-                    dashLengthModifier = -paint.StrokeWidth;
-                    gapLengthModifier = paint.StrokeWidth;
                     break;
                 default:
                     paint.StrokeCap = SKStrokeCap.Butt;
-                    dashLengthModifier = 0.0f;
-                    gapLengthModifier = 0.0f;
                     break;
             }
 
@@ -617,13 +609,12 @@ namespace Avalonia.Skia
 
                 for (var i = 0; i < srcDashes.Count; ++i)
                 {
-                    var lengthModifier = i % 2 == 0 ? dashLengthModifier : gapLengthModifier;
-
-                    // Avalonia dash lengths are relative, but Skia takes absolute sizes - need to scale
-                    dashesArray[i] = (float) srcDashes[i] * paint.StrokeWidth + lengthModifier;
+                    dashesArray[i] = (float) srcDashes[i] * paint.StrokeWidth;
                 }
 
-                var pe = SKPathEffect.CreateDash(dashesArray, (float) pen.DashStyle.Offset);
+                var offset = (float)(pen.DashStyle.Offset * pen.Thickness);
+
+                var pe = SKPathEffect.CreateDash(dashesArray, offset);
 
                 paint.PathEffect = pe;
                 rv.AddDisposable(pe);

+ 5 - 3
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@@ -122,14 +122,16 @@ namespace Avalonia.Direct2D1
         /// <returns>The Direct2D brush.</returns>
         public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Factory factory)
         {
+            var d2dLineCap = pen.LineCap.ToDirect2D();
+
             var properties = new StrokeStyleProperties
             {
                 DashStyle = DashStyle.Solid,
                 MiterLimit = (float)pen.MiterLimit,
                 LineJoin = pen.LineJoin.ToDirect2D(),
-                StartCap = pen.StartLineCap.ToDirect2D(),
-                EndCap = pen.EndLineCap.ToDirect2D(),
-                DashCap = pen.DashCap.ToDirect2D()
+                StartCap = d2dLineCap,
+                EndCap = d2dLineCap,
+                DashCap = d2dLineCap
             };
             float[] dashes = null;
             if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)

+ 1 - 7
tests/Avalonia.RenderTests/Shapes/PathTests.cs

@@ -334,11 +334,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
             CompareImages();
         }
 
-#if AVALONIA_SKIA_SKIP_FAIL
-        [Fact(Skip = "FIXME")]
-#else
         [Fact]
-#endif
         public async Task Path_With_PenLineCap()
         {
             Decorator target = new Decorator
@@ -351,10 +347,8 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
                     StrokeThickness = 10,
                     HorizontalAlignment = HorizontalAlignment.Center,
                     VerticalAlignment = VerticalAlignment.Center,
-                    StrokeDashCap = PenLineCap.Triangle,
                     StrokeDashArray = new AvaloniaList<double>(3, 1),
-                    StrokeStartLineCap = PenLineCap.Round,
-                    StrokeEndLineCap = PenLineCap.Square,
+                    StrokeLineCap = PenLineCap.Round,
                     Data = StreamGeometry.Parse("M 20,20 L 180,180"),
                 }
             };

+ 1 - 2
tests/Avalonia.RenderTests/Shapes/PolylineTests.cs

@@ -61,8 +61,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
                     Points = polylinePoints,
                     Stretch = Stretch.Uniform,
                     StrokeJoin = PenLineJoin.Round,
-                    StrokeStartLineCap = PenLineCap.Round,
-                    StrokeEndLineCap = PenLineCap.Round,
+                    StrokeLineCap = PenLineCap.Round,
                     StrokeThickness = 10
                 }
             };

二進制
tests/TestFiles/Direct2D1/Shapes/Path/Path_With_PenLineCap.expected.png


二進制
tests/TestFiles/Skia/Shapes/Path/Path_With_PenLineCap.expected.png