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#[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#[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 #[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 #[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 #[inline]
122 pub fn additive(self) -> Self {
123 let [r, g, b, _] = self.0;
124 Self([r, g, b, 0.0])
125 }
126
127 #[inline]
129 pub fn is_additive(self) -> bool {
130 self.a() == 0.0
131 }
132
133 #[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 #[inline]
166 pub fn intensity(&self) -> f32 {
167 0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b()
168 }
169
170 #[inline]
172 pub fn to_opaque(&self) -> Self {
173 if self.a() == 0.0 {
174 Self::from_rgb(self.r(), self.g(), self.b())
176 } else {
177 Self::from_rgb(
179 self.r() / self.a(),
180 self.g() / self.a(),
181 self.b() / self.a(),
182 )
183 }
184 }
185
186 #[inline]
188 pub fn to_array(&self) -> [f32; 4] {
189 [self.r(), self.g(), self.b(), self.a()]
190 }
191
192 #[inline]
194 pub fn to_tuple(&self) -> (f32, f32, f32, f32) {
195 (self.r(), self.g(), self.b(), self.a())
196 }
197
198 #[inline]
200 pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {
201 let a = self.a();
202 if a == 0.0 {
203 self.0
205 } else {
206 [self.r() / a, self.g() / a, self.b() / a, a]
207 }
208 }
209
210 #[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}