Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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