Skip to content

Commit

Permalink
Merge pull request #324 from SixLabors/issue323
Browse files Browse the repository at this point in the history
Mitigate issue where outline thicknesses less than 0.5 fail out generate solid shapes not outlines
  • Loading branch information
JimBobSquarePants authored Mar 11, 2024
2 parents 2a9ee51 + ab658c0 commit 6b62e73
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 4 deletions.
39 changes: 35 additions & 4 deletions src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ public static class OutlinePathExtensions
private const JointStyle DefaultJointStyle = JointStyle.Square;
private const EndCapStyle DefaultEndCapStyle = EndCapStyle.Butt;

/// <summary>
/// Calculates the scaling matrixes tha tmust be applied to the inout and output paths of for successful clipping.
/// </summary>
/// <param name="width">the requested width</param>
/// <param name="scaleUpMartrix">The matrix to apply to the input path</param>
/// <param name="scaleDownMartrix">The matrix to apply to the output path</param>
/// <returns>The final width to use internally to outlining</returns>
private static float CalculateScalingMatrix(float width, out Matrix3x2 scaleUpMartrix, out Matrix3x2 scaleDownMartrix)
{
// when the thickness is below a 0.5 threshold we need to scale
// the source path (up) and result path (down) by a factor to ensure
// the offest is greater than 0.5 to ensure offsetting isn't skipped.
scaleUpMartrix = Matrix3x2.Identity;
scaleDownMartrix = Matrix3x2.Identity;
if (width < 0.5)
{
float scale = 1 / width;
scaleUpMartrix = Matrix3x2.CreateScale(scale);
scaleDownMartrix = Matrix3x2.CreateScale(width);
width = 1;
}

return width;
}

/// <summary>
/// Generates an outline of the path.
/// </summary>
Expand All @@ -41,10 +66,14 @@ public static IPath GenerateOutline(this IPath path, float width, JointStyle joi
return Path.Empty;
}

width = CalculateScalingMatrix(width, out Matrix3x2 scaleUpMartrix, out Matrix3x2 scaleDownMartrix);

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

return offset.Execute(width);
// transform is noop for Matrix3x2.Identity
offset.AddPath(path.Transform(scaleUpMartrix), jointStyle, endCapStyle);

return offset.Execute(width).Transform(scaleDownMartrix);
}

/// <summary>
Expand Down Expand Up @@ -106,7 +135,9 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
return path.GenerateOutline(width, jointStyle, endCapStyle);
}

IEnumerable<ISimplePath> paths = path.Flatten();
width = CalculateScalingMatrix(width, out Matrix3x2 scaleUpMartrix, out Matrix3x2 scaleDownMartrix);

IEnumerable<ISimplePath> paths = path.Transform(scaleUpMartrix).Flatten();

ClipperOffset offset = new(MiterOffsetDelta);
List<PointF> buffer = new();
Expand Down Expand Up @@ -186,6 +217,6 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
}
}

return offset.Execute(width);
return offset.Execute(width).Transform(scaleDownMartrix);
}
}
54 changes: 54 additions & 0 deletions tests/ImageSharp.Drawing.Tests/Issues/Issue_323.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;

namespace SixLabors.ImageSharp.Drawing.Tests.Issues;

public class Issue_323
{
[Theory]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 3f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 1f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.3f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.7f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.003f)]
public void DrawPolygonMustDrawoutlineOnly<TPixel>(TestImageProvider<TPixel> provider, float scale)
where TPixel : unmanaged, IPixel<TPixel>
{
Color color = Color.RebeccaPurple;
provider.RunValidatingProcessorTest(
x => x.DrawPolygon(
color,
scale,
new PointF[] {
new(5, 5),
new(5, 150),
new(190, 150),
}),
new { scale });
}

[Theory]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 3f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 1f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.3f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.7f)]
[WithSolidFilledImages(300, 300, "White", PixelTypes.Rgba32, 0.003f)]
public void DrawPolygonMustDrawoutlineOnly_Pattern<TPixel>(TestImageProvider<TPixel> provider, float scale)
where TPixel : unmanaged, IPixel<TPixel>
{
Color color = Color.RebeccaPurple;
var pen = Pens.DashDot(color, scale);
provider.RunValidatingProcessorTest(
x => x.DrawPolygon(
pen,
new PointF[] {
new(5, 5),
new(5, 150),
new(190, 150),
}),
new { scale });
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6b62e73

Please sign in to comment.