* add isStroked overload for IGeometryContext * move IGeometryContextEx * fix Fill Path line * remove added whitespaces * close a figure to non-stroke lines with a line * add geometry tests * add wpf test files * Added a test for tracking path closure with missing strokes for various line caps/joins * add IsStroke overload for other segments * update tests * Skip line join test for closed geometry with holes for now --------- Co-authored-by: Nikita Tsukanov <keks9n@gmail.com>pull/15562/head
@ -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); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Media; |
|||
using CrossUI; |
|||
using Xunit; |
|||
|
|||
#if AVALONIA_SKIA
|
|||
namespace Avalonia.Skia.RenderTests; |
|||
#elif AVALONIA_D2D
|
|||
namespace Avalonia.Direct2D1.RenderTests; |
|||
#else
|
|||
namespace Avalonia.RenderTests.WpfCompare; |
|||
#endif
|
|||
|
|||
|
|||
public class CrossGeometryTests : CrossTestBase |
|||
{ |
|||
public CrossGeometryTests() : base("Media/Geometry") |
|||
{ |
|||
} |
|||
|
|||
[CrossFact] |
|||
public void Should_Render_Stream_Geometry() |
|||
{ |
|||
var geometry = new CrossStreamGeometry(); |
|||
|
|||
var context = geometry.GetContext(); |
|||
context.BeginFigure(new Point(150, 15), true, true); |
|||
context.LineTo(new Point(258, 77), true); |
|||
context.LineTo(new Point(258, 202), true); |
|||
context.LineTo(new Point(150, 265), true); |
|||
context.LineTo(new Point(42, 202), true); |
|||
context.LineTo(new Point(42, 77), true); |
|||
context.EndFigure(); |
|||
|
|||
var brush = new CrossDrawingBrush() |
|||
{ |
|||
TileMode = TileMode.None, |
|||
Drawing = new CrossDrawingGroup() |
|||
{ |
|||
Children = new List<CrossDrawing>() |
|||
{ |
|||
new CrossGeometryDrawing(new CrossRectangleGeometry(new(0, 0, 300, 280))) |
|||
{ |
|||
Brush = new CrossSolidColorBrush(Colors.White) |
|||
}, |
|||
new CrossGeometryDrawing(geometry) |
|||
{ |
|||
Pen = new CrossPen() |
|||
{ |
|||
Brush = new CrossSolidColorBrush(Colors.Black), |
|||
Thickness = 2 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
RenderAndCompare(new CrossControl() |
|||
{ |
|||
Width = 300, |
|||
Height = 280, |
|||
Background = brush |
|||
}); |
|||
} |
|||
|
|||
[CrossFact] |
|||
public void Should_Render_Geometry_With_Strokeless_Lines() |
|||
{ |
|||
var geometry = new CrossStreamGeometry(); |
|||
|
|||
var context = geometry.GetContext(); |
|||
context.BeginFigure(new Point(150, 15), true, true); |
|||
context.LineTo(new Point(258, 77), true); |
|||
context.LineTo(new Point(258, 202), false); |
|||
context.LineTo(new Point(150, 265), true); |
|||
context.LineTo(new Point(42, 202), true); |
|||
context.LineTo(new Point(42, 77), false); |
|||
context.EndFigure(); |
|||
|
|||
var brush = new CrossDrawingBrush() |
|||
{ |
|||
TileMode = TileMode.None, |
|||
Drawing = new CrossDrawingGroup() |
|||
{ |
|||
Children = new List<CrossDrawing>() |
|||
{ |
|||
new CrossGeometryDrawing(new CrossRectangleGeometry(new(0, 0, 300, 280))) |
|||
{ |
|||
Brush = new CrossSolidColorBrush(Colors.White) |
|||
}, |
|||
new CrossGeometryDrawing(geometry) |
|||
{ |
|||
Pen = new CrossPen() |
|||
{ |
|||
Brush = new CrossSolidColorBrush(Colors.Black), |
|||
Thickness = 2 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
RenderAndCompare(new CrossControl() |
|||
{ |
|||
Width = 300, |
|||
Height = 280, |
|||
Background = brush |
|||
}); |
|||
} |
|||
|
|||
// Skip the test for now
|
|||
#if !AVALONIA_SKIA
|
|||
[CrossTheory, |
|||
InlineData(PenLineCap.Flat, PenLineJoin.Round), |
|||
InlineData(PenLineCap.Flat, PenLineJoin.Bevel), |
|||
InlineData(PenLineCap.Flat, PenLineJoin.Miter), |
|||
InlineData(PenLineCap.Round, PenLineJoin.Round), |
|||
InlineData(PenLineCap.Round, PenLineJoin.Bevel), |
|||
InlineData(PenLineCap.Round, PenLineJoin.Miter), |
|||
] |
|||
#endif
|
|||
public void Should_Properly_CloseFigure(PenLineCap lineCap, PenLineJoin lineJoin) |
|||
{ |
|||
var geometry = new CrossPathGeometry(); |
|||
|
|||
|
|||
var center = new Point(150, 150); |
|||
var r = 100d; |
|||
|
|||
var pointCount = 5; |
|||
var points = Enumerable.Range(0, pointCount).Select(a => a * Math.PI / pointCount * 2).Select(a => |
|||
new Point(center.X + Math.Sin(a) * r, center.Y + Math.Cos(a) * r)).ToArray(); |
|||
|
|||
var figure = new CrossPathFigure() { Start = points[0], Closed = true }; |
|||
geometry.Figures.Add(figure); |
|||
var lineNum = 0; |
|||
for (var c = 2; lineNum < pointCount - 1; c = (c + 2) % pointCount, lineNum++) |
|||
{ |
|||
figure.Segments.Add(new CrossPathSegment.Line(points[c], (lineNum) % 3 < 2)); |
|||
} |
|||
|
|||
var control = new CrossFuncControl(ctx => |
|||
{ |
|||
ctx.DrawRectangle(new CrossSolidColorBrush(Colors.White), null, new(0, 0, 300, 300)); |
|||
ctx.DrawGeometry(null, |
|||
new CrossPen() |
|||
{ |
|||
Brush = new CrossSolidColorBrush(Colors.Black), |
|||
Thickness = 20, |
|||
LineJoin = lineJoin, |
|||
LineCap = lineCap |
|||
}, geometry); |
|||
}) { Width = 300, Height = 300 }; |
|||
RenderAndCompare(control, |
|||
$"{nameof(Should_Properly_CloseFigure)}_{lineCap}_{lineJoin}"); |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |