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.total_cmp(&offset),
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}