66using System . Diagnostics . CodeAnalysis ;
77using System . Linq ;
88using System . Numerics ;
9+ using System . Runtime . CompilerServices ;
910
1011namespace SixLabors . ImageSharp . Drawing
1112{
@@ -118,18 +119,33 @@ SegmentInfo IPathInternals.PointAlongPath(float distance)
118119 /// <inheritdoc/>
119120 IReadOnlyList < InternalPath > IInternalPathOwner . GetRingsAsInternalPath ( ) => new [ ] { this . InnerPath } ;
120121
122+ /// <summary>
123+ /// Converts an svg path into a Path
124+ /// </summary>
125+ /// <param name="data">data</param>
126+ /// <param name="value">path</param>
127+ /// <returns>true if successful</returns>
128+ public static bool TryParseSvgPath ( string data , out IPath value )
129+ => TryParseSvgPath ( data . AsSpan ( ) , out value ) ;
130+
131+ /// <summary>
132+ /// Converts an svg path into a Path
133+ /// </summary>
134+ /// <param name="data">data</param>
135+ /// <param name="value">path</param>
136+ /// <returns>true if successful</returns>
121137 public static bool TryParseSvgPath ( ReadOnlySpan < char > data , out IPath value )
122138 {
123139 value = null ;
124140
125141 var builder = new PathBuilder ( ) ;
126142
127- //parse svg
128143 PointF first = PointF . Empty ;
129144 PointF c = PointF . Empty ;
130145 PointF lastc = PointF . Empty ;
131- // stackalloc ???
132- var points = new PointF [ 3 ] . AsSpan ( ) ;
146+ PointF point1 ;
147+ PointF point2 ;
148+ PointF point3 ;
133149
134150 char op = '\0 ' ;
135151 char previousOp = '\0 ' ;
@@ -167,22 +183,22 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> data, out IPath value)
167183
168184 data = TrimSeperator ( data . Slice ( 1 ) ) ;
169185 }
186+
170187 switch ( op )
171188 {
172189 case 'M' :
173- data = FindPoints ( data , points , 1 , relative , c ) ;
174- builder . MoveTo ( points [ 0 ] ) ;
190+ data = FindPoint ( data , out point1 , relative , c ) ;
191+ builder . MoveTo ( point1 ) ;
175192 previousOp = '\0 ' ;
176193 op = 'L' ;
177- c = points [ 0 ] ;
194+ c = point1 ;
178195 break ;
179196 case 'L' :
180- data = FindPoints ( data , points , 1 , relative , c ) ;
181- builder . LineTo ( points [ 0 ] ) ;
182- c = points [ 0 ] ;
197+ data = FindPoint ( data , out point1 , relative , c ) ;
198+ builder . LineTo ( point1 ) ;
199+ c = point1 ;
183200 break ;
184201 case 'H' :
185- {
186202 data = FindScaler ( data , out float x ) ;
187203 if ( relative )
188204 {
@@ -191,11 +207,8 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> data, out IPath value)
191207
192208 builder . LineTo ( x , c . Y ) ;
193209 c . X = x ;
194- }
195-
196- break ;
210+ break ;
197211 case 'V' :
198- {
199212 data = FindScaler ( data , out float y ) ;
200213 if ( relative )
201214 {
@@ -204,47 +217,50 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> data, out IPath value)
204217
205218 builder . LineTo ( c . X , y ) ;
206219 c . Y = y ;
207- }
208- break ;
220+ break ;
209221 case 'C' :
210- data = FindPoints ( data , points , 3 , relative , c ) ;
211- builder . CubicBezierTo ( points [ 0 ] , points [ 1 ] , points [ 2 ] ) ;
212- lastc = points [ 1 ] ;
213- c = points [ 2 ] ;
222+ data = FindPoint ( data , out point1 , relative , c ) ;
223+ data = FindPoint ( data , out point2 , relative , c ) ;
224+ data = FindPoint ( data , out point3 , relative , c ) ;
225+ builder . CubicBezierTo ( point1 , point2 , point3 ) ;
226+ lastc = point2 ;
227+ c = point3 ;
214228 break ;
215229 case 'S' :
216- data = FindPoints ( data , points , 2 , relative , c ) ;
217- points [ 0 ] = c ;
218- if ( previousOp == 'C' || previousOp == 'S' )
230+ data = FindPoint ( data , out point2 , relative , c ) ;
231+ data = FindPoint ( data , out point3 , relative , c ) ;
232+ point1 = c ;
233+ if ( previousOp is 'C' or 'S' )
219234 {
220- points [ 0 ] . X -= lastc . X - c . X ;
221- points [ 0 ] . Y -= lastc . Y - c . Y ;
235+ point1 . X -= lastc . X - c . X ;
236+ point1 . Y -= lastc . Y - c . Y ;
222237 }
223- builder . CubicBezierTo ( points [ 0 ] , points [ 1 ] , points [ 2 ] ) ;
224- lastc = points [ 1 ] ;
225- c = points [ 2 ] ;
238+
239+ builder . CubicBezierTo ( point1 , point2 , point3 ) ;
240+ lastc = point2 ;
241+ c = point3 ;
226242 break ;
227243 case 'Q' : // Quadratic Bezier Curve
228- data = FindPoints ( data , points , 2 , relative , c ) ;
229- builder . QuadraticBezierTo ( points [ 0 ] , points [ 1 ] ) ;
230- lastc = points [ 0 ] ;
231- c = points [ 1 ] ;
244+ data = FindPoint ( data , out point1 , relative , c ) ;
245+ data = FindPoint ( data , out point2 , relative , c ) ;
246+ builder . QuadraticBezierTo ( point1 , point2 ) ;
247+ lastc = point2 ;
248+ c = point2 ;
232249 break ;
233250 case 'T' :
234- data = FindPoints ( data , points . Slice ( 1 ) , 1 , relative , c ) ;
235- points [ 0 ] = c ;
251+ data = FindPoint ( data , out point2 , relative , c ) ;
252+ point1 = c ;
236253 if ( previousOp is 'Q' or 'T' )
237254 {
238- points [ 0 ] . X -= lastc . X - c . X ;
239- points [ 0 ] . Y -= lastc . Y - c . Y ;
255+ point1 . X -= lastc . X - c . X ;
256+ point1 . Y -= lastc . Y - c . Y ;
240257 }
241258
242- builder . QuadraticBezierTo ( points [ 0 ] , points [ 1 ] ) ;
243- lastc = points [ 0 ] ;
244- c = points [ 1 ] ;
259+ builder . QuadraticBezierTo ( point1 , point2 ) ;
260+ lastc = point1 ;
261+ c = point2 ;
245262 break ;
246263 case 'A' :
247- {
248264 data = FindScaler ( data , out float radiiX ) ;
249265 data = TrimSeperator ( data ) ;
250266 data = FindScaler ( data , out float radiiY ) ;
@@ -261,30 +277,31 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> data, out IPath value)
261277 builder . ArcTo ( radiiX , radiiY , angle , largeArc == 1 , sweep == 1 , point ) ;
262278 c = point ;
263279 }
264- }
265- break ;
280+
281+ break ;
266282 case 'Z' :
267283 builder . CloseFigure ( ) ;
268284 c = first ;
269285 break ;
270286 case '~' :
271- {
272- SkPoint args [ 2 ] ;
273- data = find_points ( data , args , 2 , false , nullptr ) ;
274- path . moveTo ( args [ 0 ] . fX , args [ 0 ] . fY ) ;
275- path . lineTo ( args [ 1 ] . fX , args [ 1 ] . fY ) ;
276- }
277- break ;
287+ data = FindPoint ( data , out point1 , relative , c ) ;
288+ data = FindPoint ( data , out point2 , relative , c ) ;
289+ builder . MoveTo ( point1 ) ;
290+ builder . LineTo ( point2 ) ;
291+ break ;
278292 default :
279293 return false ;
280294 }
295+
281296 if ( previousOp == 0 )
282297 {
283298 first = c ;
284299 }
300+
285301 previousOp = op ;
286302 }
287303
304+ value = builder . Build ( ) ;
288305 return true ;
289306
290307 static bool IsSeperator ( char ch )
@@ -309,7 +326,6 @@ static ReadOnlySpan<char> TrimSeperator(ReadOnlySpan<char> data)
309326 return data . Slice ( idx ) ;
310327 }
311328
312-
313329 static ReadOnlySpan < char > FindPoint ( ReadOnlySpan < char > str , out PointF value , bool isRelative , in PointF relative )
314330 {
315331 str = FindScaler ( str , out float x ) ;
@@ -324,33 +340,42 @@ static ReadOnlySpan<char> FindPoint(ReadOnlySpan<char> str, out PointF value, bo
324340 return str ;
325341 }
326342
327- static ReadOnlySpan < char > FindPoints ( ReadOnlySpan < char > str , Span < PointF > value , int count , bool isRelative , in PointF relative )
328- {
329- for ( int i = 0 ; i < value . Length && i < count ; i ++ )
330- {
331- str = FindPoint ( str , out value [ i ] , isRelative , relative ) ;
332- }
333-
334- return str ;
335- }
336-
337343 static ReadOnlySpan < char > FindScaler ( ReadOnlySpan < char > str , out float scaler )
338344 {
339- str = str . TrimStart ( ) ;
345+ str = TrimSeperator ( str ) ;
340346 scaler = 0 ;
341347
342348 for ( var i = 0 ; i < str . Length ; i ++ )
343349 {
344- if ( IsSeperator ( str [ i ] ) )
350+ if ( IsSeperator ( str [ i ] ) || i == str . Length )
345351 {
346- scaler = float . Parse ( str . Slice ( 0 , i ) ) ;
352+ scaler = ParseFloat ( str . Slice ( 0 , i ) ) ;
347353 str = str . Slice ( i ) ;
354+ return str ;
348355 }
349356 }
350357
351- // we concumed eveything
352- return ReadOnlySpan < char > . Empty ;
358+ if ( str . Length > 0 )
359+ {
360+ scaler = ParseFloat ( str ) ;
361+ }
362+
363+ str = ReadOnlySpan < char > . Empty ;
364+ return str ;
365+ }
366+
367+ #if ! NETCOREAPP2_1_OR_GREATER
368+ static unsafe float ParseFloat ( ReadOnlySpan < char > str )
369+ {
370+ fixed ( char * p = str )
371+ {
372+ return float . Parse ( new string ( p , 0 , str . Length ) ) ;
373+ }
353374 }
375+ #else
376+ static float ParseFloat ( ReadOnlySpan < char > str )
377+ => float . Parse ( str ) ;
378+ #endif
354379 }
355380 }
356381}
0 commit comments