1use crate::{prelude::*, scalar, Color, Color4f, ColorSpace, Matrix, Point, Shader, TileMode};
2use sb::SkGradientShader_Interpolation;
3use skia_bindings as sb;
4
5#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
6#[repr(C)]
7pub struct Interpolation {
8 pub in_premul: interpolation::InPremul,
9 pub color_space: interpolation::ColorSpace,
10 pub hue_method: interpolation::HueMethod,
11}
12
13native_transmutable!(
14 SkGradientShader_Interpolation,
15 Interpolation,
16 interpolation_layout
17);
18
19pub mod interpolation {
20 pub type InPremul = skia_bindings::SkGradientShader_Interpolation_InPremul;
21 variant_name!(InPremul::Yes);
22
23 pub type ColorSpace = skia_bindings::SkGradientShader_Interpolation_ColorSpace;
24 variant_name!(ColorSpace::HSL);
25
26 pub type HueMethod = skia_bindings::SkGradientShader_Interpolation_HueMethod;
27 variant_name!(HueMethod::Shorter);
28}
29
30impl From<Flags> for Interpolation {
31 fn from(flags: Flags) -> Self {
32 let in_premul: SkGradientShader_Interpolation_InPremul = if flags.contains(Flags::INTERPOLATE_COLORS_IN_PREMUL) {
33 interpolation::InPremul::Yes
34 } else {
35 interpolation::InPremul::No
36 };
37 Self {
38 in_premul,
39 color_space: interpolation::ColorSpace::Destination,
40 hue_method: interpolation::HueMethod::Shorter,
41 }
42 }
43}
44
45impl Shader {
46 pub fn linear_gradient<'a>(
47 points: (impl Into<Point>, impl Into<Point>),
48 colors: impl Into<GradientShaderColors<'a>>,
49 pos: impl Into<Option<&'a [scalar]>>,
50 mode: TileMode,
51 flags: impl Into<Option<Flags>>,
52 local_matrix: impl Into<Option<&'a Matrix>>,
53 ) -> Option<Self> {
54 linear(points, colors, pos, mode, flags, local_matrix)
55 }
56
57 pub fn linear_gradient_with_interpolation<'a>(
58 points: (impl Into<Point>, impl Into<Point>),
59 colors: (&[Color4f], impl Into<Option<ColorSpace>>),
60 pos: impl Into<Option<&'a [scalar]>>,
61 mode: TileMode,
62 interpolation: impl Into<Interpolation>,
63 local_matrix: impl Into<Option<&'a Matrix>>,
64 ) -> Option<Self> {
65 linear_with_interpolation(points, colors, pos, mode, interpolation, local_matrix)
66 }
67
68 pub fn radial_gradient<'a>(
69 center: impl Into<Point>,
70 radius: scalar,
71 colors: impl Into<GradientShaderColors<'a>>,
72 pos: impl Into<Option<&'a [scalar]>>,
73 mode: TileMode,
74 flags: impl Into<Option<self::Flags>>,
75 local_matrix: impl Into<Option<&'a Matrix>>,
76 ) -> Option<Self> {
77 radial(center, radius, colors, pos, mode, flags, local_matrix)
78 }
79
80 #[allow(clippy::too_many_arguments)]
81 pub fn radial_gradient_with_interpolation<'a>(
82 center_and_radius: (impl Into<Point>, scalar),
83 colors: (&[Color4f], impl Into<Option<ColorSpace>>),
84 pos: impl Into<Option<&'a [scalar]>>,
85 mode: TileMode,
86 interpolation: impl Into<Interpolation>,
87 local_matrix: impl Into<Option<&'a Matrix>>,
88 ) -> Option<Shader> {
89 radial_with_interpolation(
90 center_and_radius,
91 colors,
92 pos,
93 mode,
94 interpolation,
95 local_matrix,
96 )
97 }
98
99 #[allow(clippy::too_many_arguments)]
100 pub fn two_point_conical_gradient<'a>(
101 start: impl Into<Point>,
102 start_radius: scalar,
103 end: impl Into<Point>,
104 end_radius: scalar,
105 colors: impl Into<GradientShaderColors<'a>>,
106 pos: impl Into<Option<&'a [scalar]>>,
107 mode: TileMode,
108 flags: impl Into<Option<self::Flags>>,
109 local_matrix: impl Into<Option<&'a Matrix>>,
110 ) -> Option<Self> {
111 two_point_conical(
112 start,
113 start_radius,
114 end,
115 end_radius,
116 colors,
117 pos,
118 mode,
119 flags,
120 local_matrix,
121 )
122 }
123
124 pub fn two_point_conical_gradient_with_interpolation<'a>(
125 start_and_radius: (impl Into<Point>, scalar),
126 end_and_radius: (impl Into<Point>, scalar),
127 colors: (&[Color4f], impl Into<Option<ColorSpace>>),
128 pos: impl Into<Option<&'a [scalar]>>,
129 mode: TileMode,
130 interpolation: impl Into<Interpolation>,
131 local_matrix: impl Into<Option<&'a Matrix>>,
132 ) -> Option<Shader> {
133 two_point_conical_with_interpolation(
134 start_and_radius,
135 end_and_radius,
136 colors,
137 pos,
138 mode,
139 interpolation,
140 local_matrix,
141 )
142 }
143
144 pub fn sweep_gradient<'a>(
145 center: impl Into<Point>,
146 colors: impl Into<GradientShaderColors<'a>>,
147 pos: impl Into<Option<&'a [scalar]>>,
148 mode: TileMode,
149 angles: impl Into<Option<(scalar, scalar)>>,
150 flags: impl Into<Option<self::Flags>>,
151 local_matrix: impl Into<Option<&'a Matrix>>,
152 ) -> Option<Self> {
153 sweep(center, colors, pos, mode, angles, flags, local_matrix)
154 }
155
156 pub fn sweep_gradient_with_interpolation<'a>(
157 center: impl Into<Point>,
158 colors: (&[Color4f], impl Into<Option<ColorSpace>>),
159 pos: impl Into<Option<&'a [scalar]>>,
160 mode: TileMode,
161 angles: impl Into<Option<(scalar, scalar)>>,
162 interpolation: impl Into<Interpolation>,
163 local_matrix: impl Into<Option<&'a Matrix>>,
164 ) -> Option<Shader> {
165 sweep_with_interpolation(
166 center,
167 colors,
168 pos,
169 mode,
170 angles,
171 interpolation,
172 local_matrix,
173 )
174 }
175}
176
177bitflags! {
178 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
179 pub struct Flags: u32 {
180 const INTERPOLATE_COLORS_IN_PREMUL = sb::SkGradientShader_Flags_kInterpolateColorsInPremul_Flag as _;
181 }
182}
183
184impl Default for self::Flags {
185 fn default() -> Self {
186 Self::empty()
187 }
188}
189
190pub fn linear<'a>(
191 points: (impl Into<Point>, impl Into<Point>),
192 colors: impl Into<GradientShaderColors<'a>>,
193 pos: impl Into<Option<&'a [scalar]>>,
194 mode: TileMode,
195 flags: impl Into<Option<Flags>>,
196 local_matrix: impl Into<Option<&'a Matrix>>,
197) -> Option<Shader> {
198 let colors = colors.into();
199 let pos = pos.into();
200 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
201 let flags = flags.into().unwrap_or_default();
202 let local_matrix = local_matrix.into();
203
204 match colors {
205 GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe {
206 let points = [points.0.into(), points.1.into()];
207 sb::C_SkGradientShader_MakeLinear(
208 points.native().as_ptr(),
209 colors.native().as_ptr(),
210 pos.as_ptr_or_null(),
211 colors.len().try_into().unwrap(),
212 mode,
213 flags.bits(),
214 local_matrix.native_ptr_or_null(),
215 )
216 }),
217 GradientShaderColors::ColorsInSpace(colors, color_space) => linear_with_interpolation(
218 points,
219 (colors, color_space),
220 pos,
221 mode,
222 flags,
223 local_matrix,
224 ),
225 }
226}
227
228pub fn linear_with_interpolation<'a>(
229 points: (impl Into<Point>, impl Into<Point>),
230 (colors: &[Color4f], color_space: impl Into>>): (&[Color4f], impl Into<Option<ColorSpace>>),
231 pos: impl Into<Option<&'a [scalar]>>,
232 mode: TileMode,
233 interpolation: impl Into<Interpolation>,
234 local_matrix: impl Into<Option<&'a Matrix>>,
235) -> Option<Shader> {
236 let points: [{unknown}; 2] = [points.0.into(), points.1.into()];
237 let color_space = color_space.into();
238 let pos = pos.into();
239 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
240 let interpolation = interpolation.into();
241 let local_matrix = local_matrix.into();
242 Shader::from_ptr(unsafe {
243 sb::C_SkGradientShader_MakeLinearWithInterpolation(
244 pts:points.native().as_ptr(),
245 colors:colors.native().as_ptr(),
246 colorSpace:color_space.into_ptr_or_null(),
247 pos:pos.as_ptr_or_null(),
248 count:colors.len().try_into().unwrap(),
249 mode,
250 interpolation:interpolation.native(),
251 localMatrix:local_matrix.native_ptr_or_null(),
252 )
253 })
254}
255
256pub fn radial<'a>(
257 center: impl Into<Point>,
258 radius: scalar,
259 colors: impl Into<GradientShaderColors<'a>>,
260 pos: impl Into<Option<&'a [scalar]>>,
261 mode: TileMode,
262 flags: impl Into<Option<self::Flags>>,
263 local_matrix: impl Into<Option<&'a Matrix>>,
264) -> Option<Shader> {
265 let colors = colors.into();
266 let center = center.into();
267 let pos = pos.into();
268 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
269 let flags = flags.into().unwrap_or_default();
270 let local_matrix = local_matrix.into();
271
272 match colors {
273 GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe {
274 sb::C_SkGradientShader_MakeRadial(
275 center.native(),
276 radius,
277 colors.native().as_ptr(),
278 pos.as_ptr_or_null(),
279 colors.len().try_into().unwrap(),
280 mode,
281 flags.bits(),
282 local_matrix.native_ptr_or_null(),
283 )
284 }),
285
286 GradientShaderColors::ColorsInSpace(colors, color_space) => radial_with_interpolation(
287 (center, radius),
288 (colors, color_space),
289 pos,
290 mode,
291 flags,
292 local_matrix,
293 ),
294 }
295}
296
297pub fn radial_with_interpolation<'a>(
298 (center: impl Into, radius: f32): (impl Into<Point>, scalar),
299 (colors: &[Color4f], color_space: impl Into>>): (&[Color4f], impl Into<Option<ColorSpace>>),
300 pos: impl Into<Option<&'a [scalar]>>,
301 mode: TileMode,
302 interpolation: impl Into<Interpolation>,
303 local_matrix: impl Into<Option<&'a Matrix>>,
304) -> Option<Shader> {
305 let color_space = color_space.into();
306 let center = center.into();
307 let pos = pos.into();
308 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
309 let interpolation = interpolation.into();
310 let local_matrix = local_matrix.into();
311
312 Shader::from_ptr(unsafe {
313 sb::C_SkGradientShader_MakeRadialWithInterpolation(
314 center:center.native(),
315 radius,
316 colors:colors.native().as_ptr(),
317 colorSpace:color_space.into_ptr_or_null(),
318 pos:pos.as_ptr_or_null(),
319 count:colors.len().try_into().unwrap(),
320 mode,
321 interpolation:interpolation.native(),
322 localMatrix:local_matrix.native_ptr_or_null(),
323 )
324 })
325}
326
327#[allow(clippy::too_many_arguments)]
328pub fn two_point_conical<'a>(
329 start: impl Into<Point>,
330 start_radius: scalar,
331 end: impl Into<Point>,
332 end_radius: scalar,
333 colors: impl Into<GradientShaderColors<'a>>,
334 pos: impl Into<Option<&'a [scalar]>>,
335 mode: TileMode,
336 flags: impl Into<Option<self::Flags>>,
337 local_matrix: impl Into<Option<&'a Matrix>>,
338) -> Option<Shader> {
339 let colors = colors.into();
340 let start = start.into();
341 let end = end.into();
342 let pos = pos.into();
343 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
344 let flags = flags.into().unwrap_or_default();
345 let local_matrix = local_matrix.into();
346
347 match colors {
348 GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe {
349 sb::C_SkGradientShader_MakeTwoPointConical(
350 start.native(),
351 start_radius,
352 end.native(),
353 end_radius,
354 colors.native().as_ptr(),
355 pos.as_ptr_or_null(),
356 colors.len().try_into().unwrap(),
357 mode,
358 flags.bits(),
359 local_matrix.native_ptr_or_null(),
360 )
361 }),
362
363 GradientShaderColors::ColorsInSpace(colors, color_space) => {
364 two_point_conical_with_interpolation(
365 (start, start_radius),
366 (end, end_radius),
367 (colors, color_space),
368 pos,
369 mode,
370 flags,
371 local_matrix,
372 )
373 }
374 }
375}
376
377pub fn two_point_conical_with_interpolation<'a>(
378 (start: impl Into, start_radius: f32): (impl Into<Point>, scalar),
379 (end: impl Into, end_radius: f32): (impl Into<Point>, scalar),
380 (colors: &[Color4f], color_space: impl Into>>): (&[Color4f], impl Into<Option<ColorSpace>>),
381 pos: impl Into<Option<&'a [scalar]>>,
382 mode: TileMode,
383 interpolation: impl Into<Interpolation>,
384 local_matrix: impl Into<Option<&'a Matrix>>,
385) -> Option<Shader> {
386 let start = start.into();
387 let end = end.into();
388 let pos = pos.into();
389 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
390 let local_matrix = local_matrix.into();
391
392 Shader::from_ptr(unsafe {
393 sb::C_SkGradientShader_MakeTwoPointConicalWithInterpolation(
394 start:start.native(),
395 startRadius:start_radius,
396 end:end.native(),
397 endRadius:end_radius,
398 colors:colors.native().as_ptr(),
399 colorSpace:color_space.into().into_ptr_or_null(),
400 pos:pos.as_ptr_or_null(),
401 count:colors.len().try_into().unwrap(),
402 mode,
403 interpolation:interpolation.into().native(),
404 localMatrix:local_matrix.native_ptr_or_null(),
405 )
406 })
407}
408
409pub fn sweep<'a>(
410 center: impl Into<Point>,
411 colors: impl Into<GradientShaderColors<'a>>,
412 pos: impl Into<Option<&'a [scalar]>>,
413 mode: TileMode,
414 angles: impl Into<Option<(scalar, scalar)>>,
415 flags: impl Into<Option<self::Flags>>,
416 local_matrix: impl Into<Option<&'a Matrix>>,
417) -> Option<Shader> {
418 let center = center.into();
419 let colors = colors.into();
420 let pos = pos.into();
421 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
422 let angles = angles.into();
423 let flags = flags.into().unwrap_or_default();
424 let local_matrix = local_matrix.into();
425
426 let (start_angle, end_angle) = (
427 angles.map(|a| a.0).unwrap_or(0.0),
428 angles.map(|a| a.1).unwrap_or(360.0),
429 );
430
431 match colors {
432 GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe {
433 sb::C_SkGradientShader_MakeSweep(
434 center.x,
435 center.y,
436 colors.native().as_ptr(),
437 pos.as_ptr_or_null(),
438 colors.len().try_into().unwrap(),
439 mode,
440 start_angle,
441 end_angle,
442 flags.bits(),
443 local_matrix.native_ptr_or_null(),
444 )
445 }),
446 GradientShaderColors::ColorsInSpace(colors, color_space) => sweep_with_interpolation(
447 center,
448 (colors, color_space),
449 pos,
450 mode,
451 angles,
452 flags,
453 local_matrix,
454 ),
455 }
456}
457
458pub fn sweep_with_interpolation<'a>(
459 center: impl Into<Point>,
460 (colors: &[Color4f], color_space: impl Into>>): (&[Color4f], impl Into<Option<ColorSpace>>),
461 pos: impl Into<Option<&'a [scalar]>>,
462 mode: TileMode,
463 angles: impl Into<Option<(scalar, scalar)>>,
464 interpolation: impl Into<Interpolation>,
465 local_matrix: impl Into<Option<&'a Matrix>>,
466) -> Option<Shader> {
467 let center = center.into();
468 let pos = pos.into();
469 assert!(pos.is_none() || (pos.unwrap().len() == colors.len()));
470 let angles = angles.into();
471 let local_matrix = local_matrix.into();
472
473 let (start_angle, end_angle) = (
474 angles.map(|a| a.0).unwrap_or(0.0),
475 angles.map(|a| a.1).unwrap_or(360.0),
476 );
477
478 Shader::from_ptr(unsafe {
479 sb::C_SkGradientShader_MakeSweepWithInterpolation(
480 center.x,
481 center.y,
482 colors.native().as_ptr(),
483 color_space.into().into_ptr_or_null(),
484 pos.as_ptr_or_null(),
485 colors.len().try_into().unwrap(),
486 mode,
487 start_angle,
488 end_angle,
489 interpolation.into().native(),
490 local_matrix.native_ptr_or_null(),
491 )
492 })
493}
494
495/// Type that represents either a slice of [`Color`], or a slice of [`Color4f`] and a color space.
496/// Whenever this type is expected, it's either possible to directly pass a `&[Color]` , or
497/// a tuple of type `(&[Color4f], &ColorSpace)`.
498#[derive(Debug)]
499pub enum GradientShaderColors<'a> {
500 Colors(&'a [Color]),
501 ColorsInSpace(&'a [Color4f], Option<ColorSpace>),
502}
503
504impl GradientShaderColors<'_> {
505 pub fn len(&self) -> usize {
506 match self {
507 GradientShaderColors::Colors(colors: &&[Color]) => colors.len(),
508 GradientShaderColors::ColorsInSpace(colors: &&[Color4f], _) => colors.len(),
509 }
510 }
511
512 // to keep clippy happy.
513 pub fn is_empty(&self) -> bool {
514 self.len() == 0
515 }
516}
517
518impl<'a> From<&'a [Color]> for GradientShaderColors<'a> {
519 fn from(colors: &'a [Color]) -> Self {
520 GradientShaderColors::<'a>::Colors(colors)
521 }
522}
523
524impl<'a> From<(&'a [Color4f], ColorSpace)> for GradientShaderColors<'a> {
525 fn from(c: (&'a [Color4f], ColorSpace)) -> Self {
526 GradientShaderColors::<'a>::ColorsInSpace(c.0, Some(c.1))
527 }
528}
529
530impl<'a> From<&'a [Color4f]> for GradientShaderColors<'a> {
531 fn from(c: &'a [Color4f]) -> Self {
532 GradientShaderColors::<'a>::ColorsInSpace(c, None)
533 }
534}
535