66using System . Collections . Generic ;
77using System . Linq ;
88using System . Numerics ;
9+ using System . Threading ;
910using SixLabors . ImageSharp . Memory ;
1011using SixLabors . ImageSharp . PixelFormats ;
1112
@@ -208,6 +209,7 @@ public Vector4 ColorAt(float distance)
208209 /// <summary>
209210 /// The path gradient brush applicator.
210211 /// </summary>
212+ /// <typeparam name="TPixel">The pixel format.</typeparam>
211213 private class PathGradientBrushApplicator < TPixel > : BrushApplicator < TPixel >
212214 where TPixel : unmanaged, IPixel < TPixel >
213215 {
@@ -219,14 +221,22 @@ private class PathGradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
219221
220222 private readonly float maxDistance ;
221223
222- private readonly int maxIntersections ;
223-
224224 private readonly IList < Edge > edges ;
225225
226226 private readonly TPixel centerPixel ;
227227
228228 private readonly TPixel transparentPixel ;
229229
230+ private readonly MemoryAllocator allocator ;
231+
232+ private readonly int maxIntersections ;
233+
234+ private readonly int scalineWidth ;
235+
236+ private readonly ThreadLocal < ThreadContextData > threadContextData ;
237+
238+ private bool isDisposed ;
239+
230240 /// <summary>
231241 /// Initializes a new instance of the <see cref="PathGradientBrushApplicator{TPixel}"/> class.
232242 /// </summary>
@@ -253,8 +263,14 @@ public PathGradientBrushApplicator(
253263 this . hasSpecialCenterColor = hasSpecialCenterColor ;
254264 this . centerPixel = centerColor . ToPixel < TPixel > ( ) ;
255265 this . maxDistance = points . Select ( p => ( Vector2 ) ( p - this . center ) ) . Max ( d => d . Length ( ) ) ;
256- this . maxIntersections = this . edges . Max ( e => e . Path . MaxIntersections ) ;
257266 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 ;
271+ this . threadContextData = new ThreadLocal < ThreadContextData > (
272+ ( ) => new ThreadContextData ( this . allocator , this . scalineWidth , this . maxIntersections ) ,
273+ true ) ;
258274 }
259275
260276 internal TPixel this [ int x , int y , Span < PointF > intersections , Span < PointOrientation > orientations ]
@@ -317,38 +333,55 @@ public PathGradientBrushApplicator(
317333 /// <inheritdoc />
318334 public override void Apply ( Span < float > scanline , int x , int y )
319335 {
320- MemoryAllocator memoryAllocator = this . Configuration . MemoryAllocator ;
321- using IMemoryOwner < float > amountBuffer = memoryAllocator . Allocate < float > ( scanline . Length ) ;
322- using IMemoryOwner < TPixel > overlayBuffer = memoryAllocator . Allocate < TPixel > ( scanline . Length ) ;
323- using IMemoryOwner < PointF > intersectionsBuffer = memoryAllocator . Allocate < PointF > ( this . maxIntersections ) ;
324- using IMemoryOwner < PointOrientation > orientationsBuffer = memoryAllocator . Allocate < PointOrientation > ( this . maxIntersections ) ;
325-
326- Span < float > amountSpan = amountBuffer . Memory . Span ;
327- Span < TPixel > overlaySpan = overlayBuffer . Memory . Span ;
328- Span < PointF > intersectionsSpan = intersectionsBuffer . Memory . Span ;
329- Span < PointOrientation > orientationsSpan = orientationsBuffer . Memory . Span ;
336+ ThreadContextData values = this . threadContextData . Value ;
337+ Span < float > amounts = values . AmountSpan . Slice ( 0 , scanline . Length ) ;
338+ Span < TPixel > overlays = values . OverlaySpan . Slice ( 0 , scanline . Length ) ;
339+ Span < PointF > intersections = values . IntersectionsSpan ;
340+ Span < PointOrientation > orientations = values . OrientationsSpan ;
330341 float blendPercentage = this . Options . BlendPercentage ;
331342
332343 // TODO: Remove bounds checks.
333344 if ( blendPercentage < 1 )
334345 {
335346 for ( int i = 0 ; i < scanline . Length ; i ++ )
336347 {
337- amountSpan [ i ] = scanline [ i ] * blendPercentage ;
338- overlaySpan [ i ] = this [ x + i , y , intersectionsSpan , orientationsSpan ] ;
348+ amounts [ i ] = scanline [ i ] * blendPercentage ;
349+ overlays [ i ] = this [ x + i , y , intersections , orientations ] ;
339350 }
340351 }
341352 else
342353 {
343354 for ( int i = 0 ; i < scanline . Length ; i ++ )
344355 {
345- amountSpan [ i ] = scanline [ i ] ;
346- overlaySpan [ i ] = this [ x + i , y , intersectionsSpan , orientationsSpan ] ;
356+ amounts [ i ] = scanline [ i ] ;
357+ overlays [ i ] = this [ x + i , y , intersections , orientations ] ;
347358 }
348359 }
349360
350361 Span < TPixel > destinationRow = this . Target . GetPixelRowSpan ( y ) . Slice ( x , scanline . Length ) ;
351- this . Blender . Blend ( this . Configuration , destinationRow , destinationRow , overlaySpan , amountSpan ) ;
362+ this . Blender . Blend ( this . Configuration , destinationRow , destinationRow , overlays , amounts ) ;
363+ }
364+
365+ protected override void Dispose ( bool disposing )
366+ {
367+ if ( this . isDisposed )
368+ {
369+ return ;
370+ }
371+
372+ base . Dispose ( disposing ) ;
373+
374+ if ( disposing )
375+ {
376+ foreach ( ThreadContextData data in this . threadContextData . Values )
377+ {
378+ data . Dispose ( ) ;
379+ }
380+
381+ this . threadContextData . Dispose ( ) ;
382+ }
383+
384+ this . isDisposed = true ;
352385 }
353386
354387 private ( Edge edge , Intersection ? info ) FindIntersection (
@@ -410,6 +443,43 @@ private static bool FindPointOnTriangle(PointF v1, PointF v2, PointF v3, PointF
410443 v = ( ( d00 * d21 ) - ( d01 * d20 ) ) / denominator ;
411444 return true ;
412445 }
446+
447+ private sealed class ThreadContextData : IDisposable
448+ {
449+ private bool isDisposed ;
450+ private readonly IMemoryOwner < float > amountBuffer ;
451+ private readonly IMemoryOwner < TPixel > overlayBuffer ;
452+ private readonly IMemoryOwner < PointF > intersectionsBuffer ;
453+ private readonly IMemoryOwner < PointOrientation > orientationsBuffer ;
454+
455+ public ThreadContextData ( MemoryAllocator memoryAllocator , int scanlineLength , int maxIntersections )
456+ {
457+ this . amountBuffer = memoryAllocator . Allocate < float > ( scanlineLength ) ;
458+ this . overlayBuffer = memoryAllocator . Allocate < TPixel > ( scanlineLength ) ;
459+ this . intersectionsBuffer = memoryAllocator . Allocate < PointF > ( maxIntersections ) ;
460+ this . orientationsBuffer = memoryAllocator . Allocate < PointOrientation > ( maxIntersections ) ;
461+ }
462+
463+ public Span < float > AmountSpan => this . amountBuffer . Memory . Span ;
464+
465+ public Span < TPixel > OverlaySpan => this . overlayBuffer . Memory . Span ;
466+
467+ public Span < PointF > IntersectionsSpan => this . intersectionsBuffer . Memory . Span ;
468+
469+ public Span < PointOrientation > OrientationsSpan => this . orientationsBuffer . Memory . Span ;
470+
471+ public void Dispose ( )
472+ {
473+ if ( ! this . isDisposed )
474+ {
475+ this . isDisposed = true ;
476+ this . amountBuffer . Dispose ( ) ;
477+ this . overlayBuffer . Dispose ( ) ;
478+ this . intersectionsBuffer . Dispose ( ) ;
479+ this . orientationsBuffer . Dispose ( ) ;
480+ }
481+ }
482+ }
413483 }
414484 }
415485}
0 commit comments