skia_safe/effects/
gradient_shader.rs

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/// Type that represents either a slice of [`Color`], or a slice of [`Color4f`] and a color space.
492/// Whenever this type is expected, it's either possible to directly pass a `&[Color]` , or
493/// a tuple of type `(&[Color4f], &ColorSpace)`.
494#[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    // to keep clippy happy.
509    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}