Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add isStroked overload for IGeometryContext #15430

Merged
merged 11 commits into from
Apr 30, 2024
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/ArcSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public SweepDirection SweepDirection

internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection);
ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection, IsStroked);
}

public override string ToString()
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/BezierSegment .cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public Point Point3

internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.CubicBezierTo(Point1, Point2, Point3);
ctx.CubicBezierTo(Point1, Point2, Point3, IsStroked);
}

public override string ToString()
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/LineSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public Point Point

internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.LineTo(Point);
ctx.LineTo(Point, IsStroked);
}

public override string ToString()
Expand Down
9 changes: 9 additions & 0 deletions src/Avalonia.Base/Media/PathSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,14 @@ namespace Avalonia.Media
public abstract class PathSegment : AvaloniaObject
{
internal abstract void ApplyTo(StreamGeometryContext ctx);

public static readonly StyledProperty<bool> IsStrokedProperty =
AvaloniaProperty.Register<PathSegment, bool>(nameof(IsStroked), true);

public bool IsStroked
{
get => GetValue(IsStrokedProperty);
set => SetValue(IsStrokedProperty, value);
}
}
}
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/QuadraticBezierSegment .cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public Point Point2

internal override void ApplyTo(StreamGeometryContext ctx)
{
ctx.QuadraticBezierTo(Point1, Point2);
ctx.QuadraticBezierTo(Point1, Point2, IsStroked);
}

public override string ToString()
Expand Down
43 changes: 42 additions & 1 deletion src/Avalonia.Base/Media/StreamGeometryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Avalonia.Media
/// of <see cref="StreamGeometryContext"/> is obtained by calling
/// <see cref="StreamGeometry.Open"/>.
/// </remarks>
public class StreamGeometryContext : IGeometryContext
public class StreamGeometryContext : IGeometryContext, IGeometryContext2
{
private readonly IStreamGeometryContextImpl _impl;

Expand Down Expand Up @@ -102,5 +102,46 @@ public void Dispose()
{
_impl.Dispose();
}

/// <inheritdoc/>
public void LineTo(Point point, bool isStroked)
{
if (_impl is IGeometryContext2 context2)
context2.LineTo(point, isStroked);
else
_impl.LineTo(point);

_currentPoint = point;
}

public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked)
{
if (_impl is IGeometryContext2 context2)
context2.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection, isStroked);
else
_impl.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection);

_currentPoint = point;
}

public void CubicBezierTo(Point controlPoint1, Point controlPoint2, Point endPoint, bool isStroked)
{
if (_impl is IGeometryContext2 context2)
context2.CubicBezierTo(controlPoint1, controlPoint2, endPoint, isStroked);
else
_impl.CubicBezierTo(controlPoint1, controlPoint2, endPoint);

_currentPoint = endPoint;
}

public void QuadraticBezierTo(Point controlPoint, Point endPoint, bool isStroked)
{
if (_impl is IGeometryContext2 context2)
context2.QuadraticBezierTo(controlPoint, endPoint, isStroked);
else
_impl.QuadraticBezierTo(controlPoint, endPoint);

_currentPoint = endPoint;
}
}
}
46 changes: 46 additions & 0 deletions src/Avalonia.Base/Platform/IGeometryContext2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Avalonia.Media;

namespace Avalonia.Platform
{
// TODO12 combine with IGeometryContext
public interface IGeometryContext2 : IGeometryContext
{
/// <summary>
/// Draws a line to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="isStroked">Whether the segment is stroked</param>
void LineTo(Point point, bool isStroked);

/// <summary>
/// Draws an arc to the specified point.
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle (in radians) of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
/// </param>
/// <param name="isStroked">Whether the segment is stroked</param>
void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked);

/// <summary>
/// Draws a Bezier curve to the specified point.
/// </summary>
/// <param name="controlPoint1">The first control point used to specify the shape of the curve.</param>
/// <param name="controlPoint2">The second control point used to specify the shape of the curve.</param>
/// <param name="endPoint">The destination point for the end of the curve.</param>
/// <param name="isStroked">Whether the segment is stroked</param>
void CubicBezierTo(Point controlPoint1, Point controlPoint2, Point endPoint, bool isStroked);

/// <summary>
/// Draws a quadratic Bezier curve to the specified point
/// </summary>
/// <param name="controlPoint ">Control point</param>
/// <param name="endPoint">DestinationPoint</param>
/// <param name="isStroked">Whether the segment is stroked</param>
void QuadraticBezierTo(Point controlPoint, Point endPoint, bool isStroked);
}

}
128 changes: 119 additions & 9 deletions src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using Avalonia.Media;
using Avalonia.Platform;
using SkiaSharp;
Expand Down Expand Up @@ -77,12 +78,14 @@ private static SKPath CreateEmptyPath()
/// <summary>
/// A Skia implementation of a <see cref="IStreamGeometryContextImpl"/>.
/// </summary>
private class StreamContext : IStreamGeometryContextImpl
private class StreamContext : IStreamGeometryContextImpl, IGeometryContext2
{
private readonly StreamGeometryImpl _geometryImpl;
private SKPath Stroke => _geometryImpl._strokePath;
private SKPath Fill => _geometryImpl._fillPath ??= new();
private bool _isFilled;
private Point _startPoint;
private bool _isFigureBroken;
private bool Duplicate => _isFilled && !ReferenceEquals(_geometryImpl._fillPath, Stroke);

/// <summary>
Expand All @@ -93,7 +96,7 @@ public StreamContext(StreamGeometryImpl geometryImpl)
{
_geometryImpl = geometryImpl;
}

/// <inheritdoc />
/// <remarks>Will update bounds of passed geometry.</remarks>
public void Dispose()
Expand All @@ -117,7 +120,7 @@ public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc,
sweep,
(float)point.X,
(float)point.Y);
if(Duplicate)
if (Duplicate)
Fill.ArcTo(
(float)size.Width,
(float)size.Height,
Expand All @@ -136,34 +139,36 @@ public void BeginFigure(Point startPoint, bool isFilled)
if (Stroke == Fill)
_geometryImpl._fillPath = Stroke.Clone();
}

