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