77using System . Linq ;
88using System . Numerics ;
99using System . Threading ;
10+ using SixLabors . ImageSharp . Drawing . Utilities ;
1011using SixLabors . ImageSharp . Memory ;
1112using SixLabors . ImageSharp . PixelFormats ;
1213
@@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Drawing.Processing
1718 /// </summary>
1819 public sealed class PathGradientBrush : IBrush
1920 {
20- private readonly IList < Edge > edges ;
21+ private readonly Edge [ ] edges ;
2122 private readonly Color centerColor ;
2223 private readonly bool hasSpecialCenterColor ;
2324
@@ -54,19 +55,16 @@ public PathGradientBrush(PointF[] points, Color[] colors)
5455
5556 int size = points . Length ;
5657
57- var lines = new ILineSegment [ size ] ;
58+ this . edges = new Edge [ points . Length ] ;
5859
59- for ( int i = 0 ; i < size ; i ++ )
60+ for ( int i = 0 ; i < points . Length ; i ++ )
6061 {
61- lines [ i ] = new LinearLineSegment ( points [ i % size ] , points [ ( i + 1 ) % size ] ) ;
62+ this . edges [ i ] = new Edge ( points [ i % size ] , points [ ( i + 1 ) % size ] , ColorAt ( i ) , ColorAt ( i + 1 ) ) ;
6263 }
6364
6465 this . centerColor = CalculateCenterColor ( colors ) ;
6566
6667 Color ColorAt ( int index ) => colors [ index % colors . Length ] ;
67-
68- this . edges = lines . Select ( s => new Path ( s ) )
69- . Select ( ( path , i ) => new Edge ( path , ColorAt ( i ) , ColorAt ( i + 1 ) ) ) . ToList ( ) ;
7068 }
7169
7270 /// <summary>
@@ -136,65 +134,29 @@ private class Edge
136134 {
137135 private readonly float length ;
138136
139- public Edge ( Path path , Color startColor , Color endColor )
137+ public Edge ( Vector2 start , Vector2 end , Color startColor , Color endColor )
140138 {
141- this . Path = path ;
142-
143- Vector2 [ ] points = path . LineSegments . SelectMany ( s => s . Flatten ( ) . ToArray ( ) ) . Select ( p => ( Vector2 ) p ) . ToArray ( ) ;
144-
145- this . Start = points [ 0 ] ;
139+ this . Start = start ;
140+ this . End = end ;
146141 this . StartColor = ( Vector4 ) startColor ;
147-
148- this . End = points . Last ( ) ;
149142 this . EndColor = ( Vector4 ) endColor ;
150143
151144 this . length = DistanceBetween ( this . End , this . Start ) ;
152145 }
153146
154- public Path Path { get ; }
147+ public Vector2 Start { get ; }
155148
156- public PointF Start { get ; }
149+ public Vector2 End { get ; }
157150
158151 public Vector4 StartColor { get ; }
159152
160- public PointF End { get ; }
161-
162153 public Vector4 EndColor { get ; }
163154
164- public Intersection ? FindIntersection (
165- PointF start ,
166- PointF end ,
167- Span < PointF > intersections ,
168- Span < PointOrientation > orientations )
169- {
170- int pathIntersections = this . Path . FindIntersections (
171- start ,
172- end ,
173- intersections . Slice ( 0 , this . Path . MaxIntersections ) ,
174- orientations . Slice ( 0 , this . Path . MaxIntersections ) ) ;
175-
176- if ( pathIntersections == 0 )
177- {
178- return null ;
179- }
180-
181- intersections = intersections . Slice ( 0 , pathIntersections ) ;
182-
183- PointF minPoint = intersections [ 0 ] ;
184- var min = new Intersection ( minPoint , ( ( Vector2 ) ( minPoint - start ) ) . LengthSquared ( ) ) ;
185- for ( int i = 1 ; i < intersections . Length ; i ++ )
186- {
187- PointF point = intersections [ i ] ;
188- var current = new Intersection ( point , ( ( Vector2 ) ( point - start ) ) . LengthSquared ( ) ) ;
189-
190- if ( min . Distance > current . Distance )
191- {
192- min = current ;
193- }
194- }
195-
196- return min ;
197- }
155+ public bool Intersect (
156+ Vector2 start ,
157+ Vector2 end ,
158+ ref Vector2 ip ) =>
159+ Utilities . Intersect . LineSegmentToLineSegmentIgnoreCollinear ( start , end , this . Start , this . End , ref ip ) ;
198160
199161 public Vector4 ColorAt ( float distance )
200162 {
@@ -213,7 +175,7 @@ public Vector4 ColorAt(float distance)
213175 private sealed class PathGradientBrushApplicator < TPixel > : BrushApplicator < TPixel >
214176 where TPixel : unmanaged, IPixel < TPixel >
215177 {
216- private readonly PointF center ;
178+ private readonly Vector2 center ;
217179
218180 private readonly Vector4 centerColor ;
219181
@@ -227,12 +189,6 @@ private sealed class PathGradientBrushApplicator<TPixel> : BrushApplicator<TPixe
227189
228190 private readonly TPixel transparentPixel ;
229191
230- private readonly MemoryAllocator allocator ;
231-
232- private readonly int maxIntersections ;
233-
234- private readonly int scalineWidth ;
235-
236192 private readonly ThreadLocal < ThreadContextData > threadContextData ;
237193
238194 private bool isDisposed ;
@@ -256,28 +212,24 @@ public PathGradientBrushApplicator(
256212 : base ( configuration , options , source )
257213 {
258214 this . edges = edges ;
259- PointF [ ] points = edges . Select ( s => s . Start ) . ToArray ( ) ;
215+ Vector2 [ ] points = edges . Select ( s => s . Start ) . ToArray ( ) ;
260216
261217 this . center = points . Aggregate ( ( p1 , p2 ) => p1 + p2 ) / edges . Count ;
262218 this . centerColor = ( Vector4 ) centerColor ;
263219 this . hasSpecialCenterColor = hasSpecialCenterColor ;
264220 this . centerPixel = centerColor . ToPixel < TPixel > ( ) ;
265221 this . maxDistance = points . Select ( p => ( Vector2 ) ( p - this . center ) ) . Max ( d => d . Length ( ) ) ;
266222 this . transparentPixel = Color . Transparent . ToPixel < TPixel > ( ) ;
267-
268- this . scalineWidth = source . Width ;
269- this . maxIntersections = this . edges . Max ( e => e . Path . MaxIntersections ) ;
270- this . allocator = configuration . MemoryAllocator ;
271223 this . threadContextData = new ThreadLocal < ThreadContextData > (
272- ( ) => new ThreadContextData ( this . allocator , this . scalineWidth , this . maxIntersections ) ,
224+ ( ) => new ThreadContextData ( configuration . MemoryAllocator , source . Width ) ,
273225 true ) ;
274226 }
275227
276- internal TPixel this [ int x , int y , Span < PointF > intersections , Span < PointOrientation > orientations ]
228+ internal TPixel this [ int x , int y ]
277229 {
278230 get
279231 {
280- var point = new PointF ( x , y ) ;
232+ var point = new Vector2 ( x , y ) ;
281233
282234 if ( point == this . center )
283235 {
@@ -307,17 +259,17 @@ public PathGradientBrushApplicator(
307259 }
308260
309261 var direction = Vector2 . Normalize ( point - this . center ) ;
310- PointF end = point + ( PointF ) ( direction * this . maxDistance ) ;
262+ Vector2 end = point + ( direction * this . maxDistance ) ;
311263
312- ( Edge edge , Intersection ? info ) = this . FindIntersection ( point , end , intersections , orientations ) ;
264+ ( Edge edge , Vector2 point ) ? isc = this . FindIntersection ( point , end ) ;
313265
314- if ( ! info . HasValue )
266+ if ( ! isc . HasValue )
315267 {
316268 return this . transparentPixel ;
317269 }
318270
319- PointF intersection = info . Value . Point ;
320- Vector4 edgeColor = edge . ColorAt ( intersection ) ;
271+ Vector2 intersection = isc . Value . point ;
272+ Vector4 edgeColor = isc . Value . edge . ColorAt ( intersection ) ;
321273
322274 float length = DistanceBetween ( intersection , this . center ) ;
323275 float ratio = length > 0 ? DistanceBetween ( intersection , point ) / length : 0 ;
@@ -336,8 +288,6 @@ public override void Apply(Span<float> scanline, int x, int y)
336288 ThreadContextData contextData = this . threadContextData . Value ;
337289 Span < float > amounts = contextData . AmountSpan . Slice ( 0 , scanline . Length ) ;
338290 Span < TPixel > overlays = contextData . OverlaySpan . Slice ( 0 , scanline . Length ) ;
339- Span < PointF > intersections = contextData . IntersectionsSpan ;
340- Span < PointOrientation > orientations = contextData . OrientationsSpan ;
341291 float blendPercentage = this . Options . BlendPercentage ;
342292
343293 // TODO: Remove bounds checks.
@@ -346,15 +296,15 @@ public override void Apply(Span<float> scanline, int x, int y)
346296 for ( int i = 0 ; i < scanline . Length ; i ++ )
347297 {
348298 amounts [ i ] = scanline [ i ] * blendPercentage ;
349- overlays [ i ] = this [ x + i , y , intersections , orientations ] ;
299+ overlays [ i ] = this [ x + i , y ] ;
350300 }
351301 }
352302 else
353303 {
354304 for ( int i = 0 ; i < scanline . Length ; i ++ )
355305 {
356306 amounts [ i ] = scanline [ i ] ;
357- overlays [ i ] = this [ x + i , y , intersections , orientations ] ;
307+ overlays [ i ] = this [ x + i , y ] ;
358308 }
359309 }
360310
@@ -385,33 +335,33 @@ protected override void Dispose(bool disposing)
385335 this . isDisposed = true ;
386336 }
387337
388- private ( Edge edge , Intersection ? info ) FindIntersection (
338+ private ( Edge edge , Vector2 point ) ? FindIntersection (
389339 PointF start ,
390- PointF end ,
391- Span < PointF > intersections ,
392- Span < PointOrientation > orientations )
340+ PointF end )
393341 {
394- ( Edge edge , Intersection ? info ) closest = default ;
395-
342+ Vector2 ip = default ;
343+ Vector2 closestIntersection = default ;
344+ Edge closestEdge = null ;
345+ float minDistance = float . MaxValue ;
396346 foreach ( Edge edge in this . edges )
397347 {
398- Intersection ? intersection = edge . FindIntersection ( start , end , intersections , orientations ) ;
399-
400- if ( ! intersection . HasValue )
348+ if ( ! edge . Intersect ( start , end , ref ip ) )
401349 {
402350 continue ;
403351 }
404352
405- if ( closest . info == null || closest . info . Value . Distance > intersection . Value . Distance )
353+ float d = Vector2 . DistanceSquared ( start , end ) ;
354+ if ( d < minDistance )
406355 {
407- closest = ( edge , intersection ) ;
356+ closestEdge = edge ;
357+ closestIntersection = ip ;
408358 }
409359 }
410360
411- return closest ;
361+ return closestEdge != null ? ( closestEdge , closestIntersection ) : ( ( Edge edge , Vector2 point ) ? ) null ;
412362 }
413363
414- private static bool FindPointOnTriangle ( PointF v1 , PointF v2 , PointF v3 , PointF point , out float u , out float v )
364+ private static bool FindPointOnTriangle ( Vector2 v1 , Vector2 v2 , Vector2 v3 , Vector2 point , out float u , out float v )
415365 {
416366 Vector2 e1 = v2 - v1 ;
417367 Vector2 e2 = v3 - v2 ;
@@ -450,34 +400,24 @@ private sealed class ThreadContextData : IDisposable
450400 private bool isDisposed ;
451401 private readonly IMemoryOwner < float > amountBuffer ;
452402 private readonly IMemoryOwner < TPixel > overlayBuffer ;
453- private readonly IMemoryOwner < PointF > intersectionsBuffer ;
454- private readonly IMemoryOwner < PointOrientation > orientationsBuffer ;
455403
456- public ThreadContextData ( MemoryAllocator allocator , int scanlineLength , int maxIntersections )
404+ public ThreadContextData ( MemoryAllocator allocator , int scanlineLength )
457405 {
458406 this . amountBuffer = allocator . Allocate < float > ( scanlineLength ) ;
459407 this . overlayBuffer = allocator . Allocate < TPixel > ( scanlineLength ) ;
460- this . intersectionsBuffer = allocator . Allocate < PointF > ( maxIntersections ) ;
461- this . orientationsBuffer = allocator . Allocate < PointOrientation > ( maxIntersections ) ;
462408 }
463409
464410 public Span < float > AmountSpan => this . amountBuffer . Memory . Span ;
465411
466412 public Span < TPixel > OverlaySpan => this . overlayBuffer . Memory . Span ;
467413
468- public Span < PointF > IntersectionsSpan => this . intersectionsBuffer . Memory . Span ;
469-
470- public Span < PointOrientation > OrientationsSpan => this . orientationsBuffer . Memory . Span ;
471-
472414 public void Dispose ( )
473415 {
474416 if ( ! this . isDisposed )
475417 {
476418 this . isDisposed = true ;
477419 this . amountBuffer . Dispose ( ) ;
478420 this . overlayBuffer . Dispose ( ) ;
479- this . intersectionsBuffer . Dispose ( ) ;
480- this . orientationsBuffer . Dispose ( ) ;
481421 }
482422 }
483423 }
0 commit comments