_isFilled = isFilled;
_startPoint = startPoint;
_isFigureBroken = false;
Stroke.MoveTo((float)startPoint.X, (float)startPoint.Y);
if(Duplicate)
if (Duplicate)
Fill.MoveTo((float)startPoint.X, (float)startPoint.Y);
}

/// <inheritdoc />
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
Stroke.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
if(Duplicate)
if (Duplicate)
Fill.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
}

/// <inheritdoc />
public void QuadraticBezierTo(Point point1, Point point2)
{
Stroke.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
if(Duplicate)
if (Duplicate)
Fill.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
}

/// <inheritdoc />
public void LineTo(Point point)
{
Stroke.LineTo((float)point.X, (float)point.Y);
if(Duplicate)
if (Duplicate)
Fill.LineTo((float)point.X, (float)point.Y);
}

Expand All @@ -172,7 +177,13 @@ public void EndFigure(bool isClosed)
{
if (isClosed)
{
Stroke.Close();
if (_isFigureBroken)
{
LineTo(_startPoint);
_isFigureBroken = false;
}
else
Stroke.Close();
if (Duplicate)
Fill.Close();
}
Expand All @@ -183,6 +194,105 @@ public void SetFillRule(FillRule fillRule)
{
Fill.FillType = fillRule == FillRule.EvenOdd ? SKPathFillType.EvenOdd : SKPathFillType.Winding;
}

/// <inheritdoc />
public void LineTo(Point point, bool isStroked)
{
if (isStroked)
{
Stroke.LineTo((float)point.X, (float)point.Y);
}
else
{
if (Stroke == Fill)
_geometryImpl._fillPath = Stroke.Clone();

_isFigureBroken = true;

Stroke.MoveTo((float)point.X, (float)point.Y);
}
if (Duplicate)
Fill.LineTo((float)point.X, (float)point.Y);
}

/// <inheritdoc />
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked)
{
var arc = isLargeArc ? SKPathArcSize.Large : SKPathArcSize.Small;
var sweep = sweepDirection == SweepDirection.Clockwise
? SKPathDirection.Clockwise
: SKPathDirection.CounterClockwise;

if (isStroked)
{
Stroke.ArcTo(
(float)size.Width,
(float)size.Height,
(float)rotationAngle,
arc,
sweep,
(float)point.X,
(float)point.Y);
}
else
{
if (Stroke == Fill)
_geometryImpl._fillPath = Stroke.Clone();

_isFigureBroken = true;

Stroke.MoveTo((float)point.X, (float)point.Y);
}
if (Duplicate)
Fill.ArcTo(
(float)size.Width,
(float)size.Height,
(float)rotationAngle,
arc,
sweep,
(float)point.X,
(float)point.Y);
}

/// <inheritdoc />
public void CubicBezierTo(Point point1, Point point2, Point point3, bool isStroked)
{
if (isStroked)
{
Stroke.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
}
else
{
if (Stroke == Fill)
_geometryImpl._fillPath = Stroke.Clone();

_isFigureBroken = true;

Stroke.MoveTo((float)point3.X, (float)point3.Y);
}
if (Duplicate)
Fill.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
}

/// <inheritdoc />
public void QuadraticBezierTo(Point point1, Point point2, bool isStroked)
{
if (isStroked)
{
Stroke.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
}
else
{
if (Stroke == Fill)
_geometryImpl._fillPath = Stroke.Clone();

_isFigureBroken = true;

Stroke.MoveTo((float)point2.X, (float)point2.Y);
}
if (Duplicate)
Fill.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
}
}
}
}
Loading
Loading