11using System ;
2- using System . Diagnostics ;
3- using System . Numerics ;
42
53using SixLabors . ImageSharp . PixelFormats ;
6- using SixLabors . ImageSharp . PixelFormats . PixelBlenders ;
74using SixLabors . Primitives ;
85
9- namespace SixLabors . ImageSharp . Processing . Drawing . Brushes
6+ namespace SixLabors . ImageSharp . Processing . Drawing . Brushes . GradientBrushes
107{
118 /// <summary>
12- /// Provides an implementation of a brush for painting gradients within areas.
9+ /// Provides an implementation of a brush for painting linear gradients within areas.
1310 /// Supported right now:
1411 /// - a set of colors in relative distances to each other.
15- /// - two points to gradient along.
1612 /// </summary>
1713 /// <typeparam name="TPixel">The pixel format</typeparam>
18- public class LinearGradientBrush < TPixel > : IBrush < TPixel >
14+ public class LinearGradientBrush < TPixel > : AbstractGradientBrush < TPixel >
1915 where TPixel : struct , IPixel < TPixel >
2016 {
2117 private readonly Point p1 ;
2218
2319 private readonly Point p2 ;
2420
25- private readonly ColorStop [ ] colorStops ;
26-
2721 /// <summary>
2822 /// Initializes a new instance of the <see cref="LinearGradientBrush{TPixel}"/> class.
2923 /// </summary>
3024 /// <param name="p1">Start point</param>
3125 /// <param name="p2">End point</param>
32- /// <param name="colorStops">
33- /// A set of color keys and where they are.
34- /// The double should be in range [0..1] and is relative between p1 and p2.
35- /// TODO: what about the [0..1] restriction? is it necessary? If so, it should be checked, if not, it should be explained what happens for greater/smaller values.
36- /// </param>
37- public LinearGradientBrush ( Point p1 , Point p2 , params ColorStop [ ] colorStops )
26+ /// <param name="colorStops"><inheritdoc /></param>
27+ public LinearGradientBrush ( Point p1 , Point p2 , params ColorStop < TPixel > [ ] colorStops )
28+ : base ( colorStops )
3829 {
3930 this . p1 = p1 ;
4031 this . p2 = p2 ;
41- this . colorStops = colorStops ;
4232 }
4333
4434 /// <inheritdoc />
45- public BrushApplicator < TPixel > CreateApplicator ( ImageFrame < TPixel > source , RectangleF region , GraphicsOptions options )
46- => new LinearGradientBrushApplicator ( source , this . p1 , this . p2 , this . colorStops , region , options ) ;
47-
48- /// <summary>
49- /// A struct that defines a single color stop.
50- /// </summary>
51- [ DebuggerDisplay ( "ColorStop({Ratio} -> {Color}" ) ]
52- public struct ColorStop
53- {
54- /// <summary>
55- /// Initializes a new instance of the <see cref="ColorStop" /> struct.
56- /// </summary>
57- /// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the <see cref="LinearGradientBrush{TPixel}"/>.</param>
58- /// <param name="color">What color should be used at that point?</param>
59- public ColorStop ( float ratio , TPixel color )
60- {
61- this . Ratio = ratio ;
62- this . Color = color ;
63- }
64-
65- /// <summary>
66- /// Gets the point along the defined <see cref="LinearGradientBrush{TPixel}" /> gradient axis.
67- /// </summary>
68- public float Ratio { get ; }
69-
70- /// <summary>
71- /// Gets the color to be used.
72- /// </summary>
73- public TPixel Color { get ; }
74- }
35+ public override BrushApplicator < TPixel > CreateApplicator ( ImageFrame < TPixel > source , RectangleF region , GraphicsOptions options )
36+ => new LinearGradientBrushApplicator ( source , this . p1 , this . p2 , this . ColorStops , region , options ) ;
7537
7638 /// <summary>
7739 /// The linear gradient brush applicator.
7840 /// </summary>
79- private class LinearGradientBrushApplicator : BrushApplicator < TPixel >
41+ private class LinearGradientBrushApplicator : AbstractGradientBrushApplicator
8042 {
8143 private readonly Point start ;
8244
8345 private readonly Point end ;
8446
85- private readonly ColorStop [ ] colorStops ;
86-
8747 /// <summary>
8848 /// the vector along the gradient, x component
8949 /// </summary>
@@ -127,14 +87,13 @@ public LinearGradientBrushApplicator(
12787 ImageFrame < TPixel > source ,
12888 Point start ,
12989 Point end ,
130- ColorStop [ ] colorStops ,
131- RectangleF region , // TODO: use region, compare with other Brushes for reference.
90+ ColorStop < TPixel > [ ] colorStops ,
91+ RectangleF region ,
13292 GraphicsOptions options )
133- : base ( source , options )
93+ : base ( source , options , colorStops , region )
13494 {
13595 this . start = start ;
13696 this . end = end ;
137- this . colorStops = colorStops ; // TODO: requires colorStops to be sorted by Item1!
13897
13998 // the along vector:
14099 this . alongX = this . end . X - this . start . X ;
@@ -149,61 +108,7 @@ public LinearGradientBrushApplicator(
149108 this . length = ( float ) Math . Sqrt ( this . alongsSquared ) ;
150109 }
151110
152- /// <summary>
153- /// Gets the color for a single pixel
154- /// </summary>
155- /// <param name="x">The x coordinate.</param>
156- /// <param name="y">The y coordinate.</param>
157- internal override TPixel this [ int x , int y ]
158- {
159- get
160- {
161- // the following formula is the result of the linear equation system that forms the vector.
162- // TODO: this formula should be abstracted as it's the only difference between linear and radial gradient!
163- float onCompleteGradient = this . RatioOnGradient ( x , y ) ;
164-
165- var localGradientFrom = this . colorStops [ 0 ] ;
166- ColorStop localGradientTo = default ;
167-
168- // TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
169- foreach ( var colorStop in this . colorStops )
170- {
171- localGradientTo = colorStop ;
172-
173- if ( colorStop . Ratio > onCompleteGradient )
174- {
175- // we're done here, so break it!
176- break ;
177- }
178-
179- localGradientFrom = localGradientTo ;
180- }
181-
182- TPixel resultColor = default ;
183- if ( localGradientFrom . Color . Equals ( localGradientTo . Color ) )
184- {
185- resultColor = localGradientFrom . Color ;
186- }
187- else
188- {
189- var fromAsVector = localGradientFrom . Color . ToVector4 ( ) ;
190- var toAsVector = localGradientTo . Color . ToVector4 ( ) ;
191- float onLocalGradient = ( onCompleteGradient - localGradientFrom . Ratio ) / localGradientTo . Ratio ; // TODO:
192-
193- Vector4 result = PorterDuffFunctions . Normal (
194- fromAsVector ,
195- toAsVector ,
196- onLocalGradient ) ;
197-
198- // TODO: when resultColor is a struct, what does PackFromVector4 do here?
199- resultColor . PackFromVector4 ( result ) ;
200- }
201-
202- return resultColor ;
203- }
204- }
205-
206- private float RatioOnGradient ( int x , int y )
111+ protected override float PositionOnGradient ( int x , int y )
207112 {
208113 if ( this . acrossX == 0 )
209114 {
@@ -234,6 +139,10 @@ private float RatioOnGradient(int x, int y)
234139 }
235140 }
236141
142+ public override void Dispose ( )
143+ {
144+ }
145+
237146 internal override void Apply ( Span < float > scanline , int x , int y )
238147 {
239148 base . Apply ( scanline , x , y ) ;
@@ -257,11 +166,6 @@ internal override void Apply(Span<float> scanline, int x, int y)
257166 // this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
258167 // }
259168 }
260-
261- /// <inheritdoc />
262- public override void Dispose ( )
263- {
264- }
265169 }
266170 }
267171}
0 commit comments