Skip to content

Commit

Permalink
mitigate limits of outline generation
Browse files Browse the repository at this point in the history
if the outline thickness is going to trigger the noop behaviour of the clipper then scale the inout and output to no trigger that behaviour.
  • Loading branch information
tocsoft committed Mar 8, 2024
1 parent 2a9ee51 commit ab658c0
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 ab658c0

Please sign in to comment.