@@ -15,6 +15,8 @@ use core::any::Any;
1515use core:: fmt;
1616use kurbo:: Affine ;
1717use peniko:: Brush ;
18+ use peniko:: InterpolationAlphaSpace ;
19+ use peniko:: color:: { ColorSpaceTag , HueDirection } ;
1820use skia_safe as sk;
1921use understory_imaging:: {
2022 DrawOp , ImageDesc , ImageId , ImagingBackend , ImagingOp , PaintDesc , PaintId , PathDesc , PathId ,
@@ -129,92 +131,121 @@ fn brush_to_paint(brush: &Brush, opacity: f32, paint_xf: Affine) -> sk::Paint {
129131 paint. set_color ( skia_safe:: Color :: from_argb ( a_scaled, r, g, b) ) ;
130132 }
131133 Brush :: Gradient ( grad) => {
132- // Map peniko gradients to Skia shaders. For now we implement
133- // linear gradients; other kinds fall back to the first stop .
134+ // Map peniko gradients to Skia gradient shaders, honoring
135+ // interpolation color space and hue direction .
134136 let stops = grad. stops . as_ref ( ) ;
135137 if stops. is_empty ( ) {
136138 paint. set_color ( skia_safe:: Color :: TRANSPARENT ) ;
137139 return paint;
138140 }
139141
140- let mut colors: Vec < sk:: Color > = Vec :: with_capacity ( stops. len ( ) ) ;
142+ let mut colors: Vec < sk:: Color4f > = Vec :: with_capacity ( stops. len ( ) ) ;
141143 let mut pos: Vec < f32 > = Vec :: with_capacity ( stops. len ( ) ) ;
142144
143145 for s in stops {
144- let color = s
145- . color
146- . to_alpha_color :: < peniko:: color:: Srgb > ( )
147- . multiply_alpha ( alpha_scale) ;
148- let rgba = color. to_rgba8 ( ) ;
149- let ( r, g, b, a) = ( rgba. r , rgba. g , rgba. b , rgba. a ) ;
150- colors. push ( skia_safe:: Color :: from_argb ( a, r, g, b) ) ;
146+ // Use the dynamic color components directly and apply additional
147+ // opacity as an alpha multiplier.
148+ let comps = s. color . components ;
149+ let a = comps[ 3 ] * alpha_scale;
150+ colors. push ( skia_safe:: Color4f :: new ( comps[ 0 ] , comps[ 1 ] , comps[ 2 ] , a) ) ;
151151 pos. push ( s. offset . clamp ( 0.0 , 1.0 ) ) ;
152152 }
153153
154- let tile_mode = match grad. extend {
155- peniko:: Extend :: Pad => skia_safe:: TileMode :: Clamp ,
156- peniko:: Extend :: Repeat => skia_safe:: TileMode :: Repeat ,
157- peniko:: Extend :: Reflect => skia_safe:: TileMode :: Mirror ,
158- } ;
154+ let tile_mode = tile_mode_from_extend ( grad. extend ) ;
159155
160156 let local = affine_to_matrix ( paint_xf) ;
161157
158+ let interpolation = skia_safe:: gradient_shader:: Interpolation {
159+ color_space : gradient_shader_cs_from_cs_tag ( grad. interpolation_cs ) ,
160+ in_premul : match grad. interpolation_alpha_space {
161+ InterpolationAlphaSpace :: Premultiplied => {
162+ skia_safe:: gradient_shader:: interpolation:: InPremul :: Yes
163+ }
164+ InterpolationAlphaSpace :: Unpremultiplied => {
165+ skia_safe:: gradient_shader:: interpolation:: InPremul :: No
166+ }
167+ } ,
168+ hue_method : gradient_shader_hue_method_from_hue_direction ( grad. hue_direction ) ,
169+ } ;
170+
162171 match grad. kind {
163172 peniko:: GradientKind :: Linear ( line) => {
164173 let p0 = sk:: Point :: new ( f64_to_f32 ( line. start . x ) , f64_to_f32 ( line. start . y ) ) ;
165174 let p1 = sk:: Point :: new ( f64_to_f32 ( line. end . x ) , f64_to_f32 ( line. end . y ) ) ;
166- if let Some ( shader) = sk:: Shader :: linear_gradient (
175+ if let Some ( shader) = sk:: Shader :: linear_gradient_with_interpolation (
167176 ( p0, p1) ,
168- colors. as_slice ( ) ,
169- Some ( pos. as_slice ( ) ) ,
177+ ( & colors[ .. ] , None ) ,
178+ & pos[ .. ] ,
170179 tile_mode,
171- None ,
172- Some ( & local) ,
180+ interpolation ,
181+ & Some ( local) ,
173182 ) {
174183 paint. set_shader ( shader) ;
175184 }
176185 }
177186 peniko:: GradientKind :: Radial ( rad) => {
178- let center = sk:: Point :: new (
187+ let start_center = sk:: Point :: new (
179188 f64_to_f32 ( rad. start_center . x ) ,
180189 f64_to_f32 ( rad. start_center . y ) ,
181190 ) ;
182- let radius = rad. end_radius ;
183- if let Some ( shader) = sk:: Shader :: radial_gradient (
184- center,
185- radius,
186- colors. as_slice ( ) ,
187- Some ( pos. as_slice ( ) ) ,
188- tile_mode,
189- None ,
190- Some ( & local) ,
191- ) {
191+ let start_radius = rad. start_radius ;
192+ let end_center =
193+ sk:: Point :: new ( f64_to_f32 ( rad. end_center . x ) , f64_to_f32 ( rad. end_center . y ) ) ;
194+ let end_radius = rad. end_radius ;
195+
196+ let shader = if start_center == end_center && start_radius == end_radius {
197+ sk:: Shader :: radial_gradient_with_interpolation (
198+ ( start_center, start_radius) ,
199+ ( & colors[ ..] , None ) ,
200+ & pos[ ..] ,
201+ tile_mode,
202+ interpolation,
203+ & Some ( local) ,
204+ )
205+ } else {
206+ sk:: Shader :: two_point_conical_gradient_with_interpolation (
207+ ( start_center, start_radius) ,
208+ ( end_center, end_radius) ,
209+ ( & colors[ ..] , None ) ,
210+ & pos[ ..] ,
211+ tile_mode,
212+ interpolation,
213+ & Some ( local) ,
214+ )
215+ } ;
216+
217+ if let Some ( shader) = shader {
192218 paint. set_shader ( shader) ;
193219 }
194220 }
195221 peniko:: GradientKind :: Sweep ( sweep) => {
196222 let center =
197223 sk:: Point :: new ( f64_to_f32 ( sweep. center . x ) , f64_to_f32 ( sweep. center . y ) ) ;
198224 let angles = ( sweep. start_angle . to_degrees ( ) , sweep. end_angle . to_degrees ( ) ) ;
199- if let Some ( shader) = sk:: Shader :: sweep_gradient (
225+ if let Some ( shader) = sk:: Shader :: sweep_gradient_with_interpolation (
200226 center,
201- colors. as_slice ( ) ,
202- Some ( pos. as_slice ( ) ) ,
227+ ( & colors[ .. ] , None ) ,
228+ & pos[ .. ] ,
203229 tile_mode,
204- Some ( angles) ,
205- None ,
206- Some ( & local) ,
230+ angles,
231+ interpolation ,
232+ & Some ( local) ,
207233 ) {
208234 paint. set_shader ( shader) ;
209235 }
210236 }
211237 }
212238
213239 // If shader creation failed for any reason, fall back to the last stop.
214- if paint. shader ( ) . is_none ( )
215- && let Some ( last) = colors. last ( )
216- {
217- paint. set_color ( * last) ;
240+ if paint. shader ( ) . is_none ( ) {
241+ if let Some ( last_stop) = stops. last ( ) {
242+ let color = last_stop
243+ . color
244+ . to_alpha_color :: < peniko:: color:: Srgb > ( )
245+ . multiply_alpha ( alpha_scale) ;
246+ let rgba = color. to_rgba8 ( ) ;
247+ paint. set_color ( skia_safe:: Color :: from_argb ( rgba. a , rgba. r , rgba. g , rgba. b ) ) ;
248+ }
218249 }
219250 }
220251 // Image brushes are not yet mapped; fall back to solid black with opacity.
@@ -231,6 +262,50 @@ fn brush_to_paint(brush: &Brush, opacity: f32, paint_xf: Affine) -> sk::Paint {
231262 paint
232263}
233264
265+ fn tile_mode_from_extend ( extend : peniko:: Extend ) -> sk:: TileMode {
266+ match extend {
267+ peniko:: Extend :: Pad => sk:: TileMode :: Clamp ,
268+ peniko:: Extend :: Repeat => sk:: TileMode :: Repeat ,
269+ peniko:: Extend :: Reflect => sk:: TileMode :: Mirror ,
270+ }
271+ }
272+
273+ fn gradient_shader_cs_from_cs_tag (
274+ color_space : ColorSpaceTag ,
275+ ) -> skia_safe:: gradient_shader:: interpolation:: ColorSpace {
276+ use skia_safe:: gradient_shader:: interpolation:: ColorSpace as SkGradientShaderColorSpace ;
277+
278+ match color_space {
279+ ColorSpaceTag :: Srgb => SkGradientShaderColorSpace :: SRGB ,
280+ ColorSpaceTag :: LinearSrgb => SkGradientShaderColorSpace :: SRGBLinear ,
281+ ColorSpaceTag :: Lab => SkGradientShaderColorSpace :: Lab ,
282+ ColorSpaceTag :: Lch => SkGradientShaderColorSpace :: LCH ,
283+ ColorSpaceTag :: Hsl => SkGradientShaderColorSpace :: HSL ,
284+ ColorSpaceTag :: Hwb => SkGradientShaderColorSpace :: HWB ,
285+ ColorSpaceTag :: Oklab => SkGradientShaderColorSpace :: OKLab ,
286+ ColorSpaceTag :: Oklch => SkGradientShaderColorSpace :: OKLCH ,
287+ ColorSpaceTag :: DisplayP3 => SkGradientShaderColorSpace :: DisplayP3 ,
288+ ColorSpaceTag :: A98Rgb => SkGradientShaderColorSpace :: A98RGB ,
289+ ColorSpaceTag :: ProphotoRgb => SkGradientShaderColorSpace :: ProphotoRGB ,
290+ ColorSpaceTag :: Rec2020 => SkGradientShaderColorSpace :: Rec2020 ,
291+ _ => SkGradientShaderColorSpace :: SRGB ,
292+ }
293+ }
294+
295+ fn gradient_shader_hue_method_from_hue_direction (
296+ direction : HueDirection ,
297+ ) -> skia_safe:: gradient_shader:: interpolation:: HueMethod {
298+ use skia_safe:: gradient_shader:: interpolation:: HueMethod as SkGradientShaderHueMethod ;
299+
300+ match direction {
301+ HueDirection :: Shorter => SkGradientShaderHueMethod :: Shorter ,
302+ HueDirection :: Longer => SkGradientShaderHueMethod :: Longer ,
303+ HueDirection :: Increasing => SkGradientShaderHueMethod :: Increasing ,
304+ HueDirection :: Decreasing => SkGradientShaderHueMethod :: Decreasing ,
305+ _ => SkGradientShaderHueMethod :: Shorter ,
306+ }
307+ }
308+
234309fn map_blend_mode ( mode : & understory_imaging:: BlendMode ) -> sk:: BlendMode {
235310 use peniko:: { Compose , Mix } ;
236311
0 commit comments