Skip to content

Commit 9e2259d

Browse files
committed
Return empty path when outline width is less than or equal to zero
1 parent 764f6f1 commit 9e2259d

4 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Numerics;
7+
8+
namespace SixLabors.ImageSharp.Drawing
9+
{
10+
/// <summary>
11+
/// A path that is always empty.
12+
/// </summary>
13+
public class EmptyPath : IPath
14+
{
15+
private EmptyPath(PathTypes pathType) => this.PathType = pathType;
16+
17+
/// <summary>
18+
/// Gets the closed path instance of the empty path
19+
/// </summary>
20+
public static EmptyPath ClosedPath { get; } = new EmptyPath(PathTypes.Closed);
21+
22+
/// <summary>
23+
/// Gets the open path instance of the empty path
24+
/// </summary>
25+
public static EmptyPath OpenPath { get; } = new EmptyPath(PathTypes.Open);
26+
27+
/// <inheritdoc />
28+
public PathTypes PathType { get; }
29+
30+
/// <inheritdoc />
31+
public RectangleF Bounds => RectangleF.Empty;
32+
33+
/// <inheritdoc />
34+
public IPath AsClosedPath() => ClosedPath;
35+
36+
/// <inheritdoc />
37+
public IEnumerable<ISimplePath> Flatten() => Array.Empty<ISimplePath>();
38+
39+
/// <inheritdoc />
40+
public IPath Transform(Matrix3x2 matrix) => this;
41+
}
42+
}

src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public static class OutlinePathExtensions
3737
/// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception>
3838
public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle, EndCapStyle endCapStyle)
3939
{
40+
if (width <= 0)
41+
{
42+
return Path.Empty;
43+
}
44+
4045
ClipperOffset offset = new(MiterOffsetDelta);
4146
offset.AddPath(path, jointStyle, endCapStyle);
4247

@@ -92,6 +97,11 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
9297
/// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception>
9398
public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<float> pattern, bool startOff, JointStyle jointStyle, EndCapStyle endCapStyle)
9499
{
100+
if (width <= 0)
101+
{
102+
return Path.Empty;
103+
}
104+
95105
if (pattern.Length < 2)
96106
{
97107
return path.GenerateOutline(width, jointStyle, endCapStyle);

src/ImageSharp.Drawing/Shapes/Path.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public Path(Path path)
4242
public Path(params ILineSegment[] segments)
4343
=> this.lineSegments = segments ?? throw new ArgumentNullException(nameof(segments));
4444

45+
/// <summary>
46+
/// Gets the default empty path.
47+
/// </summary>
48+
public static IPath Empty { get; } = EmptyPath.OpenPath;
49+
4550
/// <inheritdoc/>
4651
bool ISimplePath.IsClosed => this.IsClosed;
4752

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Numerics;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
11+
namespace SixLabors.ImageSharp.Drawing.Tests
12+
{
13+
/// <summary>
14+
/// see https://github.com/SixLabors/ImageSharp.Drawing/issues/224
15+
/// </summary>
16+
public class Issue_224
17+
{
18+
[Fact]
19+
public async Task OutliningWithZeroWidth_MultiplePatterns()
20+
{
21+
var shape = new RectangularPolygon(10, 10, 10, 10);
22+
23+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
24+
{
25+
_ = shape.GenerateOutline(0, new float[] { 1, 2 });
26+
});
27+
}
28+
29+
[Fact]
30+
public async Task OutliningWithZeroWidth_SinglePAttern()
31+
{
32+
var shape = new RectangularPolygon(10, 10, 10, 10);
33+
34+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
35+
{
36+
_ = shape.GenerateOutline(0, new float[] { 1 });
37+
});
38+
}
39+
40+
[Fact]
41+
public async Task OutliningWithZeroWidth_NoPattern()
42+
{
43+
var shape = new RectangularPolygon(10, 10, 10, 10);
44+
45+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
46+
{
47+
_ = shape.GenerateOutline(0);
48+
});
49+
}
50+
51+
[Fact]
52+
public async Task OutliningWithLessThanZeroWidth_MultiplePatterns()
53+
{
54+
var shape = new RectangularPolygon(10, 10, 10, 10);
55+
56+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
57+
{
58+
_ = shape.GenerateOutline(-10, new float[] { 1, 2 });
59+
});
60+
}
61+
62+
[Fact]
63+
public async Task OutliningWithLessThanZeroWidth_SinglePAttern()
64+
{
65+
var shape = new RectangularPolygon(10, 10, 10, 10);
66+
67+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
68+
{
69+
_ = shape.GenerateOutline(-10, new float[] { 1 });
70+
});
71+
}
72+
73+
[Fact]
74+
public async Task OutliningWithLessThanZeroWidth_NoPattern()
75+
{
76+
var shape = new RectangularPolygon(10, 10, 10, 10);
77+
78+
await this.CompletesIn(TimeSpan.FromSeconds(1), () =>
79+
{
80+
_ = shape.GenerateOutline(-10);
81+
});
82+
}
83+
84+
private async Task CompletesIn(TimeSpan span, Action action)
85+
{
86+
var task = Task.Run(action);
87+
var timeout = Task.Delay(span);
88+
89+
var completed = await Task.WhenAny(task, timeout);
90+
91+
Assert.True(task == completed, $"Failed to compelete in {span}");
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)