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    #[doc(alias = "from_grey")]
95    #[inline]
96    pub const fn from_gray(l: f32) -> Self {
97        Self([l, l, l, 1.0])
98    }
99
100    #[inline]
101    pub fn from_luminance_alpha(l: f32, a: f32) -> Self {
102        debug_assert!(0.0 <= l && l <= 1.0);
103        debug_assert!(0.0 <= a && a <= 1.0);
104        Self([l * a, l * a, l * a, a])
105    }
106
107    /// Transparent black
108    #[inline]
109    pub fn from_black_alpha(a: f32) -> Self {
110        debug_assert!(0.0 <= a && a <= 1.0);
111        Self([0.0, 0.0, 0.0, a])
112    }
113
114    /// Transparent white
115    #[inline]
116    pub fn from_white_alpha(a: f32) -> Self {
117        debug_assert!(0.0 <= a && a <= 1.0, "a: {a}");
118        Self([a, a, a, a])
119    }
120
121    /// Return an additive version of this color (alpha = 0)
122    #[inline]
123    pub fn additive(self) -> Self {
124        let [r, g, b, _] = self.0;
125        Self([r, g, b, 0.0])
126    }
127
128    /// Is the alpha=0 ?
129    #[inline]
130    pub fn is_additive(self) -> bool {
131        self.a() == 0.0
132    }
133
134    /// Multiply with e.g. 0.5 to make us half transparent
135    #[inline]
136    pub fn multiply(self, alpha: f32) -> Self {
137        Self([
138            alpha * self[0],
139            alpha * self[1],
140            alpha * self[2],
141            alpha * self[3],
142        ])
143    }
144
145    #[inline]
146    pub fn r(&self) -> f32 {
147        self.0[0]
148    }
149
150    #[inline]
151    pub fn g(&self) -> f32 {
152        self.0[1]
153    }
154
155    #[inline]
156    pub fn b(&self) -> f32 {
157        self.0[2]
158    }
159
160    #[inline]
161    pub fn a(&self) -> f32 {
162        self.0[3]
163    }
164
165    /// How perceptually intense (bright) is the color?
166    #[inline]
167    pub fn intensity(&self) -> f32 {
168        0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b()
169    }
170
171    /// Returns an opaque version of self
172    #[inline]
173    pub fn to_opaque(&self) -> Self {
174        if self.a() == 0.0 {
175            // Additive or fully transparent black.
176            Self::from_rgb(self.r(), self.g(), self.b())
177        } else {
178            // un-multiply alpha:
179            Self::from_rgb(
180                self.r() / self.a(),
181                self.g() / self.a(),
182                self.b() / self.a(),
183            )
184        }
185    }
186
187    /// Premultiplied RGBA
188    #[inline]
189    pub fn to_array(&self) -> [f32; 4] {
190        [self.r(), self.g(), self.b(), self.a()]
191    }
192
193    /// Premultiplied RGBA
194    #[inline]
195    pub fn to_tuple(&self) -> (f32, f32, f32, f32) {
196        (self.r(), self.g(), self.b(), self.a())
197    }
198
199    /// unmultiply the alpha
200    #[inline]
201    pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {
202        let a = self.a();
203        if a == 0.0 {
204            // Additive, let's assume we are black
205            self.0
206        } else {
207            [self.r() / a, self.g() / a, self.b() / a, a]
208        }
209    }
210
211    /// unmultiply the alpha
212    #[inline]
213    pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {
214        let [r, g, b, a] = self.to_rgba_unmultiplied();
215        [
216            gamma_u8_from_linear_f32(r),
217            gamma_u8_from_linear_f32(g),
218            gamma_u8_from_linear_f32(b),
219            linear_u8_from_linear_f32(a.abs()),
220        ]
221    }
222}
223
224impl std::ops::Add for Rgba {
225    type Output = Self;
226
227    #[inline]
228    fn add(self, rhs: Self) -> Self {
229        Self([
230            self[0] + rhs[0],
231            self[1] + rhs[1],
232            self[2] + rhs[2],
233            self[3] + rhs[3],
234        ])
235    }
236}
237
238impl std::ops::Mul for Rgba {
239    type Output = Self;
240
241    #[inline]
242    fn mul(self, other: Self) -> Self {
243        Self([
244            self[0] * other[0],
245            self[1] * other[1],
246            self[2] * other[2],
247            self[3] * other[3],
248        ])
249    }
250}
251
252impl std::ops::Mul<f32> for Rgba {
253    type Output = Self;
254
255    #[inline]
256    fn mul(self, factor: f32) -> Self {
257        Self([
258            self[0] * factor,
259            self[1] * factor,
260            self[2] * factor,
261            self[3] * factor,
262        ])
263    }
264}
265
266impl std::ops::Mul<Rgba> for f32 {
267    type Output = Rgba;
268
269    #[inline]
270    fn mul(self, rgba: Rgba) -> Rgba {
271        Rgba([
272            self * rgba[0],
273            self * rgba[1],
274            self * rgba[2],
275            self * rgba[3],
276        ])
277    }
278}