// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.PolygonClipper; using System; using PCPolygon = SixLabors.PolygonClipper.Polygon; using StrokeOptions = SixLabors.ImageSharp.Drawing.Processing.StrokeOptions; namespace SixLabors.ImageSharp.Drawing.PolygonGeometry { /// /// Generates stroked and merged shapes using polygon stroking and boolean clipping. /// internal static class StrokedShapeGenerator { /// /// Strokes a path and returns a merged outline from its flattened segments. /// /// The source path. It is flattened using the current flattening settings. /// The stroke width in the caller's coordinate space. /// The stroke geometry options. /// /// A representing the stroked outline after boolean merge. /// public static ComplexPolygon GenerateStrokedShapes(IPath path, float width, StrokeOptions options) { // 1) Stroke the input path as open or closed. PCPolygon rings = []; foreach (ISimplePath sp in path.Flatten()) { ReadOnlySpan span = sp.Points.Span; if (span.Length < 2) { continue; } Contour ring = new(span.Length); for (int i = 0; i < span.Length; i++) { PointF p = span[i]; ring.Add(new Vertex(p.X, p.Y)); } if (sp.IsClosed) { ring.Add(ring[0]); } rings.Add(ring); } int count = rings.Count; if (count == 0) { return new([]); } PCPolygon result = PolygonStroker.Stroke(rings, width, CreateStrokeOptions(options)); IPath[] shapes = new IPath[result.Count]; int index = 0; for (int i = 0; i < result.Count; i++) { Contour contour = result[i]; PointF[] points = new PointF[contour.Count]; for (int j = 0; j < contour.Count; j++) { Vertex vertex = contour[j]; points[j] = new PointF((float)vertex.X, (float)vertex.Y); } shapes[index++] = new Polygon(points); } return new(shapes); } private static PolygonClipper.StrokeOptions CreateStrokeOptions(StrokeOptions options) { PolygonClipper.StrokeOptions o = new() { ArcDetailScale = options.ArcDetailScale, MiterLimit = options.MiterLimit, LineJoin = options.LineJoin switch { LineJoin.MiterRound => PolygonClipper.LineJoin.MiterRound, LineJoin.Bevel => PolygonClipper.LineJoin.Bevel, LineJoin.Round => PolygonClipper.LineJoin.Round, LineJoin.MiterRevert => PolygonClipper.LineJoin.MiterRevert, _ => PolygonClipper.LineJoin.Miter, }, LineCap = options.LineCap switch { LineCap.Round => PolygonClipper.LineCap.Round, LineCap.Square => PolygonClipper.LineCap.Square, _ => PolygonClipper.LineCap.Butt, } }; return o; } } }