Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<ItemGroup>
<PackageReference Include="SixLabors.Fonts" Version="3.0.0-alpha.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="4.0.0-alpha.0.44" />
<PackageReference Include="SixLabors.PolygonClipper" Version="1.0.0-alpha.0.48" />
</ItemGroup>
<Import Project="..\..\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems" Label="Shared" />
</Project>
6 changes: 4 additions & 2 deletions src/ImageSharp.Drawing/Processing/ShapeOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.PolygonClipper;

namespace SixLabors.ImageSharp.Drawing.Processing;

/// <summary>
Expand All @@ -24,9 +26,9 @@ private ShapeOptions(ShapeOptions source)
/// <summary>
/// Gets or sets the clipping operation.
/// <para/>
/// Defaults to <see cref="ClippingOperation.Difference"/>.
/// Defaults to <see cref="BooleanOperation.Difference"/>.
/// </summary>
public ClippingOperation ClippingOperation { get; set; } = ClippingOperation.Difference;
public BooleanOperation ClippingOperation { get; set; } = BooleanOperation.Difference;

/// <summary>
/// Gets or sets the rule for calculating intersection points.
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp.Drawing/Shapes/ClipPathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ public static IPath Clip(
ShapeOptions options,
IEnumerable<IPath> clipPaths)
{
Clipper clipper = new();
Clipper clipper = new(options.IntersectionRule);

clipper.AddPath(subjectPath, ClippingType.Subject);
clipper.AddPaths(clipPaths, ClippingType.Clip);

IPath[] result = clipper.GenerateClippedShapes(options.ClippingOperation, options.IntersectionRule);
IPath[] result = clipper.GenerateClippedShapes(options.ClippingOperation);

return new ComplexPolygon(result);
}
Expand Down
38 changes: 0 additions & 38 deletions src/ImageSharp.Drawing/Shapes/ClippingOperation.cs

This file was deleted.

21 changes: 21 additions & 0 deletions src/ImageSharp.Drawing/Shapes/ISimplePath.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ComTypes;
using SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper;
using SixLabors.PolygonClipper;

namespace SixLabors.ImageSharp.Drawing;

/// <summary>
Expand All @@ -17,4 +22,20 @@ public interface ISimplePath
/// Gets the points that make this up as a simple linear path.
/// </summary>
ReadOnlyMemory<PointF> Points { get; }

/// <summary>
/// Converts to <see cref="SixLabors.PolygonClipper.Polygon"/>
/// </summary>
/// <returns>The converted polygon.</returns>
internal SixLabors.PolygonClipper.Contour ToContour()
{
Contour contour = new();

foreach (PointF point in this.Points.Span)
{
contour.AddVertex(new Vertex(point.X, point.Y));
}

return contour;
}
}
94 changes: 44 additions & 50 deletions src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs
Original file line number Diff line number Diff line change
@@ -1,59 +1,53 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.PolygonClipper;
using ClipperPolygon = SixLabors.PolygonClipper.Polygon;
using PolygonClipperAction = SixLabors.PolygonClipper.PolygonClipper;

namespace SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper;

