@@ -191,14 +191,14 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
191191 switch ( op )
192192 {
193193 case 'M' :
194- svgPath = FindPoint ( svgPath , out point1 , relative , c ) ;
194+ svgPath = FindPoint ( svgPath , relative , c , out point1 ) ;
195195 builder . MoveTo ( point1 ) ;
196196 previousOp = '\0 ' ;
197197 op = 'L' ;
198198 c = point1 ;
199199 break ;
200200 case 'L' :
201- svgPath = FindPoint ( svgPath , out point1 , relative , c ) ;
201+ svgPath = FindPoint ( svgPath , relative , c , out point1 ) ;
202202 builder . LineTo ( point1 ) ;
203203 c = point1 ;
204204 break ;
@@ -223,16 +223,16 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
223223 c . Y = y ;
224224 break ;
225225 case 'C' :
226- svgPath = FindPoint ( svgPath , out point1 , relative , c ) ;
227- svgPath = FindPoint ( svgPath , out point2 , relative , c ) ;
228- svgPath = FindPoint ( svgPath , out point3 , relative , c ) ;
226+ svgPath = FindPoint ( svgPath , relative , c , out point1 ) ;
227+ svgPath = FindPoint ( svgPath , relative , c , out point2 ) ;
228+ svgPath = FindPoint ( svgPath , relative , c , out point3 ) ;
229229 builder . CubicBezierTo ( point1 , point2 , point3 ) ;
230230 lastc = point2 ;
231231 c = point3 ;
232232 break ;
233233 case 'S' :
234- svgPath = FindPoint ( svgPath , out point2 , relative , c ) ;
235- svgPath = FindPoint ( svgPath , out point3 , relative , c ) ;
234+ svgPath = FindPoint ( svgPath , relative , c , out point2 ) ;
235+ svgPath = FindPoint ( svgPath , relative , c , out point3 ) ;
236236 point1 = c ;
237237 if ( previousOp is 'C' or 'S' )
238238 {
@@ -245,14 +245,14 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
245245 c = point3 ;
246246 break ;
247247 case 'Q' : // Quadratic Bezier Curve
248- svgPath = FindPoint ( svgPath , out point1 , relative , c ) ;
249- svgPath = FindPoint ( svgPath , out point2 , relative , c ) ;
248+ svgPath = FindPoint ( svgPath , relative , c , out point1 ) ;
249+ svgPath = FindPoint ( svgPath , relative , c , out point2 ) ;
250250 builder . QuadraticBezierTo ( point1 , point2 ) ;
251251 lastc = point1 ;
252252 c = point2 ;
253253 break ;
254254 case 'T' :
255- svgPath = FindPoint ( svgPath , out point2 , relative , c ) ;
255+ svgPath = FindPoint ( svgPath , relative , c , out point2 ) ;
256256 point1 = c ;
257257 if ( previousOp is 'Q' or 'T' )
258258 {
@@ -265,20 +265,16 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
265265 c = point2 ;
266266 break ;
267267 case 'A' :
268- svgPath = FindScaler ( svgPath , out float radiiX ) ;
269- svgPath = TrimSeparator ( svgPath ) ;
270- svgPath = FindScaler ( svgPath , out float radiiY ) ;
271- svgPath = TrimSeparator ( svgPath ) ;
272- svgPath = FindScaler ( svgPath , out float angle ) ;
273- svgPath = TrimSeparator ( svgPath ) ;
274- svgPath = FindScaler ( svgPath , out float largeArc ) ;
275- svgPath = TrimSeparator ( svgPath ) ;
276- svgPath = FindScaler ( svgPath , out float sweep ) ;
277- svgPath = FindPoint ( svgPath , out PointF point , relative , c ) ;
278-
279- // TODO: Skia compares the input SVG with the chars not the length.
280- // Maybe we can do something with SpanAction<T>?
281- // if (svgPath.Length > 0)
268+ if ( TryFindScaler ( ref svgPath , out float radiiX )
269+ && TryTrimSeparator ( ref svgPath )
270+ && TryFindScaler ( ref svgPath , out float radiiY )
271+ && TryTrimSeparator ( ref svgPath )
272+ && TryFindScaler ( ref svgPath , out float angle )
273+ && TryTrimSeparator ( ref svgPath )
274+ && TryFindScaler ( ref svgPath , out float largeArc )
275+ && TryTrimSeparator ( ref svgPath )
276+ && TryFindScaler ( ref svgPath , out float sweep )
277+ && TryFindPoint ( ref svgPath , relative , c , out PointF point ) )
282278 {
283279 builder . ArcTo ( radiiX , radiiY , angle , largeArc == 1 , sweep == 1 , point ) ;
284280 c = point ;
@@ -290,8 +286,8 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
290286 c = first ;
291287 break ;
292288 case '~' :
293- svgPath = FindPoint ( svgPath , out point1 , relative , c ) ;
294- svgPath = FindPoint ( svgPath , out point2 , relative , c ) ;
289+ svgPath = FindPoint ( svgPath , relative , c , out point1 ) ;
290+ svgPath = FindPoint ( svgPath , relative , c , out point2 ) ;
295291 builder . MoveTo ( point1 ) ;
296292 builder . LineTo ( point2 ) ;
297293 break ;
@@ -309,77 +305,117 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
309305
310306 value = builder . Build ( ) ;
311307 return true ;
308+ }
312309
313- static bool IsSeparator ( char ch )
314- => char . IsWhiteSpace ( ch ) || ch == ',' ;
310+ private static bool TryTrimSeparator ( ref ReadOnlySpan < char > str )
311+ {
312+ ReadOnlySpan < char > result = TrimSeparator ( str ) ;
313+ if ( str . Slice ( str . Length - result . Length ) . StartsWith ( result ) )
314+ {
315+ str = result ;
316+ return true ;
317+ }
315318
316- static ReadOnlySpan < char > TrimSeparator ( ReadOnlySpan < char > data )
319+ return false ;
320+ }
321+
322+ private static bool TryFindScaler ( ref ReadOnlySpan < char > str , out float value )
323+ {
324+ ReadOnlySpan < char > result = FindScaler ( str , out float valueInner ) ;
325+ if ( str . Slice ( str . Length - result . Length ) . StartsWith ( result ) )
317326 {
318- if ( data . Length == 0 )
319- {
320- return data ;
321- }
327+ value = valueInner ;
328+ str = result ;
329+ return true ;
330+ }
322331
323- int idx = 0 ;
324- for ( ; idx < data . Length ; idx ++ )
325- {
326- if ( ! IsSeparator ( data [ idx ] ) )
327- {
328- break ;
329- }
330- }
332+ value = default ;
333+ return false ;
334+ }
335+
336+ private static bool TryFindPoint ( ref ReadOnlySpan < char > str , bool relative , PointF current , out PointF value )
337+ {
338+ ReadOnlySpan < char > result = FindPoint ( str , relative , current , out PointF valueInner ) ;
339+ if ( str . Slice ( str . Length - result . Length ) . StartsWith ( result ) )
340+ {
341+ value = valueInner ;
342+ str = result ;
343+ return true ;
344+ }
345+
346+ value = default ;
347+ return false ;
348+ }
331349
332- return data . Slice ( idx ) ;
350+ private static ReadOnlySpan < char > FindPoint ( ReadOnlySpan < char > str , bool isRelative , PointF relative , out PointF value )
351+ {
352+ str = FindScaler ( str , out float x ) ;
353+ str = FindScaler ( str , out float y ) ;
354+ if ( isRelative )
355+ {
356+ x += relative . X ;
357+ y += relative . Y ;
333358 }
334359
335- static ReadOnlySpan < char > FindPoint ( ReadOnlySpan < char > str , out PointF value , bool isRelative , PointF relative )
360+ value = new PointF ( x , y ) ;
361+ return str ;
362+ }
363+
364+ private static ReadOnlySpan < char > FindScaler ( ReadOnlySpan < char > str , out float scaler )
365+ {
366+ str = TrimSeparator ( str ) ;
367+ scaler = 0 ;
368+
369+ for ( int i = 0 ; i < str . Length ; i ++ )
336370 {
337- str = FindScaler ( str , out float x ) ;
338- str = FindScaler ( str , out float y ) ;
339- if ( isRelative )
371+ if ( IsSeparator ( str [ i ] ) || i == str . Length )
340372 {
341- x += relative . X ;
342- y += relative . Y ;
373+ scaler = ParseFloat ( str . Slice ( 0 , i ) ) ;
374+ return str . Slice ( i ) ;
343375 }
344-
345- value = new PointF ( x , y ) ;
346- return str ;
347376 }
348377
349- static ReadOnlySpan < char > FindScaler ( ReadOnlySpan < char > str , out float scaler )
378+ if ( str . Length > 0 )
350379 {
351- str = TrimSeparator ( str ) ;
352- scaler = 0 ;
380+ scaler = ParseFloat ( str ) ;
381+ }
353382
354- for ( int i = 0 ; i < str . Length ; i ++ )
355- {
356- if ( IsSeparator ( str [ i ] ) || i == str . Length )
357- {
358- scaler = ParseFloat ( str . Slice ( 0 , i ) ) ;
359- return str . Slice ( i ) ;
360- }
361- }
383+ return ReadOnlySpan < char > . Empty ;
384+ }
362385
363- if ( str . Length > 0 )
364- {
365- scaler = ParseFloat ( str ) ;
366- }
386+ private static bool IsSeparator ( char ch )
387+ => char . IsWhiteSpace ( ch ) || ch == ',' ;
367388
368- return ReadOnlySpan < char > . Empty ;
389+ private static ReadOnlySpan < char > TrimSeparator ( ReadOnlySpan < char > data )
390+ {
391+ if ( data . Length == 0 )
392+ {
393+ return data ;
369394 }
370395
371- #if ! NETCOREAPP2_1_OR_GREATER
372- static unsafe float ParseFloat ( ReadOnlySpan < char > str )
396+ int idx = 0 ;
397+ for ( ; idx < data . Length ; idx ++ )
373398 {
374- fixed ( char * p = str )
399+ if ( ! IsSeparator ( data [ idx ] ) )
375400 {
376- return float . Parse ( new string ( p , 0 , str . Length ) ) ;
401+ break ;
377402 }
378403 }
404+
405+ return data . Slice ( idx ) ;
406+ }
407+
408+ #if ! NETCOREAPP2_1_OR_GREATER
409+ private static unsafe float ParseFloat ( ReadOnlySpan < char > str )
410+ {
411+ fixed ( char * p = str )
412+ {
413+ return float . Parse ( new string ( p , 0 , str . Length ) ) ;
414+ }
415+ }
379416#else
380- static float ParseFloat ( ReadOnlySpan < char > str )
381- => float . Parse ( str ) ;
417+ private static float ParseFloat ( ReadOnlySpan < char > str )
418+ => float . Parse ( str ) ;
382419#endif
383- }
384420 }
385421}
0 commit comments