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!(
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 = 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, color_space): (&[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 = [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            points.native().as_ptr(),
245            colors.native().as_ptr(),
246            color_space.into_ptr_or_null(),
247            pos.as_ptr_or_null(),
248            colors.len().try_into().unwrap(),
249            mode,
250            interpolation.native(),
251            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, radius): (impl Into<Point>, scalar),
299    (colors, color_space): (&[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.native(),
315            radius,
316            colors.native().as_ptr(),
317            color_space.into_ptr_or_null(),
318            pos.as_ptr_or_null(),
319            colors.len().try_into().unwrap(),
320            mode,
321            interpolation.native(),
322            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, start_radius): (impl Into<Point>, scalar),
379    (end, end_radius): (impl Into<Point>, scalar),
380    (colors, color_space): (&[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.native(),
395            start_radius,
396            end.native(),
397            end_radius,
398            colors.native().as_ptr(),
399            color_space.into().into_ptr_or_null(),
400            pos.as_ptr_or_null(),
401            colors.len().try_into().unwrap(),
402            mode,
403            interpolation.into().native(),
404            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, color_space): (&[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) => colors.len(),
508            GradientShaderColors::ColorsInSpace(colors, _) => 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}