/// <summary>
/// Library to clip polygons.
/// Performs polygon clipping operations.
/// </summary>
internal class Clipper
internal sealed class Clipper
{
private readonly PolygonClipper polygonClipper;
private ClipperPolygon? subject;
private ClipperPolygon? clip;
private readonly IntersectionRule rule;

/// <summary>
/// Initializes a new instance of the <see cref="Clipper"/> class.
/// </summary>
public Clipper()
=> this.polygonClipper = new PolygonClipper();
/// <param name="rule">The intersection rule.</param>
public Clipper(IntersectionRule rule) => this.rule = rule;

/// <summary>
/// Generates the clipped shapes from the previously provided paths.
/// </summary>
/// <param name="operation">The clipping operation.</param>
/// <param name="rule">The intersection rule.</param>
/// <returns>The <see cref="T:IPath[]"/>.</returns>
public IPath[] GenerateClippedShapes(ClippingOperation operation, IntersectionRule rule)
public IPath[] GenerateClippedShapes(BooleanOperation operation)
{
PathsF closedPaths = [];
PathsF openPaths = [];
ArgumentNullException.ThrowIfNull(this.subject);
ArgumentNullException.ThrowIfNull(this.clip);

FillRule fillRule = rule == IntersectionRule.EvenOdd ? FillRule.EvenOdd : FillRule.NonZero;
this.polygonClipper.Execute(operation, fillRule, closedPaths, openPaths);
PolygonClipperAction polygonClipper = new(this.subject, this.clip, operation);

IPath[] shapes = new IPath[closedPaths.Count + openPaths.Count];
ClipperPolygon result = polygonClipper.Run();

int index = 0;
for (int i = 0; i < closedPaths.Count; i++)
{
PathF path = closedPaths[i];
PointF[] points = new PointF[path.Count];
IPath[] shapes = new IPath[result.Count];

for (int j = 0; j < path.Count; j++)
{
points[j] = path[j];
}

shapes[index++] = new Polygon(points);
}

for (int i = 0; i < openPaths.Count; i++)
int index = 0;
for (int i = 0; i < result.Count; i++)
{
PathF path = openPaths[i];
PointF[] points = new PointF[path.Count];
Contour contour = result[i];
PointF[] points = new PointF[contour.Count];

for (int j = 0; j < path.Count; j++)
for (int j = 0; j < contour.Count; j++)
{
points[j] = path[j];
Vertex vertex = contour[j];
points[j] = new PointF((float)vertex.X, (float)vertex.Y);
}

shapes[index++] = new Polygon(points);
Expand All @@ -63,17 +57,29 @@ public IPath[] GenerateClippedShapes(ClippingOperation operation, IntersectionRu
}

/// <summary>
/// Adds the shapes.
/// Adds the collection of paths.
/// </summary>
/// <param name="paths">The paths.</param>
/// <param name="clippingType">The clipping type.</param>
public void AddPaths(IEnumerable<IPath> paths, ClippingType clippingType)
{
Guard.NotNull(paths, nameof(paths));

foreach (IPath p in paths)
// Accumulate all paths of the complex shape into a single polygon.
ClipperPolygon polygon = [];

foreach (IPath path in paths)
{
this.AddPath(p, clippingType);
polygon = PolygonClipperFactory.FromSimplePaths(path.Flatten(), this.rule, polygon);
}

if (clippingType == ClippingType.Clip)
{
this.clip = polygon;
}
else
{
this.subject = polygon;
}
}

Expand All @@ -86,26 +92,14 @@ public void AddPath(IPath path, ClippingType clippingType)
{
Guard.NotNull(path, nameof(path));

foreach (ISimplePath p in path.Flatten())
ClipperPolygon polygon = PolygonClipperFactory.FromSimplePaths(path.Flatten(), this.rule);
if (clippingType == ClippingType.Clip)
{
this.AddPath(p, clippingType);
this.clip = polygon;
}
}

/// <summary>
/// Adds the path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="clippingType">Type of the poly.</param>
internal void AddPath(ISimplePath path, ClippingType clippingType)
{
ReadOnlySpan<PointF> vectors = path.Points.Span;
PathF points = new(vectors.Length);
for (int i = 0; i < vectors.Length; i++)
else
{
points.Add(vectors[i]);
this.subject = polygon;
}

this.polygonClipper.AddPath(points, clippingType, !path.IsClosed);
}
}
23 changes: 0 additions & 23 deletions src/ImageSharp.Drawing/Shapes/PolygonClipper/FillRule.cs

This file was deleted.

29 changes: 0 additions & 29 deletions src/ImageSharp.Drawing/Shapes/PolygonClipper/JoinWith.cs

This file was deleted.

Loading
Loading