1use crate::Color32;
2
3#[repr(C)]
7#[derive(Clone, Copy, Debug, Default, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
9#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
10pub struct Rgba(pub(crate) [f32; 4]);
11
12impl std::ops::Index<usize> for Rgba {
13 type Output = f32;
14
15 #[inline]
16 fn index(&self, index: usize) -> &f32 {
17 &self.0[index]
18 }
19}
20
21impl std::ops::IndexMut<usize> for Rgba {
22 #[inline]
23 fn index_mut(&mut self, index: usize) -> &mut f32 {
24 &mut self.0[index]
25 }
26}
27
28#[inline]
30pub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {
31 if f == 0.0 {
32 state.write_u8(0);
33 } else if f.is_nan() {
34 state.write_u8(1);
35 } else {
36 use std::hash::Hash as _;
37 f.to_bits().hash(state);
38 }
39}
40
41impl std::hash::Hash for Rgba {
42 #[inline]
43 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
44 crate::f32_hash(state, self.0[0]);
45 crate::f32_hash(state, self.0[1]);
46 crate::f32_hash(state, self.0[2]);
47 crate::f32_hash(state, self.0[3]);
48 }
49}
50
51impl Rgba {
52 pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0.0, 0.0, 0.0, 0.0);
53 pub const BLACK: Self = Self::from_rgb(0.0, 0.0, 0.0);
54 pub const WHITE: Self = Self::from_rgb(1.0, 1.0, 1.0);
55 pub const RED: Self = Self::from_rgb(1.0, 0.0, 0.0);
56 pub const GREEN: Self = Self::from_rgb(0.0, 1.0, 0.0);
57 pub const BLUE: Self = Self::from_rgb(0.0, 0.0, 1.0);
58
59 #[inline]
60 pub const fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
61 Self([r, g, b, a])
62 }
63
64 #[inline]
65 pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
66 Self([r * a, g * a, b * a, a])
67 }
68
69 #[inline]
70 pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
71 Self::from(Color32::from_rgba_premultiplied(r, g, b, a))
72 }
73
74 #[inline]
75 pub fn from_srgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
76 Self::from(Color32::from_rgba_unmultiplied(r, g, b, a))
77 }
78
79 #[inline]
80 pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
81 Self([r, g, b, 1.0])
82 }
83
84 #[doc(alias = "from_grey")]
85 #[inline]
86 pub const fn from_gray(l: f32) -> Self {
87 Self([l, l, l, 1.0])
88 }
89
90 #[inline]
91 pub fn from_luminance_alpha(l: f32, a: f32) -> Self {
92 debug_assert!(
93 0.0 <= l && l <= 1.0,
94 "l should be in the range [0, 1], but was {l}"
95 );
96 debug_assert!(
97 0.0 <= a && a <= 1.0,
98 "a should be in the range [0, 1], but was {a}"
99 );
100 Self([l * a, l * a, l * a, a])
101 }
102
103 #[inline]
105 pub fn from_black_alpha(a: f32) -> Self {
106 debug_assert!(
107 0.0 <= a && a <= 1.0,
108 "a should be in the range [0, 1], but was {a}"
109 );
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 crate::Color32::from(*self).to_srgba_unmultiplied()
214 }
215
216 pub fn blend(self, on_top: Self) -> Self {
218 self.multiply(1.0 - on_top.a()) + on_top
219 }
220}
221
222impl std::ops::Add for Rgba {
223 type Output = Self;
224
225 #[inline]
226 fn add(self, rhs: Self) -> Self {
227 Self([
228 self[0] + rhs[0],
229 self[1] + rhs[1],
230 self[2] + rhs[2],
231 self[3] + rhs[3],
232 ])
233 }
234}
235
236impl std::ops::Mul for Rgba {
237 type Output = Self;
238
239 #[inline]
240 fn mul(self, other: Self) -> Self {
241 Self([
242 self[0] * other[0],
243 self[1] * other[1],
244 self[2] * other[2],
245 self[3] * other[3],
246 ])
247 }
248}
249
250impl std::ops::Mul<f32> for Rgba {
251 type Output = Self;
252
253 #[inline]
254 fn mul(self, factor: f32) -> Self {
255 Self([
256 self[0] * factor,
257 self[1] * factor,
258 self[2] * factor,
259 self[3] * factor,
260 ])
261 }
262}
263
264impl std::ops::Mul<Rgba> for f32 {
265 type Output = Rgba;
266
267 #[inline]
268 fn mul(self, rgba: Rgba) -> Rgba {
269 Rgba([
270 self * rgba[0],
271 self * rgba[1],
272 self * rgba[2],
273 self * rgba[3],
274 ])
275 }
276}
277
278#[cfg(test)]
279mod test {
280
281 use super::*;
282
283 fn test_rgba() -> impl Iterator<Item = [u8; 4]> {
284 [
285 [0, 0, 0, 0],
286 [0, 0, 0, 255],
287 [10, 0, 30, 0],
288 [10, 0, 30, 40],
289 [10, 100, 200, 0],
290 [10, 100, 200, 100],
291 [10, 100, 200, 200],
292 [10, 100, 200, 255],
293 [10, 100, 200, 40],
294 [10, 20, 0, 0],
295 [10, 20, 0, 255],
296 [10, 20, 30, 255],
297 [10, 20, 30, 40],
298 [255, 255, 255, 0],
299 [255, 255, 255, 255],
300 ]
301 .into_iter()
302 }
303
304 #[test]
305 fn test_rgba_blend() {
306 let opaque = Rgba::from_rgb(0.4, 0.5, 0.6);
307 let transparent = Rgba::from_rgb(1.0, 0.5, 0.0).multiply(0.3);
308 assert_eq!(
309 transparent.blend(opaque),
310 opaque,
311 "Opaque on top of transparent"
312 );
313 assert_eq!(
314 opaque.blend(transparent),
315 Rgba::from_rgb(
316 0.7 * 0.4 + 0.3 * 1.0,
317 0.7 * 0.5 + 0.3 * 0.5,
318 0.7 * 0.6 + 0.3 * 0.0
319 ),
320 "Transparent on top of opaque"
321 );
322 }
323
324 #[test]
325 fn test_rgba_roundtrip() {
326 for in_rgba in test_rgba() {
327 let [r, g, b, a] = in_rgba;
328 if a == 0 {
329 continue;
330 }
331 let rgba = Rgba::from_srgba_unmultiplied(r, g, b, a);
332 let out_rgba = rgba.to_srgba_unmultiplied();
333
334 if a == 255 {
335 assert_eq!(in_rgba, out_rgba);
336 } else {
337 for (&a, &b) in in_rgba.iter().zip(out_rgba.iter()) {
340 assert!(a.abs_diff(b) <= 3, "{in_rgba:?} != {out_rgba:?}");
341 }
342 }
343 }
344 }
345}