@@ -62,7 +62,7 @@ internal abstract class GradientBrushApplicator<TPixel> : BrushApplicator<TPixel
62
62
63
63
private readonly MemoryAllocator allocator ;
64
64
65
- private readonly int scalineWidth ;
65
+ private readonly int scanlineWidth ;
66
66
67
67
private readonly ThreadLocalBlenderBuffers < TPixel > blenderBuffers ;
68
68
@@ -84,13 +84,14 @@ protected GradientBrushApplicator(
84
84
GradientRepetitionMode repetitionMode )
85
85
: base ( configuration , options , target )
86
86
{
87
- // TODO: requires colorStops to be sorted by position.
88
- // Use Array.Sort with a custom comparer.
89
87
this . colorStops = colorStops ;
88
+
89
+ // Ensure the color-stop order is correct.
90
+ InsertionSort ( this . colorStops , ( x , y ) => x . Ratio . CompareTo ( y . Ratio ) ) ;
90
91
this . repetitionMode = repetitionMode ;
91
- this . scalineWidth = target . Width ;
92
+ this . scanlineWidth = target . Width ;
92
93
this . allocator = configuration . MemoryAllocator ;
93
- this . blenderBuffers = new ThreadLocalBlenderBuffers < TPixel > ( this . allocator , this . scalineWidth ) ;
94
+ this . blenderBuffers = new ThreadLocalBlenderBuffers < TPixel > ( this . allocator , this . scanlineWidth ) ;
94
95
}
95
96
96
97
internal TPixel this [ int x , int y ]
@@ -135,15 +136,16 @@ protected GradientBrushApplicator(
135
136
136
137
float onLocalGradient = ( positionOnCompleteGradient - from . Ratio ) / ( to . Ratio - from . Ratio ) ;
137
138
139
+ // TODO: This should use premultiplied vectors to avoid bad blends e.g. red -> brown <- green.
138
140
return new Color ( Vector4 . Lerp ( ( Vector4 ) from . Color , ( Vector4 ) to . Color , onLocalGradient ) ) . ToPixel < TPixel > ( ) ;
139
141
}
140
142
}
141
143
142
144
/// <inheritdoc />
143
145
public override void Apply ( Span < float > scanline , int x , int y )
144
146
{
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 ] ;
147
149
float blendPercentage = this . Options . BlendPercentage ;
148
150
149
151
// TODO: Remove bounds checks.
@@ -221,5 +223,29 @@ protected override void Dispose(bool disposing)
221
223
222
224
return ( localGradientFrom , localGradientTo ) ;
223
225
}
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
+ }
224
250
}
225
251
}
0 commit comments