@@ -82,24 +82,37 @@ impl FocalData {
8282
8383#[ derive( Clone , PartialEq , Debug ) ]
8484enum GradientType {
85- Radial ,
86- Strip ,
85+ Radial { radius1 : f32 , radius2 : f32 } ,
86+ Strip {
87+ /// Radius of the first circle scaled by the distance between centers (r0 / d_center)
88+ scaled_r0 : f32 ,
89+ } ,
8790 Focal ( FocalData ) ,
8891}
8992
9093/// A 2-point conical gradient shader.
9194#[ derive( Clone , PartialEq , Debug ) ]
9295pub struct RadialGradient {
9396 pub ( crate ) base : Gradient ,
94- center1 : Point ,
95- center2 : Point ,
96- radius1 : f32 ,
97- radius2 : f32 ,
9897 gradient_type : GradientType ,
9998}
10099
101100impl RadialGradient {
102- /// Creates a new 2-point conical gradient shader.
101+ /// Creates a new two-point conical gradient shader.
102+ ///
103+ /// A two-point conical gradient (also known as a radial gradient)
104+ /// interpolates colors between two circles defined by their center points
105+ /// and radii.
106+ ///
107+ /// Returns `Shader::SolidColor` when:
108+ /// - `stops.len()` == 1
109+ ///
110+ /// Returns `None` when:
111+ /// - `stops` is empty
112+ /// - `start_radius` < 0 or `end_radius` < 0
113+ /// - `transform` is not invertible
114+ /// - The gradient is degenerate (both radii and centers are equal, except
115+ /// in specific pad mode cases)
103116 #[ allow( clippy:: new_ret_no_self) ]
104117 pub fn new (
105118 start_point : Point ,
@@ -114,14 +127,14 @@ impl RadialGradient {
114127 return None ;
115128 }
116129
117- transform. invert ( ) ?;
118-
119130 match stops. as_slice ( ) {
120131 [ ] => return None ,
121132 [ stop] => return Some ( Shader :: SolidColor ( stop. color ) ) ,
122133 _ => { }
123134 }
124135
136+ transform. invert ( ) ?;
137+
125138 let length = ( start_point - end_point) . length ( ) ;
126139 if !length. is_finite ( ) {
127140 return None ;
@@ -135,21 +148,18 @@ impl RadialGradient {
135148 // The interpolation region becomes an infinitely thin ring at the radius, so the
136149 // final gradient will be the first color repeated from p=0 to 1, and then a hard
137150 // stop switching to the last color at p=1.
138- let front = stops. first ( ) ?. clone ( ) ;
139- let back = stops. last ( ) ?. clone ( ) ;
151+ let start_color = stops. first ( ) ?. clone ( ) . color ;
152+ let end_color = stops. last ( ) ?. clone ( ) . color ;
140153 let mut new_stops = stops; // Reuse allocation from stops.
141154 new_stops. clear ( ) ;
142155 new_stops. extend_from_slice ( & [
143- GradientStop :: new ( 0.0 , front . color ) ,
144- GradientStop :: new ( 1.0 , front . color ) ,
145- GradientStop :: new ( 1.0 , back . color ) ,
156+ GradientStop :: new ( 0.0 , start_color ) ,
157+ GradientStop :: new ( 1.0 , start_color ) ,
158+ GradientStop :: new ( 1.0 , end_color ) ,
146159 ] ) ;
147160 // If the center positions are the same, then the gradient is the radial variant
148161 // of a 2 pt conical gradient, an actual radial gradient (startRadius == 0), or
149162 // it is fully degenerate (startRadius == endRadius).
150- let inv = end_radius. invert ( ) ;
151- let ts = Transform :: from_translate ( -start_point. x , -start_point. y )
152- . post_scale ( inv, inv) ;
153163 // We can treat this gradient as a simple radial, which is faster. If we got
154164 // here, we know that endRadius is not equal to 0, so this produces a meaningful
155165 // gradient
@@ -159,7 +169,6 @@ impl RadialGradient {
159169 new_stops,
160170 mode,
161171 transform,
162- ts,
163172 ) ;
164173 }
165174 // TODO: Consider making a degenerate gradient
@@ -171,10 +180,6 @@ impl RadialGradient {
171180 // is the radial variant of a 2 pt conical gradient,
172181 // an actual radial gradient (startRadius == 0),
173182 // or it is fully degenerate (startRadius == endRadius).
174- let inv = end_radius. invert ( ) ;
175- let ts =
176- Transform :: from_translate ( -start_point. x , -start_point. y ) . post_scale ( inv, inv) ;
177-
178183 // We can treat this gradient as a simple radial, which is faster. If we got here,
179184 // we know that endRadius is not equal to 0, so this produces a meaningful gradient.
180185 return Self :: new_radial_unchecked (
@@ -183,7 +188,6 @@ impl RadialGradient {
183188 stops,
184189 mode,
185190 transform,
186- ts,
187191 ) ;
188192 }
189193 }
@@ -199,40 +203,52 @@ impl RadialGradient {
199203 )
200204 }
201205
202- /// Create a simple radial shader.
206+ /// Creates a simple radial gradient shader without validation.
207+ ///
208+ /// This is an optimized path for creating radial gradients when the start radius is 0
209+ /// and the gradient is known to be valid. The function computes the points-to-unit
210+ /// transformation internally based on the center point and radius.
211+ ///
212+ /// # Parameters
213+ /// - `center`: The center point of the radial gradient
214+ /// - `radius`: The radius of the gradient (assumed to be > 0)
215+ /// - `stops`: Color stops for the gradient (assumed to have length >= 2)
216+ /// - `mode`: How the gradient extends beyond its bounds
217+ /// - `transform`: The gradient's transformation matrix (assumed to be invertible)
203218 fn new_radial_unchecked (
204- point : Point ,
219+ center : Point ,
205220 radius : f32 ,
206221 stops : Vec < GradientStop > ,
207222 mode : SpreadMode ,
208223 transform : Transform ,
209- points_to_unit : Transform ,
210224 ) -> Option < Shader < ' static > > {
225+ let inv = radius. invert ( ) ;
226+ let points_to_unit =
227+ Transform :: from_translate ( -center. x , -center. y ) . post_scale ( inv, inv) ;
228+
211229 Some ( Shader :: RadialGradient ( RadialGradient {
212230 base : Gradient :: new ( stops, mode, transform, points_to_unit) ,
213- center1 : point,
214- center2 : point,
215- radius1 : 0.0 ,
216- radius2 : radius,
217- gradient_type : GradientType :: Radial ,
231+ gradient_type : GradientType :: Radial {
232+ radius1 : 0.0 ,
233+ radius2 : radius,
234+ } ,
218235 } ) )
219236 }
220237
221238 pub ( crate ) fn push_stages ( & self , cs : ColorSpace , p : & mut RasterPipelineBuilder ) -> bool {
222239 let ( p0, p1) = match self . gradient_type {
223- GradientType :: Radial => {
224- if self . radius1 == 0.0 {
240+ GradientType :: Radial { radius1 , radius2 } => {
241+ if radius1 == 0.0 {
225242 ( 1.0 , 0.0 )
226243 } else {
227- let d_radius = self . radius2 - self . radius1 ;
244+ let d_radius = radius2 - radius1;
228245 // For concentric gradients: t = t * scale + bias
229- let p0 = self . radius1 . max ( self . radius2 ) / d_radius;
230- let p1 = -self . radius1 / d_radius;
246+ let p0 = radius1. max ( radius2) / d_radius;
247+ let p1 = -radius1 / d_radius;
231248 ( p0, p1)
232249 }
233250 }
234- GradientType :: Strip => {
235- let scaled_r0 = self . radius1 / ( self . center1 - self . center2 ) . length ( ) ;
251+ GradientType :: Strip { scaled_r0 } => {
236252 ( scaled_r0 * scaled_r0, 0.0 /*unused*/ )
237253 }
238254 GradientType :: Focal ( fd) => ( 1.0 / fd. r1 , fd. focal_x ) ,
@@ -249,15 +265,15 @@ impl RadialGradient {
249265 cs,
250266 & |p| {
251267 match self . gradient_type {
252- GradientType :: Radial => {
268+ GradientType :: Radial { .. } => {
253269 p. push ( pipeline:: Stage :: XYToRadius ) ;
254270 // Apply scale/bias to map t from [0, 1] based on r_max to proper t where
255271 // t=0 at r0 and t=1 at r1
256272 if ( p0, p1) != ( 1.0 , 0.0 ) {
257273 p. push ( pipeline:: Stage :: ApplyConcentricScaleBias ) ;
258274 }
259275 }
260- GradientType :: Strip => {
276+ GradientType :: Strip { .. } => {
261277 p. push ( pipeline:: Stage :: XYTo2PtConicalStrip ) ;
262278 p. push ( pipeline:: Stage :: Mask2PtConicalNan ) ;
263279 }
@@ -291,7 +307,7 @@ impl RadialGradient {
291307 }
292308 } ,
293309 & |p| match self . gradient_type {
294- GradientType :: Strip => p. push ( pipeline:: Stage :: ApplyVectorMask ) ,
310+ GradientType :: Strip { .. } => p. push ( pipeline:: Stage :: ApplyVectorMask ) ,
295311 GradientType :: Focal ( fd) if !fd. is_well_behaved ( ) => {
296312 p. push ( pipeline:: Stage :: ApplyVectorMask )
297313 }
@@ -323,11 +339,16 @@ fn create(
323339 // Concentric case: we can pretend we're radial (with a tiny twist).
324340 let scale = 1.0 / r0. max ( r1) ;
325341 gradient_matrix = Transform :: from_translate ( -c1. x , -c1. y ) . post_scale ( scale, scale) ;
326- gradient_type = GradientType :: Radial ;
342+ gradient_type = GradientType :: Radial {
343+ radius1 : r0,
344+ radius2 : r1,
345+ } ;
327346 } else {
328347 gradient_matrix = map_to_unit_x ( c0, c1) ?;
348+ let d_center = ( c0 - c1) . length ( ) ;
329349 gradient_type = if ( r0 - r1) . is_nearly_zero ( ) {
330- GradientType :: Strip
350+ let scaled_r0 = r0 / d_center;
351+ GradientType :: Strip { scaled_r0 }
331352 } else {
332353 GradientType :: Focal ( FocalData :: default ( ) )
333354 } ;
@@ -341,10 +362,6 @@ fn create(
341362
342363 Some ( Shader :: RadialGradient ( RadialGradient {
343364 base : Gradient :: new ( stops, mode, transform, gradient_matrix) ,
344- center1 : c0,
345- center2 : c1,
346- radius1 : r0,
347- radius2 : r1,
348365 gradient_type,
349366 } ) )
350367}
0 commit comments