zng_unit/
color.rs

1use std::{fmt, ops};
2
3use crate::{Factor, FactorPercent, about_eq, about_eq_hash};
4
5/// RGB + alpha.
6///
7/// # Equality
8///
9/// Equality is determined using [`about_eq`] with `0.00001` epsilon.
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, EPSILON)
27            && about_eq(self.green, other.green, EPSILON)
28            && about_eq(self.blue, other.blue, EPSILON)
29            && about_eq(self.alpha, other.alpha, EPSILON)
30    }
31}
32impl std::hash::Hash for Rgba {
33    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
34        about_eq_hash(self.red, EPSILON, state);
35        about_eq_hash(self.green, EPSILON, state);
36        about_eq_hash(self.blue, EPSILON, state);
37        about_eq_hash(self.alpha, EPSILON, state);
38    }
39}
40impl Rgba {
41    /// New from RGB of a the same type and A that can be of a different type.
42    pub fn new<C: Into<RgbaComponent>, A: Into<RgbaComponent>>(red: C, green: C, blue: C, alpha: A) -> Rgba {
43        Rgba {
44            red: red.into().0,
45            green: green.into().0,
46            blue: blue.into().0,
47            alpha: alpha.into().0,
48        }
49    }
50
51    /// Set the [`red`](Rgba::red) component from any type that converts to [`RgbaComponent`].
52    pub fn set_red<R: Into<RgbaComponent>>(&mut self, red: R) {
53        self.red = red.into().0
54    }
55
56    /// Set the [`green`](Rgba::green) component from any type that converts to [`RgbaComponent`].
57    pub fn set_green<G: Into<RgbaComponent>>(&mut self, green: G) {
58        self.green = green.into().0
59    }
60
61    /// Set the [`blue`](Rgba::blue) component from any type that converts to [`RgbaComponent`].
62    pub fn set_blue<B: Into<RgbaComponent>>(&mut self, blue: B) {
63        self.blue = blue.into().0
64    }
65
66    /// Set the [`alpha`](Rgba::alpha) component from any type that converts to [`RgbaComponent`].
67    pub fn set_alpha<A: Into<RgbaComponent>>(&mut self, alpha: A) {
68        self.alpha = alpha.into().0
69    }
70
71    /// Returns a copy of the color with a new `red` value.
72    pub fn with_red<R: Into<RgbaComponent>>(mut self, red: R) -> Self {
73        self.set_red(red);
74        self
75    }
76
77    /// Returns a copy of the color with a new `green` value.
78    pub fn with_green<R: Into<RgbaComponent>>(mut self, green: R) -> Self {
79        self.set_green(green);
80        self
81    }
82
83    /// Returns a copy of the color with a new `blue` value.
84    pub fn with_blue<B: Into<RgbaComponent>>(mut self, blue: B) -> Self {
85        self.set_blue(blue);
86        self
87    }
88
89    /// Returns a copy of the color with a new `alpha` value.
90    pub fn with_alpha<A: Into<RgbaComponent>>(mut self, alpha: A) -> Self {
91        self.set_alpha(alpha);
92        self
93    }
94
95    /// Returns a copy of the color with the alpha set to `0`.
96    pub fn transparent(self) -> Self {
97        self.with_alpha(0.0)
98    }
99
100    /// Convert a copy to [R, G, B, A] bytes.
101    pub fn to_bytes(self) -> [u8; 4] {
102        [
103            (self.red * 255.0) as u8,
104            (self.green * 255.0) as u8,
105            (self.blue * 255.0) as u8,
106            (self.alpha * 255.0) as u8,
107        ]
108    }
109}
110impl fmt::Debug for Rgba {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        if f.alternate() {
113            f.debug_struct("Rgba")
114                .field("red", &self.red)
115                .field("green", &self.green)
116                .field("blue", &self.blue)
117                .field("alpha", &self.alpha)
118                .finish()
119        } else {
120            fn i(n: f32) -> u8 {
121                (clamp_normal(n) * 255.0).round() as u8
122            }
123            let a = i(self.alpha);
124            if a == 255 {
125                write!(f, "rgb({}, {}, {})", i(self.red), i(self.green), i(self.blue))
126            } else {
127                write!(f, "rgba({}, {}, {}, {})", i(self.red), i(self.green), i(self.blue), a)
128            }
129        }
130    }
131}
132impl fmt::Display for Rgba {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        fn i(n: f32) -> u32 {
135            (clamp_normal(n) * 255.0).round() as u32
136        }
137
138        let mut rgb: u32 = 0;
139        rgb |= i(self.red) << 16;
140        rgb |= i(self.green) << 8;
141        rgb |= i(self.blue);
142
143        let a = i(self.alpha);
144        if a == 255 {
145            write!(f, "#{rgb:0>6X}")
146        } else {
147            let rgba = (rgb << 8) | a;
148            write!(f, "#{rgba:0>8X}")
149        }
150    }
151}
152impl ops::Add<Self> for Rgba {
153    type Output = Self;
154
155    fn add(self, rhs: Self) -> Self::Output {
156        Rgba {
157            red: self.red + rhs.red,
158            green: self.green + rhs.green,
159            blue: self.blue + rhs.blue,
160            alpha: self.alpha + rhs.alpha,
161        }
162    }
163}
164impl ops::AddAssign<Self> for Rgba {
165    fn add_assign(&mut self, rhs: Self) {
166        *self = *self + rhs;
167    }
168}
169impl ops::Sub<Self> for Rgba {
170    type Output = Self;
171
172    fn sub(self, rhs: Self) -> Self::Output {
173        Rgba {
174            red: self.red - rhs.red,
175            green: self.green - rhs.green,
176            blue: self.blue - rhs.blue,
177            alpha: self.alpha - rhs.alpha,
178        }
179    }
180}
181impl ops::SubAssign<Self> for Rgba {
182    fn sub_assign(&mut self, rhs: Self) {
183        *self = *self - rhs;
184    }
185}
186
187/// Minimal difference between values in around the 0.0..=1.0 scale.
188const EPSILON: f32 = 0.00001;
189
190// Util
191fn clamp_normal(i: f32) -> f32 {
192    i.clamp(0.0, 1.0)
193}
194
195/// Color functions argument conversion helper.
196///
197/// Don't use this value directly, if a function takes `Into<RgbaComponent>` you can use one of the
198/// types this converts from:
199///
200/// * `f32`, `f64` and [`Factor`] for a value in the `0.0` to `1.0` range.
201/// * `u8` for a value in the `0` to `255` range.
202/// * [`FactorPercent`] for a percentage value.
203///
204/// [`Factor`]: crate::Factor
205/// [`FactorPercent`]: crate::FactorPercent
206#[derive(Clone, Copy)]
207pub struct RgbaComponent(pub f32);
208/// Color channel value is in the [0..=1] range.
209impl From<f32> for RgbaComponent {
210    fn from(f: f32) -> Self {
211        RgbaComponent(f)
212    }
213}
214/// Color channel value is in the [0..=1] range.
215impl From<f64> for RgbaComponent {
216    fn from(f: f64) -> Self {
217        RgbaComponent(f as f32)
218    }
219}
220/// Color channel value is in the [0..=255] range.
221impl From<u8> for RgbaComponent {
222    fn from(u: u8) -> Self {
223        RgbaComponent(f32::from(u) / 255.)
224    }
225}
226/// Color channel value is in the [0..=100] range.
227impl From<FactorPercent> for RgbaComponent {
228    fn from(p: FactorPercent) -> Self {
229        RgbaComponent(p.0 / 100.)
230    }
231}
232/// Color channel value is in the [0..=1] range.
233impl From<Factor> for RgbaComponent {
234    fn from(f: Factor) -> Self {
235        RgbaComponent(f.0)
236    }
237}