1#[inline]
7fn clamp01(v: f32) -> f32 {
8 v.clamp(0.0, 1.0)
9}
10
11#[inline]
12fn to_u8(v: f32) -> u8 {
13 (clamp01(v) * 255.0 + 0.5) as u8
14}
15
16#[derive(Copy, Clone, Debug, PartialEq)]
19pub struct Color {
20 r: f32,
21 g: f32,
22 b: f32,
23 a: f32,
24}
25
26impl Color {
27 pub const TRANSPARENT: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
28 pub const BLACK: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
29 pub const WHITE: Color = Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
30
31 pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
33 Color { r: clamp01(r), g: clamp01(g), b: clamp01(b), a: clamp01(a) }
34 }
35
36 pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Color {
38 Color {
39 r: r as f32 / 255.0,
40 g: g as f32 / 255.0,
41 b: b as f32 / 255.0,
42 a: a as f32 / 255.0,
43 }
44 }
45
46 #[inline]
47 pub fn red(&self) -> f32 {
48 self.r
49 }
50 #[inline]
51 pub fn green(&self) -> f32 {
52 self.g
53 }
54 #[inline]
55 pub fn blue(&self) -> f32 {
56 self.b
57 }
58 #[inline]
59 pub fn alpha(&self) -> f32 {
60 self.a
61 }
62
63 #[inline]
65 pub fn is_opaque(&self) -> bool {
66 self.a >= 1.0
67 }
68
69 pub fn set_alpha(&mut self, a: f32) {
71 self.a = clamp01(a);
72 }
73
74 #[inline]
76 pub fn premultiply(&self) -> PremultipliedColor {
77 PremultipliedColor { r: self.r * self.a, g: self.g * self.a, b: self.b * self.a, a: self.a }
78 }
79
80 pub fn to_color_u8(&self) -> ColorU8 {
82 ColorU8 { r: to_u8(self.r), g: to_u8(self.g), b: to_u8(self.b), a: to_u8(self.a) }
83 }
84}
85
86#[derive(Copy, Clone, Debug, PartialEq, Eq)]
88pub struct ColorU8 {
89 r: u8,
90 g: u8,
91 b: u8,
92 a: u8,
93}
94
95impl ColorU8 {
96 pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> ColorU8 {
97 ColorU8 { r, g, b, a }
98 }
99 #[inline]
100 pub fn red(&self) -> u8 {
101 self.r
102 }
103 #[inline]
104 pub fn green(&self) -> u8 {
105 self.g
106 }
107 #[inline]
108 pub fn blue(&self) -> u8 {
109 self.b
110 }
111 #[inline]
112 pub fn alpha(&self) -> u8 {
113 self.a
114 }
115
116 pub fn to_color(self) -> Color {
117 Color::from_rgba8(self.r, self.g, self.b, self.a)
118 }
119
120 pub fn premultiply(self) -> PremultipliedColorU8 {
122 let a = self.a as u16;
123 let m = |c: u8| ((c as u16 * a + 127) / 255) as u8;
124 PremultipliedColorU8 { r: m(self.r), g: m(self.g), b: m(self.b), a: self.a }
125 }
126}
127
128#[derive(Copy, Clone, Debug, PartialEq)]
130pub struct PremultipliedColor {
131 pub r: f32,
132 pub g: f32,
133 pub b: f32,
134 pub a: f32,
135}
136
137impl PremultipliedColor {
138 pub fn to_color_u8(&self) -> PremultipliedColorU8 {
140 let a = clamp01(self.a);
142 PremultipliedColorU8 {
143 r: to_u8(self.r.min(a)),
144 g: to_u8(self.g.min(a)),
145 b: to_u8(self.b.min(a)),
146 a: to_u8(a),
147 }
148 }
149}
150
151#[derive(Copy, Clone, Debug, PartialEq, Eq)]
153pub struct PremultipliedColorU8 {
154 r: u8,
155 g: u8,
156 b: u8,
157 a: u8,
158}
159
160impl PremultipliedColorU8 {
161 pub const TRANSPARENT: PremultipliedColorU8 = PremultipliedColorU8 { r: 0, g: 0, b: 0, a: 0 };
162
163 pub const fn from_rgba_unchecked(r: u8, g: u8, b: u8, a: u8) -> PremultipliedColorU8 {
165 PremultipliedColorU8 { r, g, b, a }
166 }
167
168 #[inline]
169 pub fn red(&self) -> u8 {
170 self.r
171 }
172 #[inline]
173 pub fn green(&self) -> u8 {
174 self.g
175 }
176 #[inline]
177 pub fn blue(&self) -> u8 {
178 self.b
179 }
180 #[inline]
181 pub fn alpha(&self) -> u8 {
182 self.a
183 }
184
185 pub fn demultiply(&self) -> ColorU8 {
187 if self.a == 0 {
188 ColorU8 { r: 0, g: 0, b: 0, a: 0 }
189 } else {
190 let a = self.a as u16;
191 let d = |c: u8| (((c as u16) * 255 + a / 2) / a).min(255) as u8;
192 ColorU8 { r: d(self.r), g: d(self.g), b: d(self.b), a: self.a }
193 }
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn premultiply_roundtrip() {
203 let c = ColorU8::from_rgba(200, 100, 50, 128);
204 let p = c.premultiply();
205 let back = p.demultiply();
206 assert!((back.red() as i32 - 200).abs() <= 2);
208 assert!((back.green() as i32 - 100).abs() <= 2);
209 assert_eq!(back.alpha(), 128);
210 }
211
212 #[test]
213 fn opaque_premultiply_identity() {
214 let c = Color::from_rgba8(10, 20, 30, 255).premultiply().to_color_u8();
215 assert_eq!(c, PremultipliedColorU8::from_rgba_unchecked(10, 20, 30, 255));
216 }
217}