iced_core/
gradient.rs

1//! Colors that transition progressively.
2use crate::{Color, Radians};
3
4use std::cmp::Ordering;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD),
8/// or conically (TBD).
9pub enum Gradient {
10    /// A linear gradient interpolates colors along a direction at a specific angle.
11    Linear(Linear),
12}
13
14impl Gradient {
15    /// Scales the alpha channel of the [`Gradient`] by the given factor.
16    pub fn scale_alpha(self, factor: f32) -> Self {
17        match self {
18            Gradient::Linear(linear) => {
19                Gradient::Linear(linear.scale_alpha(factor))
20            }
21        }
22    }
23}
24
25impl From<Linear> for Gradient {
26    fn from(gradient: Linear) -> Self {
27        Self::Linear(gradient)
28    }
29}
30
31#[derive(Debug, Default, Clone, Copy, PartialEq)]
32/// A point along the gradient vector where the specified [`color`] is unmixed.
33///
34/// [`color`]: Self::color
35pub struct ColorStop {
36    /// Offset along the gradient vector.
37    pub offset: f32,
38
39    /// The color of the gradient at the specified [`offset`].
40    ///
41    /// [`offset`]: Self::offset
42    pub color: Color,
43}
44
45/// A linear gradient.
46#[derive(Debug, Clone, Copy, PartialEq)]
47pub struct Linear {
48    /// How the [`Gradient`] is angled within its bounds.
49    pub angle: Radians,
50    /// [`ColorStop`]s along the linear gradient path.
51    pub stops: [Option<ColorStop>; 8],
52}
53
54impl Linear {
55    /// Creates a new [`Linear`] gradient with the given angle in [`Radians`].
56    pub fn new(angle: impl Into<Radians>) -> Self {
57        Self {
58            angle: angle.into(),
59            stops: [None; 8],
60        }
61    }
62
63    /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient.
64    ///
65    /// Any `offset` that is not within `0.0..=1.0` will be silently ignored.
66    ///
67    /// Any stop added after the 8th will be silently ignored.
68    pub fn add_stop(mut self, offset: f32, color: Color) -> Self {
69        if offset.is_finite() && (0.0..=1.0).contains(&offset) {
70            let (Ok(index) | Err(index)) =
71                self.stops.binary_search_by(|stop| match stop {
72                    None => Ordering::Greater,
73                    Some(stop) => stop.offset.partial_cmp(&offset).unwrap(),
74                });
75
76            if index < 8 {
77                self.stops[index] = Some(ColorStop { offset, color });
78            }
79        } else {
80            log::warn!("Gradient color stop must be within 0.0..=1.0 range.");
81        };
82
83        self
84    }
85
86    /// Adds multiple [`ColorStop`]s to the gradient.
87    ///
88    /// Any stop added after the 8th will be silently ignored.
89    pub fn add_stops(
90        mut self,
91        stops: impl IntoIterator<Item = ColorStop>,
92    ) -> Self {
93        for stop in stops {
94            self = self.add_stop(stop.offset, stop.color);
95        }
96
97        self
98    }
99
100    /// Scales the alpha channel of the [`Linear`] gradient by the given
101    /// factor.
102    pub fn scale_alpha(mut self, factor: f32) -> Self {
103        for stop in self.stops.iter_mut().flatten() {
104            stop.color.a *= factor;
105        }
106
107        self
108    }
109}