1#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
9pub struct Color([u8; 4]);
10
11impl Color {
12 pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0, 0, 0, 0);
15
16 pub const PLACEHOLDER: Self = Self::from_rgba_premultiplied(64, 254, 0, 128);
19
20 pub const BLACK: Self = Self::from_rgb(0, 0, 0);
21 pub const WHITE: Self = Self::from_rgb(255, 255, 255);
22 pub const GRAY: Self = Self::from_rgb(160, 160, 160);
23
24 #[inline]
26 pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
27 Self([r, g, b, 255])
28 }
29
30 #[inline]
31 pub const fn from_rgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
32 Self([r, g, b, a])
33 }
34
35 #[inline]
38 pub const fn from_rgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
39 match a {
40 0 => Self::TRANSPARENT,
41 255 => Self::from_rgb(r, g, b),
42 a => {
43 let alpha = a as f32 / 255.0;
44 Self::from_rgba_premultiplied(
45 round_u8(r as f32 * alpha),
46 round_u8(g as f32 * alpha),
47 round_u8(b as f32 * alpha),
48 a,
49 )
50 }
51 }
52 }
53
54 #[inline]
56 pub const fn r(self) -> u8 {
57 self.0[0]
58 }
59
60 #[inline]
62 pub const fn g(self) -> u8 {
63 self.0[1]
64 }
65
66 #[inline]
68 pub const fn b(self) -> u8 {
69 self.0[2]
70 }
71
72 #[inline]
73 pub const fn a(self) -> u8 {
74 self.0[3]
75 }
76
77 #[inline]
78 pub const fn is_opaque(self) -> bool {
79 self.a() == 255
80 }
81
82 #[inline]
84 pub const fn to_array(self) -> [u8; 4] {
85 self.0
86 }
87
88 #[inline]
90 pub const fn to_tuple(self) -> (u8, u8, u8, u8) {
91 (self.r(), self.g(), self.b(), self.a())
92 }
93
94 #[inline]
97 pub fn to_srgba_unmultiplied(self) -> [u8; 4] {
98 let [r, g, b, a] = self.0;
99 match a {
100 0 | 255 => self.0,
101 a => {
102 let factor = 255.0 / a as f32;
103 [
104 round_u8(factor * r as f32),
105 round_u8(factor * g as f32),
106 round_u8(factor * b as f32),
107 a,
108 ]
109 }
110 }
111 }
112
113 #[must_use]
116 #[inline]
117 pub fn gamma_multiply(self, factor: f32) -> Self {
118 debug_assert!(
119 0.0 <= factor && factor.is_finite(),
120 "gamma_multiply factor should be finite and non-negative, but was {factor}"
121 );
122 let Self([r, g, b, a]) = self;
123 Self([
124 (r as f32 * factor + 0.5) as u8,
125 (g as f32 * factor + 0.5) as u8,
126 (b as f32 * factor + 0.5) as u8,
127 (a as f32 * factor + 0.5) as u8,
128 ])
129 }
130}
131
132#[inline]
135const fn round_u8(value: f32) -> u8 {
136 (value + 0.5) as u8
137}
138
139impl std::fmt::Debug for Color {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 let [r, g, b, a] = self.0;
143 write!(f, "#{r:02X}_{g:02X}_{b:02X}_{a:02X}")
144 }
145}