@@ -62,7 +62,7 @@ internal abstract class GradientBrushApplicator<TPixel> : BrushApplicator<TPixel
6262
6363 private readonly MemoryAllocator allocator ;
6464
65- private readonly int scalineWidth ;
65+ private readonly int scanlineWidth ;
6666
6767 private readonly ThreadLocalBlenderBuffers < TPixel > blenderBuffers ;
6868
@@ -84,13 +84,14 @@ protected GradientBrushApplicator(
8484 GradientRepetitionMode repetitionMode )
8585 : base ( configuration , options , target )
8686 {
87- // TODO: requires colorStops to be sorted by position.
88- // Use Array.Sort with a custom comparer.
8987 this . colorStops = colorStops ;
88+
89+ // Ensure the color-stop order is correct.
90+ InsertionSort ( this . colorStops , ( x , y ) => x . Ratio . CompareTo ( y . Ratio ) ) ;
9091 this . repetitionMode = repetitionMode ;
91- this . scalineWidth = target . Width ;
92+ this . scanlineWidth = target . Width ;
9293 this . allocator = configuration . MemoryAllocator ;
93- this . blenderBuffers = new ThreadLocalBlenderBuffers < TPixel > ( this . allocator , this . scalineWidth ) ;
94+ this . blenderBuffers = new ThreadLocalBlenderBuffers < TPixel > ( this . allocator , this . scanlineWidth ) ;
9495 }
9596
9697 internal TPixel this [ int x , int y ]
@@ -135,15 +136,16 @@ protected GradientBrushApplicator(
135136
136137 float onLocalGradient = ( positionOnCompleteGradient - from . Ratio ) / ( to . Ratio - from . Ratio ) ;
137138
139+ // TODO: This should use premultiplied vectors to avoid bad blends e.g. red -> brown <- green.
138140 return new Color ( Vector4 . Lerp ( ( Vector4 ) from . Color , ( Vector4 ) to . Color , onLocalGradient ) ) . ToPixel < TPixel > ( ) ;
139141 }
140142 }
141143
142144 /// <inheritdoc />
143145 public override void Apply ( Span < float > scanline , int x , int y )
144146 {
145- Span < float > amounts = this . blenderBuffers . AmountSpan . Slice ( 0 , scanline . Length ) ;
146- Span < TPixel > overlays = this . blenderBuffers . OverlaySpan . Slice ( 0 , scanline . Length ) ;
147+ Span < float > amounts = this . blenderBuffers . AmountSpan [ .. scanline . Length ] ;
148+ Span < TPixel > overlays = this . blenderBuffers . OverlaySpan [ .. scanline . Length ] ;
147149 float blendPercentage = this . Options . BlendPercentage ;
148150
149151 // TODO: Remove bounds checks.
@@ -221,5 +223,29 @@ protected override void Dispose(bool disposing)
221223
222224 return ( localGradientFrom , localGradientTo ) ;
223225 }
226+
227+ /// <summary>
228+ /// Provides a stable sorting algorithm for the given array.
229+ /// <see cref="Array.Sort(Array, System.Collections.IComparer?)"/> is not stable.
230+ /// </summary>
231+ /// <typeparam name="T">The type of element to sort.</typeparam>
232+ /// <param name="collection">The array to sort.</param>
233+ /// <param name="comparison">The comparison delegate.</param>
234+ private static void InsertionSort < T > ( T [ ] collection , Comparison < T > comparison )
235+ {
236+ int count = collection . Length ;
237+ for ( int j = 1 ; j < count ; j ++ )
238+ {
239+ T key = collection [ j ] ;
240+
241+ int i = j - 1 ;
242+ for ( ; i >= 0 && comparison ( collection [ i ] , key ) > 0 ; i -- )
243+ {
244+ collection [ i + 1 ] = collection [ i ] ;
245+ }
246+
247+ collection [ i + 1 ] = key ;
248+ }
249+ }
224250 }
225251}
0 commit comments