skia_safe/effects/
gradient_shader.rs

1use crate::{
2    gradient, scalar, shaders, Color, Color4f, ColorSpace, Matrix, Point, Shader, TileMode,
3};
4
5pub use gradient::{interpolation, Colors as GradientColors, Gradient, Interpolation};
6
7impl From<Flags> for Interpolation {
8    fn from(flags: Flags) -> Self {
9        let in_premul = if flags.contains(Flags::INTERPOLATE_COLORS_IN_PREMUL) {
10            interpolation::InPremul::Yes
11        } else {
12            interpolation::InPremul::No
13        };
14        Self {
15            in_premul,
16            color_space: interpolation::ColorSpace::Destination,
17            hue_method: interpolation::HueMethod::Shorter,
18        }
19    }
20}
21
22impl Shader {
23    #[deprecated(since = "0.93.0", note = "Use shaders::linear_gradient() instead")]
24    pub fn linear_gradient<'a>(
25        points: (impl Into<Point>, impl Into<Point>),
26        colors: impl Into<GradientShaderColors<'a>>,
27        pos: impl Into<Option<&'a [scalar]>>,
28        mode: TileMode,
29        flags: impl Into<Option<Flags>>,
30        local_matrix: impl Into<Option<&'a Matrix>>,
31    ) -> Option<Self> {
32        linear(points, colors, pos, mode, flags, local_matrix)
33    }
34
35    #[deprecated(since = "0.93.0", note = "Use shaders::linear_gradient() instead")]
36    pub fn linear_gradient_with_interpolation<'a>(
37        points: (impl Into<Point>, impl Into<Point>),
38        colors: (&'a [Color4f], impl Into<Option<ColorSpace>>),
39        pos: impl Into<Option<&'a [scalar]>>,
40        mode: TileMode,
41        interpolation: impl Into<Interpolation>,
42        local_matrix: impl Into<Option<&'a Matrix>>,
43    ) -> Option<Self> {
44        linear_with_interpolation(points, colors, pos, mode, interpolation, local_matrix)
45    }
46
47    #[deprecated(since = "0.93.0", note = "Use shaders::radial_gradient() instead")]
48    pub fn radial_gradient<'a>(
49        center: impl Into<Point>,
50        radius: scalar,
51        colors: impl Into<GradientShaderColors<'a>>,
52        pos: impl Into<Option<&'a [scalar]>>,
53        mode: TileMode,
54        flags: impl Into<Option<self::Flags>>,
55        local_matrix: impl Into<Option<&'a Matrix>>,
56    ) -> Option<Self> {
57        radial(center, radius, colors, pos, mode, flags, local_matrix)
58    }
59
60    #[deprecated(since = "0.93.0", note = "Use shaders::radial_gradient() instead")]
61    #[allow(clippy::too_many_arguments)]
62    pub fn radial_gradient_with_interpolation<'a>(
63        center_and_radius: (impl Into<Point>, scalar),
64        colors: (&'a [Color4f], impl Into<Option<ColorSpace>>),
65        pos: impl Into<Option<&'a [scalar]>>,
66        mode: TileMode,
67        interpolation: impl Into<Interpolation>,
68        local_matrix: impl Into<Option<&'a Matrix>>,
69    ) -> Option<Shader> {
70        radial_with_interpolation(
71            center_and_radius,
72            colors,
73            pos,
74            mode,
75            interpolation,
76            local_matrix,
77        )
78    }
79
80    #[deprecated(
81        since = "0.93.0",
82        note = "Use shaders::two_point_conical_gradient() instead"
83    )]
84    #[allow(clippy::too_many_arguments)]
85    pub fn two_point_conical_gradient<'a>(
86        start: impl Into<Point>,
87        start_radius: scalar,
88        end: impl Into<Point>,
89        end_radius: scalar,
90        colors: impl Into<GradientShaderColors<'a>>,
91        pos: impl Into<Option<&'a [scalar]>>,
92        mode: TileMode,
93        flags: impl Into<Option<self::Flags>>,
94        local_matrix: impl Into<Option<&'a Matrix>>,
95    ) -> Option<Self> {
96        two_point_conical(
97            start,
98            start_radius,
99            end,
100            end_radius,
101            colors,
102            pos,
103            mode,
104            flags,
105            local_matrix,
106        )
107    }
108
109    #[deprecated(
110        since = "0.93.0",
111        note = "Use shaders::two_point_conical_gradient() instead"
112    )]
113    pub fn two_point_conical_gradient_with_interpolation<'a>(
114        start_and_radius: (impl Into<Point>, scalar),
115        end_and_radius: (impl Into<Point>, scalar),
116        colors: (&'a [Color4f], impl Into<Option<ColorSpace>>),
117        pos: impl Into<Option<&'a [scalar]>>,
118        mode: TileMode,
119        interpolation: impl Into<Interpolation>,
120        local_matrix: impl Into<Option<&'a Matrix>>,
121    ) -> Option<Shader> {
122        two_point_conical_with_interpolation(
123            start_and_radius,
124            end_and_radius,
125            colors,
126            pos,
127            mode,
128            interpolation,
129            local_matrix,
130        )
131    }
132
133    #[deprecated(since = "0.93.0", note = "Use shaders::sweep_gradient() instead")]
134    pub fn sweep_gradient<'a>(
135        center: impl Into<Point>,
136        colors: impl Into<GradientShaderColors<'a>>,
137        pos: impl Into<Option<&'a [scalar]>>,
138        mode: TileMode,
139        angles: impl Into<Option<(scalar, scalar)>>,
140        flags: impl Into<Option<self::Flags>>,
141        local_matrix: impl Into<Option<&'a Matrix>>,
142    ) -> Option<Self> {
143        sweep(center, colors, pos, mode, angles, flags, local_matrix)
144    }
145
146    #[deprecated(since = "0.93.0", note = "Use shaders::sweep_gradient() instead")]
147    pub fn sweep_gradient_with_interpolation<'a>(
148        center: impl Into<Point>,
149        colors: (&'a [Color4f], impl Into<Option<ColorSpace>>),
150        pos: impl Into<Option<&'a [scalar]>>,
151        mode: TileMode,
152        angles: impl Into<Option<(scalar, scalar)>>,
153        interpolation: impl Into<Interpolation>,
154        local_matrix: impl Into<Option<&'a Matrix>>,
155    ) -> Option<Shader> {
156        sweep_with_interpolation(
157            center,
158            colors,
159            pos,
160            mode,
161            angles,
162            interpolation,
163            local_matrix,
164        )
165    }
166}
167
168bitflags! {
169    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170    pub struct Flags: u32 {
171        const INTERPOLATE_COLORS_IN_PREMUL = 1 << 0;
172    }
173}
174
175impl Default for self::Flags {
176    fn default() -> Self {
177        Self::empty()
178    }
179}
180
181pub fn linear<'a>(
182    points: (impl Into<Point>, impl Into<Point>),
183    colors: impl Into<GradientShaderColors<'a>>,
184    pos: impl Into<Option<&'a [scalar]>>,
185    mode: TileMode,
186    flags: impl Into<Option<Flags>>,
187    local_matrix: impl Into<Option<&'a Matrix>>,
188) -> Option<Shader> {
189    let colors = colors.into();
190    let pos = pos.into();
191    let flags = flags.into().unwrap_or_default();
192
193    match colors {
194        GradientShaderColors::Colors(colors) => {
195            // Convert Color to Color4f
196            let colors4f: Vec<Color4f> = colors.iter().map(|c| Color4f::from(*c)).collect();
197            let grad_colors = GradientColors::new(&colors4f, pos, mode, None);
198            let grad = Gradient::new(grad_colors, flags);
199            shaders::linear_gradient(points, &grad, local_matrix)
200        }
201        GradientShaderColors::ColorsInSpace(colors, color_space) => linear_with_interpolation(
202            points,
203            (colors, color_space),
204            pos,
205            mode,
206            flags,
207            local_matrix,
208        ),
209    }
210}
211
212pub fn linear_with_interpolation<'a>(
213    points: (impl Into<Point>, impl Into<Point>),
214    (colors, color_space): (&'a [Color4f], impl Into<Option<ColorSpace>>),
215    pos: impl Into<Option<&'a [scalar]>>,
216    mode: TileMode,
217    interpolation: impl Into<Interpolation>,
218    local_matrix: impl Into<Option<&'a Matrix>>,
219) -> Option<Shader> {
220    let pos = pos.into();
221    let color_space = color_space.into();
222    let grad_colors = GradientColors::new(colors, pos, mode, color_space);
223    let grad = Gradient::new(grad_colors, interpolation);
224    shaders::linear_gradient(points, &grad, local_matrix)
225}
226
227pub fn radial<'a>(
228    center: impl Into<Point>,
229    radius: scalar,
230    colors: impl Into<GradientShaderColors<'a>>,
231    pos: impl Into<Option<&'a [scalar]>>,
232    mode: TileMode,
233    flags: impl Into<Option<self::Flags>>,
234    local_matrix: impl Into<Option<&'a Matrix>>,
235) -> Option<Shader> {
236    let colors = colors.into();
237    let pos = pos.into();
238    let flags = flags.into().unwrap_or_default();
239
240    match colors {
241        GradientShaderColors::Colors(colors) => {
242            let colors4f: Vec<Color4f> = colors.iter().map(|c| Color4f::from(*c)).collect();
243            let grad_colors = GradientColors::new(&colors4f, pos, mode, None);
244            let grad = Gradient::new(grad_colors, flags);
245            shaders::radial_gradient((center, radius), &grad, local_matrix)
246        }
247        GradientShaderColors::ColorsInSpace(colors, color_space) => radial_with_interpolation(
248            (center, radius),
249            (colors, color_space),
250            pos,
251            mode,
252            flags,
253            local_matrix,
254        ),
255    }
256}
257
258pub fn radial_with_interpolation<'a>(
259    (center, radius): (impl Into<Point>, scalar),
260    (colors, color_space): (&'a [Color4f], impl Into<Option<ColorSpace>>),
261    pos: impl Into<Option<&'a [scalar]>>,
262    mode: TileMode,
263    interpolation: impl Into<Interpolation>,
264    local_matrix: impl Into<Option<&'a Matrix>>,
265) -> Option<Shader> {
266    let pos = pos.into();
267    let color_space = color_space.into();
268    let grad_colors = GradientColors::new(colors, pos, mode, color_space);
269    let grad = Gradient::new(grad_colors, interpolation);
270    shaders::radial_gradient((center, radius), &grad, local_matrix)
271}
272
273#[allow(clippy::too_many_arguments)]
274pub fn two_point_conical<'a>(
275    start: impl Into<Point>,
276    start_radius: scalar,
277    end: impl Into<Point>,
278    end_radius: scalar,
279    colors: impl Into<GradientShaderColors<'a>>,
280    pos: impl Into<Option<&'a [scalar]>>,
281    mode: TileMode,
282    flags: impl Into<Option<self::Flags>>,
283    local_matrix: impl Into<Option<&'a Matrix>>,
284) -> Option<Shader> {
285    let colors = colors.into();
286    let pos = pos.into();
287    let flags = flags.into().unwrap_or_default();
288
289    match colors {
290        GradientShaderColors::Colors(colors) => {
291            let colors4f: Vec<Color4f> = colors.iter().map(|c| Color4f::from(*c)).collect();
292            let grad_colors = GradientColors::new(&colors4f, pos, mode, None);
293            let grad = Gradient::new(grad_colors, flags);
294            shaders::two_point_conical_gradient(
295                (start, start_radius),
296                (end, end_radius),
297                &grad,
298                local_matrix,
299            )
300        }
301        GradientShaderColors::ColorsInSpace(colors, color_space) => {
302            two_point_conical_with_interpolation(
303                (start, start_radius),
304                (end, end_radius),
305                (colors, color_space),
306                pos,
307                mode,
308                flags,
309                local_matrix,
310            )
311        }
312    }
313}
314
315pub fn two_point_conical_with_interpolation<'a>(
316    (start, start_radius): (impl Into<Point>, scalar),
317    (end, end_radius): (impl Into<Point>, scalar),
318    (colors, color_space): (&'a [Color4f], impl Into<Option<ColorSpace>>),
319    pos: impl Into<Option<&'a [scalar]>>,
320    mode: TileMode,
321    interpolation: impl Into<Interpolation>,
322    local_matrix: impl Into<Option<&'a Matrix>>,
323) -> Option<Shader> {
324    let pos = pos.into();
325    let color_space = color_space.into();
326    let grad_colors = GradientColors::new(colors, pos, mode, color_space);
327    let grad = Gradient::new(grad_colors, interpolation);
328    shaders::two_point_conical_gradient(
329        (start, start_radius),
330        (end, end_radius),
331        &grad,
332        local_matrix,
333    )
334}
335
336pub fn sweep<'a>(
337    center: impl Into<Point>,
338    colors: impl Into<GradientShaderColors<'a>>,
339    pos: impl Into<Option<&'a [scalar]>>,
340    mode: TileMode,
341    angles: impl Into<Option<(scalar, scalar)>>,
342    flags: impl Into<Option<self::Flags>>,
343    local_matrix: impl Into<Option<&'a Matrix>>,
344) -> Option<Shader> {
345    let colors = colors.into();
346    let pos = pos.into();
347    let angles = angles.into();
348    let flags = flags.into().unwrap_or_default();
349
350    let (start_angle, end_angle) = (
351        angles.map(|a| a.0).unwrap_or(0.0),
352        angles.map(|a| a.1).unwrap_or(360.0),
353    );
354
355    match colors {
356        GradientShaderColors::Colors(colors) => {
357            let colors4f: Vec<Color4f> = colors.iter().map(|c| Color4f::from(*c)).collect();
358            let grad_colors = GradientColors::new(&colors4f, pos, mode, None);
359            let grad = Gradient::new(grad_colors, flags);
360            shaders::sweep_gradient(center, (start_angle, end_angle), &grad, local_matrix)
361        }
362        GradientShaderColors::ColorsInSpace(colors, color_space) => sweep_with_interpolation(
363            center,
364            (colors, color_space),
365            pos,
366            mode,
367            angles,
368            flags,
369            local_matrix,
370        ),
371    }
372}
373
374pub fn sweep_with_interpolation<'a>(
375    center: impl Into<Point>,
376    (colors, color_space): (&'a [Color4f], impl Into<Option<ColorSpace>>),
377    pos: impl Into<Option<&'a [scalar]>>,
378    mode: TileMode,
379    angles: impl Into<Option<(scalar, scalar)>>,
380    interpolation: impl Into<Interpolation>,
381    local_matrix: impl Into<Option<&'a Matrix>>,
382) -> Option<Shader> {
383    let pos = pos.into();
384    let angles = angles.into();
385    let color_space = color_space.into();
386
387    let (start_angle, end_angle) = (
388        angles.map(|a| a.0).unwrap_or(0.0),
389        angles.map(|a| a.1).unwrap_or(360.0),
390    );
391
392    let grad_colors = GradientColors::new(colors, pos, mode, color_space);
393    let grad = Gradient::new(grad_colors, interpolation);
394    shaders::sweep_gradient(center, (start_angle, end_angle), &grad, local_matrix)
395}
396
397/// Type that represents either a slice of [`Color`], or a slice of [`Color4f`] and a color space.
398/// Whenever this type is expected, it's either possible to directly pass a `&[Color]` , or
399/// a tuple of type `(&[Color4f], &ColorSpace)`.
400#[derive(Debug)]
401pub enum GradientShaderColors<'a> {
402    Colors(&'a [Color]),
403    ColorsInSpace(&'a [Color4f], Option<ColorSpace>),
404}
405
406impl GradientShaderColors<'_> {
407    pub fn len(&self) -> usize {
408        match self {
409            GradientShaderColors::Colors(colors) => colors.len(),
410            GradientShaderColors::ColorsInSpace(colors, _) => colors.len(),
411        }
412    }
413
414    // to keep clippy happy.
415    pub fn is_empty(&self) -> bool {
416        self.len() == 0
417    }
418}
419
420impl<'a> From<&'a [Color]> for GradientShaderColors<'a> {
421    fn from(colors: &'a [Color]) -> Self {
422        GradientShaderColors::<'a>::Colors(colors)
423    }
424}
425
426impl<'a> From<(&'a [Color4f], ColorSpace)> for GradientShaderColors<'a> {
427    fn from(c: (&'a [Color4f], ColorSpace)) -> Self {
428        GradientShaderColors::<'a>::ColorsInSpace(c.0, Some(c.1))
429    }
430}
431
432impl<'a> From<(&'a [Color4f], Option<ColorSpace>)> for GradientShaderColors<'a> {
433    fn from(c: (&'a [Color4f], Option<ColorSpace>)) -> Self {
434        GradientShaderColors::<'a>::ColorsInSpace(c.0, c.1)
435    }
436}
437
438impl<'a> From<&'a [Color4f]> for GradientShaderColors<'a> {
439    fn from(c: &'a [Color4f]) -> Self {
440        GradientShaderColors::<'a>::ColorsInSpace(c, None)
441    }
442}