ecolor/
rgba.rs

1use crate::{
2    gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8,
3    linear_u8_from_linear_f32,
4};
5
6/// 0-1 linear space `RGBA` color with premultiplied alpha.
7#[repr(C)]
8#[derive(Clone, Copy, Debug, Default, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
11pub struct Rgba(pub(crate) [f32; 4]);
12
13impl std::ops::Index<usize> for Rgba {
14    type Output = f32;
15
16    #[inline]
17    fn index(&self, index: usize) -> &f32 {
18        &self.0[index]
19    }
20}
21
22impl std::ops::IndexMut<usize> for Rgba {
23    #[inline]
24    fn index_mut(&mut self, index: usize) -> &mut f32 {
25        &mut self.0[index]
26    }
27}
28
29/// Deterministically hash an `f32`, treating all NANs as equal, and ignoring the sign of zero.
30#[inline]
31pub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {
32    if f == 0.0 {
33        state.write_u8(0);
34    } else if f.is_nan() {
35        state.write_u8(1);
36    } else {
37        use std::hash::Hash;
38        f.to_bits().hash(state);
39    }
40}
41
42#[allow(clippy::derived_hash_with_manual_eq)]
43impl std::hash::Hash for Rgba {
44    #[inline]
45    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
46        crate::f32_hash(state, self.0[0]);
47        crate::f32_hash(state, self.0[1]);
48        crate::f32_hash(state, self.0[2]);
49        crate::f32_hash(state, self.0[3]);
50    }
51}
52
53impl Rgba {
54    pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0.0, 0.0, 0.0, 0.0);
55    pub const BLACK: Self = Self::from_rgb(0.0, 0.0, 0.0);
56    pub const WHITE: Self = Self::from_rgb(1.0, 1.0, 1.0);
57    pub const RED: Self = Self::from_rgb(1.0, 0.0, 0.0);
58    pub const GREEN: Self = Self::from_rgb(0.0, 1.0, 0.0);
59    pub const BLUE: Self = Self::from_rgb(0.0, 0.0, 1.0);
60
61    #[inline]
62    pub const fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
63        Self([r, g, b, a])
64    }
65
66    #[inline]
67    pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
68        Self([r * a, g * a, b * a, a])
69    }
70
71    #[inline]
72    pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
73        let r = linear_f32_from_gamma_u8(r);
74        let g = linear_f32_from_gamma_u8(g);
75        let b = linear_f32_from_gamma_u8(b);
76        let a = linear_f32_from_linear_u8(a);
77        Self::from_rgba_premultiplied(r, g, b, a)
78    }
79
80    #[inline]
81    pub fn from_srgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
82        let r = linear_f32_from_gamma_u8(r);
83        let g = linear_f32_from_gamma_u8(g);
84        let b = linear_f32_from_gamma_u8(b);
85        let a = linear_f32_from_linear_u8(a);
86        Self::from_rgba_premultiplied(r * a, g * a, b * a, a)
87    }
88
89    #[inline]
90    pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
91        Self([r, g, b, 1.0])
92    }
93
94    #[inline]
95    pub const fn from_gray(l: f32) -> Self {
96        Self([l, l, l, 1.0])
97    }
98
99    #[inline]
100    pub fn from_luminance_alpha(l: f32, a: f32) -> Self {
101        debug_assert!(0.0 <= l && l <= 1.0);
102        debug_assert!(0.0 <= a && a <= 1.0);
103        Self([l * a, l * a, l * a, a])
104    }
105
106    /// Transparent black
107    #[inline]
108    pub fn from_black_alpha(a: f32) -> Self {
109        debug_assert!(0.0 <= a && a <= 1.0);
110        Self([0.0, 0.0, 0.0, a])
111    }
112
113    /// Transparent white
114    #[inline]
115    pub fn from_white_alpha(a: f32) -> Self {
116        debug_assert!(0.0 <= a && a <= 1.0, "a: {a}");
117        Self([a, a, a, a])
118    }
119
120    /// Return an additive version of this color (alpha = 0)
121    #[inline]
122    pub fn additive(self) -> Self {
123        let [r, g, b, _] = self.0;
124        Self([r, g, b, 0.0])
125    }
126
127    /// Is the alpha=0 ?
128    #[inline]
129    pub fn is_additive(self) -> bool {
130        self.a() == 0.0
131    }
132
133    /// Multiply with e.g. 0.5 to make us half transparent
134    #[inline]
135    pub fn multiply(self, alpha: f32) -> Self {
136        Self([
137            alpha * self[0],
138            alpha * self[1],
139            alpha * self[2],
140            alpha * self[3],
141        ])
142    }
143
144    #[inline]
145    pub fn r(&self) -> f32 {
146        self.0[0]
147    }
148
149    #[inline]
150    pub fn g(&self) -> f32 {
151        self.0[1]
152    }
153
154    #[inline]
155    pub fn b(&self) -> f32 {
156        self.0[2]
157    }
158
159    #[inline]
160    pub fn a(&self) -> f32 {
161        self.0[3]
162    }
163
164    /// How perceptually intense (bright) is the color?
165    #[inline]
166    pub fn intensity(&self) -> f32 {
167        0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b()
168    }
169
170    /// Returns an opaque version of self
171    #[inline]
172    pub fn to_opaque(&self) -> Self {
173        if self.a() == 0.0 {
174            // Additive or fully transparent black.
175            Self::from_rgb(self.r(), self.g(), self.b())
176        } else {
177            // un-multiply alpha:
178            Self::from_rgb(
179                self.r() / self.a(),
180                self.g() / self.a(),
181                self.b() / self.a(),
182            )
183        }
184    }
185
186    /// Premultiplied RGBA
187    #[inline]
188    pub fn to_array(&self) -> [f32; 4] {
189        [self.r(), self.g(), self.b(), self.a()]
190    }
191
192    /// Premultiplied RGBA
193    #[inline]
194    pub fn to_tuple(&self) -> (f32, f32, f32, f32) {
195        (self.r(), self.g(), self.b(), self.a())
196    }
197
198    /// unmultiply the alpha
199    #[inline]
200    pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {
201        let a = self.a();
202        if a == 0.0 {
203            // Additive, let's assume we are black
204            self.0
205        } else {
206            [self.r() / a, self.g() / a, self.b() / a, a]
207        }
208    }
209
210    /// unmultiply the alpha
211    #[inline]
212    pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {
213        let [r, g, b, a] = self.to_rgba_unmultiplied();
214        [
215            gamma_u8_from_linear_f32(r),
216            gamma_u8_from_linear_f32(g),
217            gamma_u8_from_linear_f32(b),
218            linear_u8_from_linear_f32(a.abs()),
219        ]
220    }
221}
222
223impl std::ops::Add for Rgba {
224    type Output = Self;
225
226    #[inline]
227    fn add(self, rhs: Self) -> Self {
228        Self([
229            self[0] + rhs[0],
230            self[1] + rhs[1],
231            self[2] + rhs[2],
232            self[3] + rhs[3],
233        ])
234    }
235}
236
237impl std::ops::Mul for Rgba {
238    type Output = Self;
239
240    #[inline]
241    fn mul(self, other: Self) -> Self {
242        Self([
243            self[0] * other[0],
244            self[1] * other[1],
245            self[2] * other[2],
246            self[3] * other[3],
247        ])
248    }
249}
250
251impl std::ops::Mul<f32> for Rgba {
252    type Output = Self;
253
254    #[inline]
255    fn mul(self, factor: f32) -> Self {
256        Self([
257            self[0] * factor,
258            self[1] * factor,
259            self[2] * factor,
260            self[3] * factor,
261        ])
262    }
263}
264
265impl std::ops::Mul<Rgba> for f32 {
266    type Output = Rgba;
267
268    #[inline]
269    fn mul(self, rgba: Rgba) -> Rgba {
270        Rgba([
271            self * rgba[0],
272            self * rgba[1],
273            self * rgba[2],
274            self * rgba[3],
275        ])
276    }
277}