1+ using System ;
2+
3+ using SixLabors . ImageSharp . PixelFormats ;
4+ using SixLabors . Primitives ;
5+
6+ namespace SixLabors . ImageSharp . Processing . Drawing . Brushes . GradientBrushes
7+ {
8+ /// <summary>
9+ /// Gradient Brush with elliptic shape.
10+ /// The ellipse is defined by a center point,
11+ /// a point on the longest extension of the ellipse and
12+ /// the ratio between longest and shortest extension.
13+ /// </summary>
14+ /// <typeparam name="TPixel">The Pixel format that is used.</typeparam>
15+ public sealed class EllipticGradientBrush < TPixel > : GradientBrushBase < TPixel >
16+ where TPixel : struct , IPixel < TPixel >
17+ {
18+ private readonly Point center ;
19+
20+ private readonly Point referenceAxisEnd ;
21+
22+ private readonly float axisRatio ;
23+
24+ /// <inheritdoc cref="GradientBrushBase{TPixel}" />
25+ /// <param name="center">The center of the elliptical gradient and 0 for the color stops.</param>
26+ /// <param name="referenceAxisEnd">The end point of the reference axis of the ellipse.</param>
27+ /// <param name="axisRatio">
28+ /// The ratio of the axis widths.
29+ /// The second axis' is perpendicular to the reference axis and
30+ /// it's length is the reference axis' length multiplied by this factor.
31+ /// </param>
32+ /// <param name="repetitionMode">Defines how the colors of the gradients are repeated.</param>
33+ /// <param name="colorStops">the color stops as defined in base class.</param>
34+ public EllipticGradientBrush (
35+ Point center ,
36+ Point referenceAxisEnd ,
37+ float axisRatio ,
38+ GradientRepetitionMode repetitionMode ,
39+ params ColorStop < TPixel > [ ] colorStops )
40+ : base ( repetitionMode , colorStops )
41+ {
42+ this . center = center ;
43+ this . referenceAxisEnd = referenceAxisEnd ;
44+ this . axisRatio = axisRatio ;
45+ }
46+
47+ /// <inheritdoc cref="CreateApplicator" />
48+ public override BrushApplicator < TPixel > CreateApplicator (
49+ ImageFrame < TPixel > source ,
50+ RectangleF region ,
51+ GraphicsOptions options ) =>
52+ new RadialGradientBrushApplicator (
53+ source ,
54+ options ,
55+ this . center ,
56+ this . referenceAxisEnd ,
57+ this . axisRatio ,
58+ this . ColorStops ,
59+ this . RepetitionMode ) ;
60+
61+ /// <inheritdoc />
62+ private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
63+ {
64+ private readonly Point center ;
65+
66+ private readonly Point referenceAxisEnd ;
67+
68+ private readonly float axisRatio ;
69+
70+ private readonly double rotation ;
71+
72+ private readonly float referenceRadius ;
73+
74+ private readonly float secondRadius ;
75+
76+ private readonly float cosRotation ;
77+
78+ private readonly float sinRotation ;
79+
80+ private readonly float secondRadiusSquared ;
81+
82+ private readonly float referenceRadiusSquared ;
83+
84+ /// <summary>
85+ /// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
86+ /// </summary>
87+ /// <param name="target">The target image</param>
88+ /// <param name="options">The options</param>
89+ /// <param name="center">Center of the ellipse</param>
90+ /// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
91+ /// <param name="axisRatio">
92+ /// Ratio of the axis length's. Used to determine the length of the second axis,
93+ /// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param>
94+ /// <param name="colorStops">Definition of colors</param>
95+ /// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
96+ public RadialGradientBrushApplicator (
97+ ImageFrame < TPixel > target ,
98+ GraphicsOptions options ,
99+ Point center ,
100+ Point referenceAxisEnd ,
101+ float axisRatio ,
102+ ColorStop < TPixel > [ ] colorStops ,
103+ GradientRepetitionMode repetitionMode )
104+ : base ( target , options , colorStops , repetitionMode )
105+ {
106+ this . center = center ;
107+ this . referenceAxisEnd = referenceAxisEnd ;
108+ this . axisRatio = axisRatio ;
109+ this . rotation = this . AngleBetween (
110+ this . center ,
111+ new PointF ( this . center . X + 1 , this . center . Y ) ,
112+ this . referenceAxisEnd ) ;
113+ this . referenceRadius = this . DistanceBetween ( this . center , this . referenceAxisEnd ) ;
114+ this . secondRadius = this . referenceRadius * this . axisRatio ;
115+
116+ this . referenceRadiusSquared = this . referenceRadius * this . referenceRadius ;
117+ this . secondRadiusSquared = this . secondRadius * this . secondRadius ;
118+
119+ this . sinRotation = ( float ) Math . Sin ( this . rotation ) ;
120+ this . cosRotation = ( float ) Math . Cos ( this . rotation ) ;
121+ }
122+
123+ /// <inheritdoc />
124+ public override void Dispose ( )
125+ {
126+ }
127+
128+ /// <inheritdoc />
129+ protected override float PositionOnGradient ( int xt , int yt )
130+ {
131+ float x0 = xt - this . center . X ;
132+ float y0 = yt - this . center . Y ;
133+
134+ float x = ( x0 * this . cosRotation ) - ( y0 * this . sinRotation ) ;
135+ float y = ( x0 * this . sinRotation ) + ( y0 * this . cosRotation ) ;
136+
137+ float xSquared = x * x ;
138+ float ySquared = y * y ;
139+
140+ var inBoundaryChecker = ( xSquared / this . referenceRadiusSquared )
141+ + ( ySquared / this . secondRadiusSquared ) ;
142+
143+ return inBoundaryChecker ;
144+ }
145+
146+ private float AngleBetween ( PointF junction , PointF a , PointF b )
147+ {
148+ var vA = a - junction ;
149+ var vB = b - junction ;
150+ return ( float ) ( Math . Atan2 ( vB . Y , vB . X )
151+ - Math . Atan2 ( vA . Y , vA . X ) ) ;
152+ }
153+
154+ private float DistanceBetween (
155+ PointF p1 ,
156+ PointF p2 )
157+ {
158+ float dX = p1 . X - p2 . X ;
159+ float dXsquared = dX * dX ;
160+
161+ float dY = p1 . Y - p2 . Y ;
162+ float dYsquared = dY * dY ;
163+ return ( float ) Math . Sqrt ( dXsquared + dYsquared ) ;
164+ }
165+ }
166+ }
167+ }
0 commit comments