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