Skip to content
Merged
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
42 changes: 42 additions & 0 deletions src/ImageSharp.Drawing/Shapes/EmptyPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Numerics;

namespace SixLabors.ImageSharp.Drawing
{
/// <summary>
/// A path that is always empty.
/// </summary>
public class EmptyPath : IPath
{
private EmptyPath(PathTypes pathType) => this.PathType = pathType;

/// <summary>
/// Gets the closed path instance of the empty path
/// </summary>
public static EmptyPath ClosedPath { get; } = new EmptyPath(PathTypes.Closed);

/// <summary>
/// Gets the open path instance of the empty path
/// </summary>
public static EmptyPath OpenPath { get; } = new EmptyPath(PathTypes.Open);

/// <inheritdoc />
public PathTypes PathType { get; }

/// <inheritdoc />
public RectangleF Bounds => RectangleF.Empty;

/// <inheritdoc />
public IPath AsClosedPath() => ClosedPath;

/// <inheritdoc />
public IEnumerable<ISimplePath> Flatten() => Array.Empty<ISimplePath>();

/// <inheritdoc />
public IPath Transform(Matrix3x2 matrix) => this;
}
}
10 changes: 10 additions & 0 deletions src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public static class OutlinePathExtensions
/// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception>
public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle, EndCapStyle endCapStyle)
{
if (width <= 0)
{
return Path.Empty;
}

ClipperOffset offset = new(MiterOffsetDelta);
offset.AddPath(path, jointStyle, endCapStyle);

Expand Down Expand Up @@ -92,6 +97,11 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
/// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception>
public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<float> pattern, bool startOff, JointStyle jointStyle, EndCapStyle endCapStyle)
{
if (width <= 0)
{
return Path.Empty;
}

if (pattern.Length < 2)
{
return path.GenerateOutline(width, jointStyle, endCapStyle);
Expand Down
5 changes: 5 additions & 0 deletions src/ImageSharp.Drawing/Shapes/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public Path(Path path)
public Path(params ILineSegment[] segments)
=> this.lineSegments = segments ?? throw new ArgumentNullException(nameof(segments));

/// <summary>
/// Gets the default empty path.
/// </summary>
public static IPath Empty { get; } = EmptyPath.OpenPath;

/// <inheritdoc/>
bool ISimplePath.IsClosed => this.IsClosed;

Expand Down
94 changes: 94 additions & 0 deletions tests/ImageSharp.Drawing.Tests/Shapes/Issues/Issue_224.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Xunit;

namespace SixLabors.ImageSharp.Drawing.Tests
{
/// <summary>
/// see https://github.com/SixLabors/ImageSharp.Drawing/issues/224
/// </summary>
public class Issue_224
{
[Fact]
public async Task OutliningWithZeroWidth_MultiplePatterns()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(0, new float[] { 1, 2 });
});
}

[Fact]
public async Task OutliningWithZeroWidth_SinglePAttern()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(0, new float[] { 1 });
});
}

[Fact]
public async Task OutliningWithZeroWidth_NoPattern()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(0);
});
}

[Fact]
public async Task OutliningWithLessThanZeroWidth_MultiplePatterns()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(-10, new float[] { 1, 2 });
});
}

[Fact]
public async Task OutliningWithLessThanZeroWidth_SinglePAttern()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(-10, new float[] { 1 });
});
}

[Fact]
public async Task OutliningWithLessThanZeroWidth_NoPattern()
{
var shape = new RectangularPolygon(10, 10, 10, 10);

await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
{
_ = shape.GenerateOutline(-10);
});
}

private async Task CompletesIn(TimeSpan span, Action action)
{
var task = Task.Run(action);
var timeout = Task.Delay(span);

var completed = await Task.WhenAny(task, timeout);

Assert.True(task == completed, $"Failed to compelete in {span}");
}
}
}