33
44using System ;
55using System . Collections . Generic ;
6- using System . Linq ;
76using System . Numerics ;
8- using System . Runtime . InteropServices ;
9- using ClipperLib ;
7+ using SixLabors . ImageSharp . Drawing . PolygonClipper ;
108
119namespace SixLabors . ImageSharp . Drawing
1210{
@@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Drawing
1614 public static class OutlinePathExtensions
1715 {
1816 private const double MiterOffsetDelta = 20 ;
19- private const float ScalingFactor = 1000.0f ;
2017
2118 /// <summary>
2219 /// Generates a outline of the path with alternating on and off segments based on the pattern.
@@ -25,6 +22,7 @@ public static class OutlinePathExtensions
2522 /// <param name="width">The final width outline</param>
2623 /// <param name="pattern">The pattern made of multiples of the width.</param>
2724 /// <returns>A new path representing the outline.</returns>
25+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
2826 public static IPath GenerateOutline ( this IPath path , float width , float [ ] pattern )
2927 => path . GenerateOutline ( width , new ReadOnlySpan < float > ( pattern ) ) ;
3028
@@ -35,6 +33,7 @@ public static IPath GenerateOutline(this IPath path, float width, float[] patter
3533 /// <param name="width">The final width outline</param>
3634 /// <param name="pattern">The pattern made of multiples of the width.</param>
3735 /// <returns>A new path representing the outline.</returns>
36+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
3837 public static IPath GenerateOutline ( this IPath path , float width , ReadOnlySpan < float > pattern )
3938 => path . GenerateOutline ( width , pattern , false ) ;
4039
@@ -46,6 +45,7 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
4645 /// <param name="pattern">The pattern made of multiples of the width.</param>
4746 /// <param name="startOff">Weather the first item in the pattern is on or off.</param>
4847 /// <returns>A new path representing the outline.</returns>
48+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
4949 public static IPath GenerateOutline ( this IPath path , float width , float [ ] pattern , bool startOff )
5050 => path . GenerateOutline ( width , new ReadOnlySpan < float > ( pattern ) , startOff ) ;
5151
@@ -57,6 +57,7 @@ public static IPath GenerateOutline(this IPath path, float width, float[] patter
5757 /// <param name="pattern">The pattern made of multiples of the width.</param>
5858 /// <param name="startOff">Weather the first item in the pattern is on or off.</param>
5959 /// <returns>A new path representing the outline.</returns>
60+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
6061 public static IPath GenerateOutline ( this IPath path , float width , ReadOnlySpan < float > pattern , bool startOff )
6162 => GenerateOutline ( path , width , pattern , startOff , JointStyle . Square , EndCapStyle . Butt ) ;
6263
@@ -70,24 +71,18 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
7071 /// <param name="jointStyle">The style to render the joints.</param>
7172 /// <param name="patternSectionCapStyle">The style to render between sections of the specified pattern.</param>
7273 /// <returns>A new path representing the outline.</returns>
74+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
7375 public static IPath GenerateOutline ( this IPath path , float width , ReadOnlySpan < float > pattern , bool startOff , JointStyle jointStyle = JointStyle . Square , EndCapStyle patternSectionCapStyle = EndCapStyle . Butt )
7476 {
7577 if ( pattern . Length < 2 )
7678 {
7779 return path . GenerateOutline ( width , jointStyle : jointStyle ) ;
7880 }
7981
80- JoinType style = Convert ( jointStyle ) ;
81- EndType patternSectionCap = Convert ( patternSectionCapStyle ) ;
82-
8382 IEnumerable < ISimplePath > paths = path . Flatten ( ) ;
8483
85- var offset = new ClipperOffset ( )
86- {
87- MiterLimit = MiterOffsetDelta
88- } ;
89-
90- var buffer = new List < IntPoint > ( 3 ) ;
84+ var offset = new ClipperOffset ( MiterOffsetDelta ) ;
85+ var buffer = new List < PointF > ( ) ;
9186 foreach ( ISimplePath p in paths )
9287 {
9388 bool online = ! startOff ;
@@ -116,13 +111,13 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
116111 float t = targetLength / distToNext ;
117112
118113 Vector2 point = ( currentPoint * ( 1 - t ) ) + ( targetPoint * t ) ;
119- buffer . Add ( currentPoint . ToPoint ( ) ) ;
120- buffer . Add ( point . ToPoint ( ) ) ;
114+ buffer . Add ( currentPoint ) ;
115+ buffer . Add ( point ) ;
121116
122117 // we now inset a line joining
123118 if ( online )
124119 {
125- offset . AddPath ( buffer , style , patternSectionCap ) ;
120+ offset . AddPath ( new ReadOnlySpan < PointF > ( buffer . ToArray ( ) ) , jointStyle , patternSectionCapStyle ) ;
126121 }
127122
128123 online = ! online ;
@@ -137,7 +132,7 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
137132 }
138133 else if ( distToNext <= targetLength )
139134 {
140- buffer . Add ( currentPoint . ToPoint ( ) ) ;
135+ buffer . Add ( currentPoint ) ;
141136 currentPoint = targetPoint ;
142137 i ++ ;
143138 targetLength -= distToNext ;
@@ -148,16 +143,16 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
148143 {
149144 if ( p . IsClosed )
150145 {
151- buffer . Add ( points [ 0 ] . ToPoint ( ) ) ;
146+ buffer . Add ( points [ 0 ] ) ;
152147 }
153148 else
154149 {
155- buffer . Add ( points [ points . Length - 1 ] . ToPoint ( ) ) ;
150+ buffer . Add ( points [ points . Length - 1 ] ) ;
156151 }
157152
158153 if ( online )
159154 {
160- offset . AddPath ( buffer , style , patternSectionCap ) ;
155+ offset . AddPath ( new ReadOnlySpan < PointF > ( buffer . ToArray ( ) ) , jointStyle , patternSectionCapStyle ) ;
161156 }
162157
163158 online = ! online ;
@@ -168,7 +163,7 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
168163 }
169164 }
170165
171- return ExecuteOutliner ( width , offset ) ;
166+ return offset . Execute ( width ) ;
172167 }
173168
174169 /// <summary>
@@ -177,6 +172,7 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
177172 /// <param name="path">the path to outline</param>
178173 /// <param name="width">The final width outline</param>
179174 /// <returns>A new path representing the outline.</returns>
175+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
180176 public static IPath GenerateOutline ( this IPath path , float width ) => GenerateOutline ( path , width , JointStyle . Square , EndCapStyle . Butt ) ;
181177
182178 /// <summary>
@@ -187,69 +183,13 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
187183 /// <param name="jointStyle">The style to render the joints.</param>
188184 /// <param name="endCapStyle">The style to render the end caps of open paths (ignored on closed paths).</param>
189185 /// <returns>A new path representing the outline.</returns>
186+ /// <exception cref="ClipperException">Couldn't calculate offset.</exception>
190187 public static IPath GenerateOutline ( this IPath path , float width , JointStyle jointStyle = JointStyle . Square , EndCapStyle endCapStyle = EndCapStyle . Square )
191188 {
192- var offset = new ClipperOffset ( )
193- {
194- MiterLimit = MiterOffsetDelta
195- } ;
196-
197- JoinType style = Convert ( jointStyle ) ;
198- EndType openEndCapStyle = Convert ( endCapStyle ) ;
199-
200- // Pattern can be applied to the path by cutting it into segments
201- IEnumerable < ISimplePath > paths = path . Flatten ( ) ;
202- foreach ( ISimplePath p in paths )
203- {
204- ReadOnlySpan < Vector2 > vectors = MemoryMarshal . Cast < PointF , Vector2 > ( p . Points . Span ) ;
205- var points = new List < IntPoint > ( vectors . Length ) ;
206- foreach ( Vector2 v in vectors )
207- {
208- points . Add ( new IntPoint ( v . X * ScalingFactor , v . Y * ScalingFactor ) ) ;
209- }
210-
211- EndType type = p . IsClosed ? EndType . etClosedLine : openEndCapStyle ;
212-
213- offset . AddPath ( points , style , type ) ;
214- }
215-
216- return ExecuteOutliner ( width , offset ) ;
217- }
218-
219- private static ComplexPolygon ExecuteOutliner ( float width , ClipperOffset offset )
220- {
221- var tree = new List < List < IntPoint > > ( ) ;
222- offset . Execute ( ref tree , width * ScalingFactor / 2 ) ;
223- var polygons = new List < Polygon > ( ) ;
224- foreach ( List < IntPoint > pt in tree )
225- {
226- PointF [ ] points = pt . Select ( p => new PointF ( p . X / ScalingFactor , p . Y / ScalingFactor ) ) . ToArray ( ) ;
227- polygons . Add ( new Polygon ( new LinearLineSegment ( points ) ) ) ;
228- }
189+ var offset = new ClipperOffset ( MiterOffsetDelta ) ;
190+ offset . AddPath ( path , jointStyle , endCapStyle ) ;
229191
230- return new ComplexPolygon ( polygons . ToArray ( ) ) ;
192+ return offset . Execute ( width ) ;
231193 }
232-
233- private static IntPoint ToPoint ( this PointF vector )
234- => new IntPoint ( vector . X * ScalingFactor , vector . Y * ScalingFactor ) ;
235-
236- private static IntPoint ToPoint ( this Vector2 vector )
237- => new IntPoint ( vector . X * ScalingFactor , vector . Y * ScalingFactor ) ;
238-
239- private static JoinType Convert ( JointStyle style )
240- => style switch
241- {
242- JointStyle . Round => JoinType . jtRound ,
243- JointStyle . Miter => JoinType . jtMiter ,
244- _ => JoinType . jtSquare ,
245- } ;
246-
247- private static EndType Convert ( EndCapStyle style )
248- => style switch
249- {
250- EndCapStyle . Round => EndType . etOpenRound ,
251- EndCapStyle . Square => EndType . etOpenSquare ,
252- _ => EndType . etOpenButt ,
253- } ;
254194 }
255195}
0 commit comments