zng_unit/
color.rs

1use std::{fmt, ops};
2
3use crate::{EQ_GRANULARITY, Factor, FactorPercent, ParseIntCompositeError, about_eq, about_eq_hash, about_eq_ord};
4
5/// RGB + alpha.
6///
7/// # Equality
8///
9/// Equality is determined using [`about_eq`] with `0.00001` granularity.
10///
11/// [`about_eq`]: crate::about_eq
12#[repr(C)]
13#[derive(Default, Copy, Clone, serde::Serialize, serde::Deserialize)]
14pub struct Rgba {
15    /// Red channel value, in the `[0.0..=1.0]` range.
16    pub red: f32,
17    /// Green channel value, in the `[0.0..=1.0]` range.
18    pub green: f32,
19    /// Blue channel value, in the `[0.0..=1.0]` range.
20    pub blue: f32,
21    /// Alpha channel value, in the `[0.0..=1.0]` range.
22    pub alpha: f32,
23}
24impl PartialEq for Rgba {
25    fn eq(&self, other: &Self) -> bool {
26        about_eq(self.red, other.red, EQ_GRANULARITY)
27            && about_eq(self.green, other.green, EQ_GRANULARITY)
28            && about_eq(self.blue, other.blue, EQ_GRANULARITY)
29            && about_eq(self.alpha, other.alpha, EQ_GRANULARITY)
30    }
31}
32impl Eq for Rgba {}
33impl PartialOrd for Rgba {
34    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
35        Some(self.cmp(other))
36    }
37}
38impl Ord for Rgba {
39    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
40        about_eq_ord(self.red, other.red, EQ_GRANULARITY)
41            .cmp(&about_eq_ord(self.green, other.green, EQ_GRANULARITY))
42            .cmp(&about_eq_ord(self.blue, other.blue, EQ_GRANULARITY))
43            .cmp(&about_eq_ord(self.alpha, other.alpha, EQ_GRANULARITY))
44    }
45}
46impl std::hash::Hash for Rgba {
47    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48        about_eq_hash(self.red, EQ_GRANULARITY, state);
49        about_eq_hash(self.green, EQ_GRANULARITY, state);
50        about_eq_hash(self.blue, EQ_GRANULARITY, state);
51        about_eq_hash(self.alpha, EQ_GRANULARITY, state);
52    }
53}
54impl Rgba {
55    /// New from RGB of a the same type and A that can be of a different type.
56    pub fn new<C: Into<RgbaComponent>, A: Into<RgbaComponent>>(red: C, green: C, blue: C, alpha: A) -> Rgba {
57        Rgba {
58            red: red.into().0,
59            green: green.into().0,
60            blue: blue.into().0,
61            alpha: alpha.into().0,
62        }
63    }
64
65    /// Set the [`red`](Rgba::red) component from any type that converts to [`RgbaComponent`].
66    pub fn set_red<R: Into<RgbaComponent>>(&mut self, red: R) {
67        self.red = red.into().0
68    }
69
70    /// Set the [`green`](Rgba::green) component from any type that converts to [`RgbaComponent`].
71    pub fn set_green<G: Into<RgbaComponent>>(&mut self, green: G) {
72        self.green = green.into().0
73    }
74
75    /// Set the [`blue`](Rgba::blue) component from any type that converts to [`RgbaComponent`].
76    pub fn set_blue<B: Into<RgbaComponent>>(&mut self, blue: B) {
77        self.blue = blue.into().0
78    }
79
80    /// Set the [`alpha`](Rgba::alpha) component from any type that converts to [`RgbaComponent`].
81    pub fn set_alpha<A: Into<RgbaComponent>>(&mut self, alpha: A) {
82        self.alpha = alpha.into().0
83    }
84
85    /// Returns a copy of the color with a new `red` value.
86    pub fn with_red<R: Into<RgbaComponent>>(mut self, red: R) -> Self {
87        self.set_red(red);
88        self
89    }
90
91    /// Returns a copy of the color with a new `green` value.
92    pub fn with_green<R: Into<RgbaComponent>>(mut self, green: R) -> Self {
93        self.set_green(green);
94        self
95    }
96
97    /// Returns a copy of the color with a new `blue` value.
98    pub fn with_blue<B: Into<RgbaComponent>>(mut self, blue: B) -> Self {
99        self.set_blue(blue);
100        self
101    }
102
103    /// Returns a copy of the color with a new `alpha` value.
104    pub fn with_alpha<A: Into<RgbaComponent>>(mut self, alpha: A) -> Self {
105        self.set_alpha(alpha);
106        self
107    }
108
109    /// Returns a copy of the color with the alpha set to `0`.
110    pub fn transparent(self) -> Self {
111        self.with_alpha(0.0)
112    }
113
114    /// Convert a copy to [R, G, B, A] bytes.
115    pub fn to_bytes(self) -> [u8; 4] {
116        [
117            (self.red * 255.0) as u8,
118            (self.green * 255.0) as u8,
119            (self.blue * 255.0) as u8,
120            (self.alpha * 255.0) as u8,
121        ]
122    }
123
124    /// Convert a copy to [B, G, R, A] bytes.
125    pub fn to_bgra_bytes(self) -> [u8; 4] {
126        [
127            (self.blue * 255.0) as u8,
128            (self.green * 255.0) as u8,
129            (self.red * 255.0) as u8,
130            (self.alpha * 255.0) as u8,
131        ]
132    }
133
134    /// If all components are finite.
135    fn is_valid(self) -> bool {
136        self.red.is_finite() && self.blue.is_finite() && self.green.is_finite() && self.alpha.is_finite()
137    }
138}
139impl fmt::Debug for Rgba {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        if f.alternate() || !self.is_valid() {
142            f.debug_struct("Rgba")
143                .field("red", &self.red)
144                .field("green", &self.green)
145                .field("blue", &self.blue)
146                .field("alpha", &self.alpha)
147                .finish()
148        } else {
149            fn i(n: f32) -> u8 {
150                (clamp_normal(n) * 255.0).round() as u8
151            }
152            let a = i(self.alpha);
153            if a == 255 {
154                write!(f, "rgb({}, {}, {})", i(self.red), i(self.green), i(self.blue))
155            } else {
156                write!(f, "rgba({}, {}, {}, {})", i(self.red), i(self.green), i(self.blue), a)
157            }
158        }
159    }
160}
161impl fmt::Display for Rgba {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        fn i(n: f32) -> u32 {
164            (clamp_normal(n) * 255.0).round() as u32
165        }
166
167        let mut rgb: u32 = 0;
168        rgb |= i(self.red) << 16;
169        rgb |= i(self.green) << 8;
170        rgb |= i(self.blue);
171
172        let a = i(self.alpha);
173        if a == 255 {
174            write!(f, "#{rgb:0>6X}")
175        } else {
176            let rgba = (rgb << 8) | a;
177            write!(f, "#{rgba:0>8X}")
178        }
179    }
180}
181impl ops::Add<Self> for Rgba {
182    type Output = Self;
183
184    fn add(self, rhs: Self) -> Self::Output {
185        Rgba {
186            red: self.red + rhs.red,
187            green: self.green + rhs.green,
188            blue: self.blue + rhs.blue,
189            alpha: self.alpha + rhs.alpha,
190        }
191    }
192}
193impl ops::AddAssign<Self> for Rgba {
194    fn add_assign(&mut self, rhs: Self) {
195        *self = *self + rhs;
196    }
197}
198impl ops::Sub<Self> for Rgba {
199    type Output = Self;
200
201    fn sub(self, rhs: Self) -> Self::Output {
202        Rgba {
203            red: self.red - rhs.red,
204            green: self.green - rhs.green,
205            blue: self.blue - rhs.blue,
206            alpha: self.alpha - rhs.alpha,
207        }
208    }
209}
210impl ops::SubAssign<Self> for Rgba {
211    fn sub_assign(&mut self, rhs: Self) {
212        *self = *self - rhs;
213    }
214}
215
216// Util
217fn clamp_normal(i: f32) -> f32 {
218    i.clamp(0.0, 1.0)
219}
220
221/// Color functions argument conversion helper.
222///
223/// Don't use this value directly, if a function takes `Into<RgbaComponent>` you can use one of the
224/// types this converts from:
225///
226/// * `f32`, `f64` and [`Factor`] for a value in the `0.0` to `1.0` range.
227/// * `u8` for a value in the `0` to `255` range.
228/// * [`FactorPercent`] for a percentage value.
229///
230/// [`Factor`]: crate::Factor
231/// [`FactorPercent`]: crate::FactorPercent
232#[derive(Clone, Copy)]
233pub struct RgbaComponent(pub f32);
234/// Color channel value is in the [0..=1] range.
235impl From<f32> for RgbaComponent {
236    fn from(f: f32) -> Self {
237        if f.is_finite() {
238            RgbaComponent(f)
239        } else {
240            #[cfg(debug_assertions)]
241            panic!("rgba color component is not finite, {f}");
242
243            #[cfg(not(debug_assertions))]
244            if f.is_nan() {
245                RgbaComponent(0.0)
246            } else if f.is_infinite() {
247                if f.is_sign_positive() {
248                    RgbaComponent(1.0)
249                } else {
250                    RgbaComponent(0.0)
251                }
252            } else {
253                unreachable!()
254            }
255        }
256    }
257}
258/// Color channel value is in the [0..=1] range.
259impl From<f64> for RgbaComponent {
260    fn from(f: f64) -> Self {
261        RgbaComponent::from(f as f32)
262    }
263}
264/// Color channel value is in the [0..=255] range.
265impl From<u8> for RgbaComponent {
266    fn from(u: u8) -> Self {
267        RgbaComponent::from(f32::from(u) / 255.)
268    }
269}
270/// Color channel value is in the [0..=100] range.
271impl From<FactorPercent> for RgbaComponent {
272    fn from(p: FactorPercent) -> Self {
273        RgbaComponent::from(p.0 / 100.)
274    }
275}
276/// Color channel value is in the [0..=1] range.
277impl From<Factor> for RgbaComponent {
278    fn from(f: Factor) -> Self {
279        RgbaComponent::from(f.0)
280    }
281}
282
283/// Parses `"#RRGGBBAA"`, `#RRGGBB`, `"rgba(u8, u8, u8, u8)"` and `"rgb(u8, u8, u8)"`.
284impl std::str::FromStr for Rgba {
285    type Err = ParseIntCompositeError;
286
287    fn from_str(s: &str) -> Result<Self, Self::Err> {
288        if let Some(hex) = s.strip_prefix('#') {
289            let mut parser = HexComponentParser::new(hex);
290            let rgba = Rgba::new(parser.next()?, parser.next()?, parser.next()?, parser.next_or(255)?);
291            parser.end()?;
292            Ok(rgba)
293        } else if let Some(rgba) = s.strip_prefix("rgba(")
294            && let Some(rgba) = rgba.strip_suffix(')')
295        {
296            let mut parser = ComponentParser::new(rgba);
297            let rgba = Rgba::new(parser.next()?, parser.next()?, parser.next()?, parser.next()?);
298            parser.end()?;
299            Ok(rgba)
300        } else if let Some(rgb) = s.strip_prefix("rgb(")
301            && let Some(rgb) = rgb.strip_suffix(')')
302        {
303            let mut parser = ComponentParser::new(rgb);
304            let rgba = Rgba::new(parser.next()?, parser.next()?, parser.next()?, 255);
305            parser.end()?;
306            Ok(rgba)
307        } else {
308            Err(ParseIntCompositeError::UnknownFormat)
309        }
310    }
311}
312struct ComponentParser<'s> {
313    s: std::str::Split<'s, char>,
314}
315impl<'s> ComponentParser<'s> {
316    fn new(s: &'s str) -> Self {
317        Self { s: s.split(',') }
318    }
319
320    fn next(&mut self) -> Result<u8, ParseIntCompositeError> {
321        Ok(self.s.next().ok_or(ParseIntCompositeError::MissingComponent)?.trim().parse()?)
322    }
323
324    fn end(mut self) -> Result<(), ParseIntCompositeError> {
325        if self.s.next().is_some() {
326            Err(ParseIntCompositeError::ExtraComponent)
327        } else {
328            Ok(())
329        }
330    }
331}
332struct HexComponentParser<'s> {
333    s: &'s str,
334}
335impl<'s> HexComponentParser<'s> {
336    fn new(s: &'s str) -> Self {
337        Self { s }
338    }
339
340    fn next(&mut self) -> Result<u8, ParseIntCompositeError> {
341        if self.s.len() < 2 {
342            Err(ParseIntCompositeError::MissingComponent)
343        } else {
344            let c = &self.s[..2];
345            self.s = &self.s[2..];
346            Ok(u8::from_str_radix(c, 16)?)
347        }
348    }
349
350    fn next_or(&mut self, c: u8) -> Result<u8, ParseIntCompositeError> {
351        if self.s.is_empty() { Ok(c) } else { self.next() }
352    }
353
354    fn end(self) -> Result<(), ParseIntCompositeError> {
355        if self.s.is_empty() {
356            Ok(())
357        } else {
358            Err(ParseIntCompositeError::ExtraComponent)
359        }
360    }
361}