diff --git a/src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs b/src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs
index c76b7811..21e499a3 100644
--- a/src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs
+++ b/src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs
@@ -15,6 +15,31 @@ public static class OutlinePathExtensions
private const JointStyle DefaultJointStyle = JointStyle.Square;
private const EndCapStyle DefaultEndCapStyle = EndCapStyle.Butt;
+ ///
+ /// Calculates the scaling matrixes tha tmust be applied to the inout and output paths of for successful clipping.
+ ///
+ /// the requested width
+ /// The matrix to apply to the input path
+ /// The matrix to apply to the output path
+ /// The final width to use internally to outlining
+ 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;
+ }
+
///
/// Generates an outline of the path.
///
@@ -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);
}
///
@@ -106,7 +135,9 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan paths = path.Flatten();
+ width = CalculateScalingMatrix(width, out Matrix3x2 scaleUpMartrix, out Matrix3x2 scaleDownMartrix);
+
+ IEnumerable paths = path.Transform(scaleUpMartrix).Flatten();
ClipperOffset offset = new(MiterOffsetDelta);
List buffer = new();
@@ -186,6 +217,6 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan(TestImageProvider provider, float scale)
+ where TPixel : unmanaged, IPixel
+ {
+ 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(TestImageProvider provider, float scale)
+ where TPixel : unmanaged, IPixel
+ {
+ 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 });
+ }
+}
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png
new file mode 100644
index 00000000..1770f151
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc86c51ad4946fb8a314c8d869a83cc2496d30468036729c3827c2c121cae69c
+size 1068
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png
new file mode 100644
index 00000000..a60e0771
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:62f86685f6f2326e629b8b84340bb1b3cbcf6ffe75facff0931b613337772345
+size 3296
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png
new file mode 100644
index 00000000..5bf6378e
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:534d3fece38b94386b6ba20aa121addda1beea61d38cb34b9d2ae09b662fd38b
+size 3585
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png
new file mode 100644
index 00000000..61df79c3
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4fff4ab1fec04c432529fd67d9c50934ce083fa6e7c0c4432fd6520eac2e53ac
+size 3625
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png
new file mode 100644
index 00000000..d5630da2
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Pattern_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:92a356847e8aa36b361a2208021a0c0e2a3287d615f0b948bdbcc0bc3b336bc4
+size 4300
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png
new file mode 100644
index 00000000..924d09bf
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.003.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b02efd026d5ed83d1e0e747c8675d340bda7ba246d5a9385ecc6dabc81861e0
+size 3015
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png
new file mode 100644
index 00000000..e6b9aee3
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.3.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f02d5a40abd8bbef39f2648f2d40d5da5a28adfbc5b015a56641eef5fb50de05
+size 3050
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png
new file mode 100644
index 00000000..8f585eb2
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-0.7.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11b915b55e0005b52bf80e4891f293b6cb6477f3e0d83fead71768ac359fa610
+size 3382
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png
new file mode 100644
index 00000000..79f87092
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a48c33c58c36ba7f581fe7d60eaf0bbc96948640b14ccbdd891adeae031a7ad
+size 3691
diff --git a/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png
new file mode 100644
index 00000000..7b5c2352
--- /dev/null
+++ b/tests/Images/ReferenceOutput/Issue_323/DrawPolygonMustDrawoutlineOnly_Rgba32_Solid300x300_(255,255,255,255)_scale-3.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e80954c53985fc65b0846778861e3c023a8c798d466be887545fe2438ab5e3a3
+size 4353