1use tiny_skia_path::{NormalizedF32, Scalar};
8
9pub type AlphaU8 = u8;
11
12pub const ALPHA_U8_TRANSPARENT: AlphaU8 = 0x00;
14
15pub const ALPHA_U8_OPAQUE: AlphaU8 = 0xFF;
17
18pub const ALPHA_TRANSPARENT: NormalizedF32 = NormalizedF32::ZERO;
20
21pub const ALPHA_OPAQUE: NormalizedF32 = NormalizedF32::ONE;
23
24#[repr(transparent)]
28#[derive(Copy, Clone, PartialEq)]
29pub struct ColorU8([u8; 4]);
30
31impl ColorU8 {
32 pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
34 ColorU8([r, g, b, a])
35 }
36
37 pub const fn red(self) -> u8 {
39 self.0[0]
40 }
41
42 pub const fn green(self) -> u8 {
44 self.0[1]
45 }
46
47 pub const fn blue(self) -> u8 {
49 self.0[2]
50 }
51
52 pub const fn alpha(self) -> u8 {
54 self.0[3]
55 }
56
57 pub fn is_opaque(&self) -> bool {
61 self.alpha() == ALPHA_U8_OPAQUE
62 }
63
64 pub fn premultiply(&self) -> PremultipliedColorU8 {
66 let a = self.alpha();
67 if a != ALPHA_U8_OPAQUE {
68 PremultipliedColorU8::from_rgba_unchecked(
69 premultiply_u8(self.red(), a),
70 premultiply_u8(self.green(), a),
71 premultiply_u8(self.blue(), a),
72 a,
73 )
74 } else {
75 PremultipliedColorU8::from_rgba_unchecked(self.red(), self.green(), self.blue(), a)
76 }
77 }
78}
79
80impl core::fmt::Debug for ColorU8 {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 f.debug_struct("ColorU8")
83 .field("r", &self.red())
84 .field("g", &self.green())
85 .field("b", &self.blue())
86 .field("a", &self.alpha())
87 .finish()
88 }
89}
90
91#[repr(transparent)]
95#[derive(Copy, Clone, PartialEq)]
96pub struct PremultipliedColorU8([u8; 4]);
97
98unsafe impl bytemuck::Zeroable for PremultipliedColorU8 {}
100unsafe impl bytemuck::Pod for PremultipliedColorU8 {}
101
102impl PremultipliedColorU8 {
103 pub const TRANSPARENT: Self = PremultipliedColorU8::from_rgba_unchecked(0, 0, 0, 0);
105
106 pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Option<Self> {
110 if r <= a && g <= a && b <= a {
111 Some(PremultipliedColorU8([r, g, b, a]))
112 } else {
113 None
114 }
115 }
116
117 pub(crate) const fn from_rgba_unchecked(r: u8, g: u8, b: u8, a: u8) -> Self {
119 PremultipliedColorU8([r, g, b, a])
120 }
121
122 pub const fn red(self) -> u8 {
126 self.0[0]
127 }
128
129 pub const fn green(self) -> u8 {
133 self.0[1]
134 }
135
136 pub const fn blue(self) -> u8 {
140 self.0[2]
141 }
142
143 pub const fn alpha(self) -> u8 {
145 self.0[3]
146 }
147
148 pub fn is_opaque(&self) -> bool {
152 self.alpha() == ALPHA_U8_OPAQUE
153 }
154
155 pub fn demultiply(&self) -> ColorU8 {
157 let alpha = self.alpha();
158 if alpha == ALPHA_U8_OPAQUE {
159 ColorU8(self.0)
160 } else {
161 let a = alpha as f64 / 255.0;
162 ColorU8::from_rgba(
163 (self.red() as f64 / a + 0.5) as u8,
164 (self.green() as f64 / a + 0.5) as u8,
165 (self.blue() as f64 / a + 0.5) as u8,
166 alpha,
167 )
168 }
169 }
170}
171
172impl core::fmt::Debug for PremultipliedColorU8 {
173 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174 f.debug_struct("PremultipliedColorU8")
175 .field("r", &self.red())
176 .field("g", &self.green())
177 .field("b", &self.blue())
178 .field("a", &self.alpha())
179 .finish()
180 }
181}
182
183#[derive(Copy, Clone, PartialEq, Debug)]
189pub struct Color {
190 r: NormalizedF32,
191 g: NormalizedF32,
192 b: NormalizedF32,
193 a: NormalizedF32,
194}
195
196const NV_ZERO: NormalizedF32 = NormalizedF32::ZERO;
197const NV_ONE: NormalizedF32 = NormalizedF32::ONE;
198
199impl Color {
200 pub const TRANSPARENT: Color = Color {
202 r: NV_ZERO,
203 g: NV_ZERO,
204 b: NV_ZERO,
205 a: NV_ZERO,
206 };
207 pub const BLACK: Color = Color {
209 r: NV_ZERO,
210 g: NV_ZERO,
211 b: NV_ZERO,
212 a: NV_ONE,
213 };
214 pub const WHITE: Color = Color {
216 r: NV_ONE,
217 g: NV_ONE,
218 b: NV_ONE,
219 a: NV_ONE,
220 };
221
222 pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Option<Self> {
226 Some(Color {
227 r: NormalizedF32::new(r)?,
228 g: NormalizedF32::new(g)?,
229 b: NormalizedF32::new(b)?,
230 a: NormalizedF32::new(a)?,
231 })
232 }
233
234 pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
238 Color {
239 r: NormalizedF32::new_u8(r),
240 g: NormalizedF32::new_u8(g),
241 b: NormalizedF32::new_u8(b),
242 a: NormalizedF32::new_u8(a),
243 }
244 }
245
246 pub fn red(&self) -> f32 {
250 self.r.get()
251 }
252
253 pub fn green(&self) -> f32 {
257 self.g.get()
258 }
259
260 pub fn blue(&self) -> f32 {
264 self.b.get()
265 }
266
267 pub fn alpha(&self) -> f32 {
271 self.a.get()
272 }
273
274 pub fn set_red(&mut self, c: f32) {
278 self.r = NormalizedF32::new_clamped(c);
279 }
280
281 pub fn set_green(&mut self, c: f32) {
285 self.g = NormalizedF32::new_clamped(c);
286 }
287
288 pub fn set_blue(&mut self, c: f32) {
292 self.b = NormalizedF32::new_clamped(c);
293 }
294
295 pub fn set_alpha(&mut self, c: f32) {
299 self.a = NormalizedF32::new_clamped(c);
300 }
301
302 pub fn apply_opacity(&mut self, opacity: f32) {
309 self.a = NormalizedF32::new_clamped(self.a.get() * opacity.bound(0.0, 1.0));
310 }
311
312 pub fn is_opaque(&self) -> bool {
316 self.a == ALPHA_OPAQUE
317 }
318
319 pub fn premultiply(&self) -> PremultipliedColor {
321 if self.is_opaque() {
322 PremultipliedColor {
323 r: self.r,
324 g: self.g,
325 b: self.b,
326 a: self.a,
327 }
328 } else {
329 PremultipliedColor {
330 r: NormalizedF32::new_clamped(self.r.get() * self.a.get()),
331 g: NormalizedF32::new_clamped(self.g.get() * self.a.get()),
332 b: NormalizedF32::new_clamped(self.b.get() * self.a.get()),
333 a: self.a,
334 }
335 }
336 }
337
338 pub fn to_color_u8(&self) -> ColorU8 {
340 let c = color_f32_to_u8(self.r, self.g, self.b, self.a);
341 ColorU8::from_rgba(c[0], c[1], c[2], c[3])
342 }
343}
344
345#[derive(Copy, Clone, PartialEq, Debug)]
352pub struct PremultipliedColor {
353 r: NormalizedF32,
354 g: NormalizedF32,
355 b: NormalizedF32,
356 a: NormalizedF32,
357}
358
359impl PremultipliedColor {
360 pub fn red(&self) -> f32 {
365 self.r.get()
366 }
367
368 pub fn green(&self) -> f32 {
373 self.g.get()
374 }
375
376 pub fn blue(&self) -> f32 {
381 self.b.get()
382 }
383
384 pub fn alpha(&self) -> f32 {
388 self.a.get()
389 }
390
391 pub fn demultiply(&self) -> Color {
393 let a = self.a.get();
394 if a == 0.0 {
395 Color::TRANSPARENT
396 } else {
397 Color {
398 r: NormalizedF32::new_clamped(self.r.get() / a),
399 g: NormalizedF32::new_clamped(self.g.get() / a),
400 b: NormalizedF32::new_clamped(self.b.get() / a),
401 a: self.a,
402 }
403 }
404 }
405
406 pub fn to_color_u8(&self) -> PremultipliedColorU8 {
408 let c = color_f32_to_u8(self.r, self.g, self.b, self.a);
409 PremultipliedColorU8::from_rgba_unchecked(c[0], c[1], c[2], c[3])
410 }
411}
412
413pub fn premultiply_u8(c: u8, a: u8) -> u8 {
415 let prod = u32::from(c) * u32::from(a) + 128;
416 ((prod + (prod >> 8)) >> 8) as u8
417}
418
419fn color_f32_to_u8(
420 r: NormalizedF32,
421 g: NormalizedF32,
422 b: NormalizedF32,
423 a: NormalizedF32,
424) -> [u8; 4] {
425 [
426 (r.get() * 255.0 + 0.5) as u8,
427 (g.get() * 255.0 + 0.5) as u8,
428 (b.get() * 255.0 + 0.5) as u8,
429 (a.get() * 255.0 + 0.5) as u8,
430 ]
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn premultiply_u8() {
439 assert_eq!(
440 ColorU8::from_rgba(10, 20, 30, 40).premultiply(),
441 PremultipliedColorU8::from_rgba_unchecked(2, 3, 5, 40)
442 );
443 }
444
445 #[test]
446 fn premultiply_u8_opaque() {
447 assert_eq!(
448 ColorU8::from_rgba(10, 20, 30, 255).premultiply(),
449 PremultipliedColorU8::from_rgba_unchecked(10, 20, 30, 255)
450 );
451 }
452
453 #[test]
454 fn demultiply_u8_1() {
455 assert_eq!(
456 PremultipliedColorU8::from_rgba_unchecked(2, 3, 5, 40).demultiply(),
457 ColorU8::from_rgba(13, 19, 32, 40)
458 );
459 }
460
461 #[test]
462 fn demultiply_u8_2() {
463 assert_eq!(
464 PremultipliedColorU8::from_rgba_unchecked(10, 20, 30, 255).demultiply(),
465 ColorU8::from_rgba(10, 20, 30, 255)
466 );
467 }
468
469 #[test]
470 fn demultiply_u8_3() {
471 assert_eq!(
472 PremultipliedColorU8::from_rgba_unchecked(153, 99, 54, 180).demultiply(),
473 ColorU8::from_rgba(217, 140, 77, 180)
474 );
475 }
476
477 #[test]
478 fn bytemuck_casts_rgba() {
479 let slice = &[
480 PremultipliedColorU8::from_rgba_unchecked(0, 1, 2, 3),
481 PremultipliedColorU8::from_rgba_unchecked(10, 11, 12, 13),
482 ];
483 let bytes: &[u8] = bytemuck::cast_slice(slice);
484 assert_eq!(bytes, &[0, 1, 2, 3, 10, 11, 12, 13]);
485 }
486}