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