1use crate::rgb::Rgb;
8use crate::routines::{
9 op_color_burn, op_color_dodge, op_darken, op_difference, op_exclusion, op_hard_light,
10 op_hard_mix, op_lighten, op_linear_burn, op_linear_light, op_overlay, op_pin_light, op_reflect,
11 op_screen, op_soft_light, op_vivid_light,
12};
13use crate::{
14 adjust_saturation, clip_color, color_add, pdf_lum, EuclideanDistance, TaxicabDistance,
15 TransferFunction,
16};
17use half::f16;
18use num_traits::{clamp, AsPrimitive, Bounded, Float, Num, Pow};
19use std::cmp::{max, min, Ordering};
20use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, Neg, Sub};
21
22#[repr(C)]
23#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
24pub struct Rgba<T> {
26 pub r: T,
28 pub g: T,
30 pub b: T,
32 pub a: T,
34}
35
36impl Rgba<f16> {
37 pub fn from_rgb(r: f16, g: f16, b: f16) -> Rgba<f16> {
38 Rgba {
39 r,
40 g,
41 b,
42 a: f16::from_f32(1f32),
43 }
44 }
45}
46
47impl<T> Rgba<T> {
48 #[inline]
49 pub fn new(r: T, g: T, b: T, a: T) -> Rgba<T> {
50 Rgba { r, g, b, a }
51 }
52}
53
54impl<T> Rgba<T>
55where
56 T: Copy,
57{
58 pub fn dup(v: T) -> Self {
59 Rgba::new(v, v, v, v)
60 }
61}
62
63macro_rules! generated_float_definition_rgba {
64 ($T: ty) => {
65 impl Rgba<$T> {
66 #[inline]
67 pub fn zeroed() -> Rgba<$T> {
68 Rgba::<$T>::dup(0.)
69 }
70
71 #[inline]
72 pub fn ones() -> Rgba<$T> {
73 Rgba::<$T>::new(1., 1., 1., 1.)
74 }
75
76 #[inline]
77 pub fn white() -> Rgba<$T> {
78 Rgba::<$T>::new(1., 1., 1., 1.)
79 }
80
81 #[inline]
82 pub fn black() -> Rgba<$T> {
83 Rgba::<$T>::new(0., 0., 0., 1.)
84 }
85
86 #[inline]
87 pub fn from_rgb(r: $T, g: $T, b: $T) -> Rgba<$T> {
88 Rgba { r, g, b, a: 1. }
89 }
90 }
91 };
92}
93
94generated_float_definition_rgba!(f32);
95generated_float_definition_rgba!(f64);
96
97macro_rules! generated_integral_definition_rgba {
98 ($T: ty) => {
99 impl Rgba<$T> {
100 #[inline]
101 pub fn zeroed() -> Rgba<$T> {
102 Rgba::<$T>::new(0, 0, 0, 0)
103 }
104
105 #[inline]
106 pub fn capped() -> Rgba<$T> {
107 Rgba::<$T>::new(<$T>::MAX, <$T>::MAX, <$T>::MAX, <$T>::MAX)
108 }
109
110 #[inline]
111 pub fn black() -> Rgba<$T> {
112 Rgba::<$T>::new(0, 0, 0, <$T>::MAX)
113 }
114
115 #[inline]
116 pub fn white() -> Rgba<$T> {
117 Rgba::<$T>::capped()
118 }
119 }
120 };
121}
122
123generated_integral_definition_rgba!(u8);
124generated_integral_definition_rgba!(u16);
125generated_integral_definition_rgba!(i8);
126generated_integral_definition_rgba!(i16);
127generated_integral_definition_rgba!(i32);
128generated_integral_definition_rgba!(u32);
129
130macro_rules! generated_default_definition_rgba {
131 ($T: ty) => {
132 impl Default for Rgba<$T> {
133 fn default() -> Self {
134 Rgba::<$T>::zeroed()
135 }
136 }
137 };
138}
139
140generated_default_definition_rgba!(u8);
141generated_default_definition_rgba!(u16);
142generated_default_definition_rgba!(i8);
143generated_default_definition_rgba!(i16);
144generated_default_definition_rgba!(i32);
145generated_default_definition_rgba!(u32);
146generated_default_definition_rgba!(f32);
147generated_default_definition_rgba!(f64);
148
149impl Rgba<f32> {
150 #[inline]
152 pub fn blend_over_alpha(&self, color_foreground: Rgba<f32>) -> Rgba<f32> {
153 let a_dst = self.a + color_foreground.a * (1. - self.a);
154 if a_dst == 0. {
155 return Rgba::<f32>::zeroed();
156 }
157 let a_dst_recpeq = 1. / a_dst;
158 let out_r = ((1. - self.a) * color_foreground.a * color_foreground.r + self.a * self.r)
159 * a_dst_recpeq;
160 let out_g = ((1. - self.a) * color_foreground.a * color_foreground.g + self.a * self.g)
161 * a_dst_recpeq;
162 let out_b = ((1. - self.a) * color_foreground.a * color_foreground.b + self.a * self.b)
163 * a_dst_recpeq;
164 Rgba::<f32>::new(out_r, out_g, out_b, a_dst)
165 }
166
167 #[inline(always)]
171 pub fn blend_over_with_op(
172 &self,
173 color_foreground: Rgba<f32>,
174 op: fn(f32, f32) -> f32,
175 ) -> Rgba<f32> {
176 let a_dst = self.a + color_foreground.a * (1. - self.a);
177 if a_dst == 0. {
178 return Rgba::<f32>::zeroed();
179 }
180 let a_dst_recpeq = 1. / a_dst;
181 let new_r = if a_dst != 0. {
182 (1. - color_foreground.a) * self.r
183 + (1. - self.a) * color_foreground.r
184 + self.a * color_foreground.a * op(self.r, color_foreground.r)
185 } else {
186 0.
187 } * a_dst_recpeq;
188 let new_g = if a_dst != 0. {
189 (1. - color_foreground.a) * self.g
190 + (1. - self.a) * color_foreground.g
191 + self.a * color_foreground.a * op(self.g, color_foreground.g)
192 } else {
193 0.
194 } * a_dst_recpeq;
195 let new_b = if a_dst != 0. {
196 (1. - color_foreground.a) * self.b
197 + (1. - self.a) * color_foreground.b
198 + self.a * color_foreground.a * op(self.b, color_foreground.b)
199 } else {
200 0.
201 } * a_dst_recpeq;
202
203 Rgba::<f32>::new(new_r, new_g, new_b, a_dst)
204 }
205
206 #[inline]
207 pub fn blend_overlay(&self, other: Rgba<f32>) -> Rgba<f32> {
208 self.blend_over_with_op(other, op_overlay)
209 }
210
211 #[inline]
212 pub fn blend_add(&self, other: Rgba<f32>) -> Rgba<f32> {
216 let new_r = color_add!(self.r, other.r);
217 let new_g = color_add!(self.g, other.g);
218 let new_b = color_add!(self.b, other.b);
219 let alpha = 1.0f32.min(self.a + other.a);
220 if alpha == 0. {
221 return Rgba::<f32>::zeroed();
222 }
223 let recprec_alpha = 1. / alpha;
224 Rgba::<f32>::new(
225 new_r * recprec_alpha,
226 new_g * recprec_alpha,
227 new_b * recprec_alpha,
228 alpha,
229 )
230 }
231
232 #[inline]
233 pub fn blend_saturate(&self, other: Rgba<f32>) -> Rgba<f32> {
237 let alpha = 1.0f32.min(self.a + other.a);
238 let src = self;
239 let dst = other;
240 let recip_alpha = 1. / alpha;
241 let new_r = if alpha != 0.0 {
242 (src.r * src.a.min(1.0 - dst.a) + dst.r * dst.a) * recip_alpha
243 } else {
244 0.0
245 };
246 let new_g = if alpha != 0.0 {
247 (src.g * src.a.min(1.0 - dst.a) + dst.g * dst.a) * recip_alpha
248 } else {
249 0.0
250 };
251 let new_b = if alpha != 0.0 {
252 (src.b * src.a.min(1.0 - dst.a) + dst.b * dst.a) * recip_alpha
253 } else {
254 0.0
255 };
256 Rgba::<f32>::new(new_r, new_g, new_b, alpha)
257 }
258
259 #[inline]
260 pub fn blend_substract(&self, other: Rgba<f32>) -> Rgba<f32> {
261 self.blend_over_with_op(other, op_linear_burn)
262 }
263
264 #[inline]
265 pub fn blend_lighten(&self, other: Rgba<f32>) -> Rgba<f32> {
266 self.blend_over_with_op(other, op_lighten)
267 }
268
269 #[inline]
270 pub fn blend_darken(&self, other: Rgba<f32>) -> Rgba<f32> {
271 self.blend_over_with_op(other, op_darken)
272 }
273
274 #[inline]
275 pub fn blend_color_burn(&self, other: Rgba<f32>) -> Rgba<f32> {
276 self.blend_over_with_op(other, op_color_burn)
277 }
278
279 #[inline]
280 pub fn blend_color_dodge(&self, other: Rgba<f32>) -> Rgba<f32> {
281 self.blend_over_with_op(other, op_color_dodge)
282 }
283
284 #[inline]
285 pub fn blend_screen(&self, other: Rgba<f32>) -> Rgba<f32> {
286 self.blend_over_with_op(other, op_screen)
287 }
288
289 #[inline]
290 pub fn blend_linear_light(&self, other: Rgba<f32>) -> Rgba<f32> {
291 self.blend_over_with_op(other, op_linear_light)
292 }
293
294 #[inline]
295 pub fn blend_vivid_light(&self, other: Rgba<f32>) -> Rgba<f32> {
296 self.blend_over_with_op(other, op_vivid_light)
297 }
298
299 #[inline]
300 pub fn blend_pin_light(&self, other: Rgba<f32>) -> Rgba<f32> {
301 self.blend_over_with_op(other, op_pin_light)
302 }
303
304 #[inline]
305 pub fn blend_hard_mix(&self, other: Rgba<f32>) -> Rgba<f32> {
306 self.blend_over_with_op(other, op_hard_mix)
307 }
308
309 #[inline]
310 pub fn blend_reflect(&self, other: Rgba<f32>) -> Rgba<f32> {
311 self.blend_over_with_op(other, op_reflect)
312 }
313
314 #[inline]
315 pub fn blend_difference(&self, other: Rgba<f32>) -> Rgba<f32> {
316 self.blend_over_with_op(other, op_difference)
317 }
318
319 #[inline]
320 pub fn blend_hard_light(&self, other: Rgba<f32>) -> Rgba<f32> {
321 self.blend_over_with_op(other, op_hard_light)
322 }
323
324 #[inline]
325 pub fn blend_soft_light(&self, other: Rgba<f32>) -> Rgba<f32> {
326 self.blend_over_with_op(other, op_soft_light)
327 }
328
329 #[inline]
330 pub fn blend_exclusion(&self, other: Rgba<f32>) -> Rgba<f32> {
331 self.blend_over_with_op(other, op_exclusion)
332 }
333
334 #[inline]
335 pub fn blend_in(&self, other: Rgba<f32>) -> Rgba<f32> {
336 Rgba::<f32>::new(self.r, self.g, self.b, other.a * self.a)
337 }
338
339 #[inline]
340 pub fn blend_out(&self, other: Rgba<f32>) -> Rgba<f32> {
341 Rgba::<f32>::new(self.r, self.g, self.b, (1. - other.a) * self.a)
342 }
343
344 #[inline]
345 pub fn blend_atop(&self, other: Rgba<f32>) -> Rgba<f32> {
346 let new_r = self.r + other.r * (1. - self.a);
347 let new_g = self.g + other.g * (1. - self.a);
348 let new_b = self.b + other.b * (1. - self.a);
349 Rgba::<f32>::new(new_r, new_g, new_b, other.a)
350 }
351
352 #[inline]
353 pub fn blend_dest_out(&self, other: Rgba<f32>) -> Rgba<f32> {
354 Rgba::<f32>::new(other.r, other.g, other.b, (1. - self.a) * other.a)
355 }
356
357 #[inline]
358 pub fn blend_dest_atop(&self, other: Rgba<f32>) -> Rgba<f32> {
359 let new_r = other.r + self.r * (1. - other.a);
360 let new_g = other.g + self.g * (1. - other.a);
361 let new_b = other.b + self.b * (1. - other.a);
362 Rgba::<f32>::new(new_r, new_g, new_b, self.a)
363 }
364
365 #[inline]
366 pub fn blend_xor(&self, other: Rgba<f32>) -> Rgba<f32> {
370 let alpha = self.a + other.a - 2. * self.a * other.a;
371 let recpec_alpha = 1. / alpha;
372 let new_r = if alpha != 0.0 {
373 (self.r * (1. - other.a) + other.r * (1. - self.a)) * recpec_alpha
374 } else {
375 0.0
376 };
377 let new_g = if alpha != 0.0 {
378 (self.g * (1. - other.a) + other.g * (1. - self.a)) * recpec_alpha
379 } else {
380 0.0
381 };
382 let new_b = if alpha != 0.0 {
383 (self.b * (1. - other.a) + other.b * (1. - self.a)) * recpec_alpha
384 } else {
385 0.0
386 };
387 Rgba::<f32>::new(new_r, new_g, new_b, alpha)
388 }
389
390 #[inline]
391 pub fn clip_color(&self) -> Rgba<f32> {
392 let (r, g, b) = clip_color!(self);
393 Rgba::<f32>::new(r, g, b, self.a)
394 }
395
396 #[inline]
397 fn pdf_set_lum(&self, new_lum: f32) -> Rgba<f32> {
398 let d = new_lum - pdf_lum!(self);
399 let r = self.r + d;
400 let g = self.g + d;
401 let b = self.b + d;
402 let new_color = Rgba::<f32>::new(r, g, b, self.a);
403 new_color.clip_color()
404 }
405
406 #[inline]
407 fn pdf_sat(&self) -> f32 {
408 self.r.max(self.g).max(self.b) - self.r.min(self.g).min(self.b)
409 }
410
411 #[inline]
412 fn pdf_set_sat(&self, s: f32) -> Rgba<f32> {
413 let mut cmax = self.r.max(self.g).max(self.b);
414 let cmin = self.r.min(self.g).min(self.b);
415
416 let mut cmid = if self.r != cmax && self.r != cmin {
417 self.r
418 } else if self.g != cmax && self.g != cmin {
419 self.g
420 } else {
421 self.b
422 };
423
424 if cmax > cmin {
425 cmid = ((cmid - cmin) * s) / (cmax - cmin);
426 cmax = s;
427 } else {
428 cmid = 0.;
429 cmax = 0.;
430 }
431
432 let cmin = 0.0;
434
435 let r = if cmax > cmin {
437 cmin + (self.r - cmin) * cmid
438 } else {
439 cmin
440 };
441 let g = if cmax > cmin {
442 cmin + (self.g - cmin) * cmid
443 } else {
444 cmin
445 };
446 let b = if cmax > cmin {
447 cmin + (self.b - cmin) * cmid
448 } else {
449 cmin
450 };
451
452 Rgba::<f32>::new(r, g, b, self.a)
453 }
454
455 #[inline]
456 pub fn blend_hsl_color(&self, backdrop: Rgba<f32>) -> Rgba<f32> {
457 let lum = pdf_lum!(backdrop);
458 self.pdf_set_lum(lum)
459 }
460
461 #[inline]
462 pub fn blend_hsl_saturation(&self, backdrop: Rgba<f32>) -> Rgba<f32> {
463 let j1 = backdrop.pdf_set_sat(self.pdf_sat());
464 j1.pdf_set_lum(pdf_lum!(backdrop))
465 }
466
467 #[inline]
468 pub fn contrast(&self, contrast: f32) -> Rgba<f32> {
469 let new_r = self.r * contrast + -0.5f32 * contrast + 0.5f32;
470 let new_g = self.g * contrast + -0.5f32 * contrast + 0.5f32;
471 let new_b = self.b * contrast + -0.5f32 * contrast + 0.5f32;
472 Rgba::<f32>::new(new_r, new_g, new_b, self.a)
473 }
474
475 #[inline]
476 pub fn saturation(&self, saturation: f32) -> Rgba<f32> {
477 let (new_r, new_g, new_b) = adjust_saturation!(self, saturation);
478 Rgba::<f32>::new(new_r, new_g, new_b, self.a)
479 }
480
481 #[inline]
482 pub fn grayscale(&self, grayscale_amount: f32) -> Rgba<f32> {
483 let gray = self.r * 0.299f32 + self.g * 0.587 + self.b * 0.114;
484 let new_r = self.r * (1.0 - grayscale_amount) + gray * grayscale_amount;
485 let new_g = self.g * (1.0 - grayscale_amount) + gray * grayscale_amount;
486 let new_b = self.b * (1.0 - grayscale_amount) + gray * grayscale_amount;
487 Rgba::<f32>::new(new_r, new_g, new_b, self.a)
488 }
489}
490
491impl Rgba<u8> {
492 #[inline]
493 pub fn from_rgb(r: u8, g: u8, b: u8) -> Rgba<u8> {
494 Rgba {
495 r,
496 g,
497 b,
498 a: u8::MAX,
499 }
500 }
501
502 #[inline]
503 pub fn to_rgb(&self) -> Rgb<u8> {
504 Rgb {
505 r: self.r,
506 g: self.g,
507 b: self.b,
508 }
509 }
510
511 #[inline]
513 pub fn blend_over_alpha(&self, color_foreground: Rgba<u8>) -> Rgba<u8> {
514 let destination_f = self.to_rgba_f32();
515 let source_f = color_foreground.to_rgba_f32();
516 let blended = destination_f.blend_over_alpha(source_f);
517 blended.to_rgba8()
518 }
519
520 #[inline]
522 pub fn blend_hsl_color(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
523 let source = self.to_rgba_f32();
524 let backdrop = backdrop.to_rgba_f32();
525 let blended = source.blend_hsl_color(backdrop);
526 blended.to_rgba8()
527 }
528
529 #[inline]
531 pub fn blend_hsl_lumonosity(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
532 let source = self.to_rgba_f32();
533 let backdrop = backdrop.to_rgba_f32();
534 let blended = backdrop.blend_hsl_color(source);
535 blended.to_rgba8()
536 }
537
538 #[inline]
539 pub fn blend_overlay(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
540 let source = self.to_rgba_f32();
541 let backdrop = backdrop.to_rgba_f32();
542 let blended = backdrop.blend_overlay(source);
543 blended.to_rgba8()
544 }
545
546 #[inline]
547 pub fn blend_add(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
548 let source = self.to_rgba_f32();
549 let backdrop = backdrop.to_rgba_f32();
550 let blended = backdrop.blend_add(source);
551 blended.to_rgba8()
552 }
553
554 #[inline]
555 pub fn blend_substract(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
556 let source = self.to_rgba_f32();
557 let backdrop = backdrop.to_rgba_f32();
558 let blended = backdrop.blend_substract(source);
559 blended.to_rgba8()
560 }
561
562 #[inline]
563 pub fn blend_lighten(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
564 let source = self.to_rgba_f32();
565 let backdrop = backdrop.to_rgba_f32();
566 let blended = backdrop.blend_lighten(source);
567 blended.to_rgba8()
568 }
569
570 #[inline]
571 pub fn blend_darken(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
572 let source = self.to_rgba_f32();
573 let backdrop = backdrop.to_rgba_f32();
574 let blended = backdrop.blend_darken(source);
575 blended.to_rgba8()
576 }
577
578 #[inline]
579 pub fn blend_color_burn(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
580 let source = self.to_rgba_f32();
581 let backdrop = backdrop.to_rgba_f32();
582 let blended = backdrop.blend_color_burn(source);
583 blended.to_rgba8()
584 }
585
586 #[inline]
587 pub fn blend_color_dodge(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
588 let source = self.to_rgba_f32();
589 let backdrop = backdrop.to_rgba_f32();
590 let blended = backdrop.blend_color_dodge(source);
591 blended.to_rgba8()
592 }
593
594 #[inline]
595 pub fn blend_screen(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
596 let source = self.to_rgba_f32();
597 let backdrop = backdrop.to_rgba_f32();
598 let blended = backdrop.blend_screen(source);
599 blended.to_rgba8()
600 }
601
602 #[inline]
603 pub fn blend_linear_light(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
604 let source = self.to_rgba_f32();
605 let backdrop = backdrop.to_rgba_f32();
606 let blended = backdrop.blend_linear_light(source);
607 blended.to_rgba8()
608 }
609
610 #[inline]
611 pub fn blend_vivid_light(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
612 let source = self.to_rgba_f32();
613 let backdrop = backdrop.to_rgba_f32();
614 let blended = backdrop.blend_vivid_light(source);
615 blended.to_rgba8()
616 }
617
618 #[inline]
619 pub fn blend_pin_light(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
620 let source = self.to_rgba_f32();
621 let backdrop = backdrop.to_rgba_f32();
622 let blended = backdrop.blend_pin_light(source);
623 blended.to_rgba8()
624 }
625
626 #[inline]
627 pub fn blend_hard_mix(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
628 let source = self.to_rgba_f32();
629 let backdrop = backdrop.to_rgba_f32();
630 let blended = backdrop.blend_hard_mix(source);
631 blended.to_rgba8()
632 }
633
634 #[inline]
635 pub fn blend_reflect(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
636 let source = self.to_rgba_f32();
637 let backdrop = backdrop.to_rgba_f32();
638 let blended = backdrop.blend_reflect(source);
639 blended.to_rgba8()
640 }
641
642 #[inline]
643 pub fn blend_difference(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
644 let source = self.to_rgba_f32();
645 let backdrop = backdrop.to_rgba_f32();
646 let blended = backdrop.blend_difference(source);
647 blended.to_rgba8()
648 }
649
650 #[inline]
651 pub fn blend_saturate(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
652 let source = self.to_rgba_f32();
653 let backdrop = backdrop.to_rgba_f32();
654 let blended = backdrop.blend_saturate(source);
655 blended.to_rgba8()
656 }
657
658 #[inline]
659 pub fn blend_hard_light(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
660 let source = self.to_rgba_f32();
661 let backdrop = backdrop.to_rgba_f32();
662 let blended = backdrop.blend_hard_light(source);
663 blended.to_rgba8()
664 }
665
666 #[inline]
667 pub fn blend_soft_light(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
668 let source = self.to_rgba_f32();
669 let backdrop = backdrop.to_rgba_f32();
670 let blended = backdrop.blend_soft_light(source);
671 blended.to_rgba8()
672 }
673
674 #[inline]
675 pub fn blend_exclusion(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
676 let source = self.to_rgba_f32();
677 let backdrop = backdrop.to_rgba_f32();
678 let blended = backdrop.blend_exclusion(source);
679 blended.to_rgba8()
680 }
681
682 #[inline]
683 pub fn blend_in(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
684 let source = self.to_rgba_f32();
685 let backdrop = backdrop.to_rgba_f32();
686 let blended = backdrop.blend_in(source);
687 blended.to_rgba8()
688 }
689
690 #[inline]
691 pub fn blend_out(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
692 let source = self.to_rgba_f32();
693 let backdrop = backdrop.to_rgba_f32();
694 let blended = backdrop.blend_out(source);
695 blended.to_rgba8()
696 }
697
698 #[inline]
699 pub fn blend_atop(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
700 let source = self.to_rgba_f32();
701 let backdrop = backdrop.to_rgba_f32();
702 let blended = backdrop.blend_atop(source);
703 blended.to_rgba8()
704 }
705
706 #[inline]
707 pub fn blend_dest_out(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
708 let source = self.to_rgba_f32();
709 let backdrop = backdrop.to_rgba_f32();
710 let blended = backdrop.blend_dest_out(source);
711 blended.to_rgba8()
712 }
713
714 #[inline]
715 pub fn blend_dest_atop(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
716 let source = self.to_rgba_f32();
717 let backdrop = backdrop.to_rgba_f32();
718 let blended = backdrop.blend_dest_atop(source);
719 blended.to_rgba8()
720 }
721
722 #[inline]
723 pub fn blend_xor(&self, backdrop: Rgba<u8>) -> Rgba<u8> {
724 let source = self.to_rgba_f32();
725 let backdrop = backdrop.to_rgba_f32();
726 let blended = backdrop.blend_xor(source);
727 blended.to_rgba8()
728 }
729
730 #[inline]
731 #[allow(clippy::manual_clamp)]
732 pub fn contrast(&self, contrast: f32) -> Rgba<u8> {
733 let new_r = (self.r as f32 * contrast + -127.5f32 * contrast + 127.5f32)
734 .round()
735 .min(255f32)
736 .max(0f32);
737 let new_g = (self.g as f32 * contrast + -127.5f32 * contrast + 127.5f32)
738 .round()
739 .min(255f32)
740 .max(0f32);
741 let new_b = (self.b as f32 * contrast + -127.5f32 * contrast + 127.5f32)
742 .round()
743 .min(255f32)
744 .max(0f32);
745 Rgba::<u8>::new(new_r as u8, new_g as u8, new_b as u8, self.a)
746 }
747
748 #[inline]
749 pub fn saturation(&self, saturation: f32) -> Rgba<u8> {
750 let source = self.to_rgba_f32();
751 let saturated = source.saturation(saturation);
752 saturated.to_rgba8()
753 }
754
755 #[inline]
756 pub fn grayscale(&self, grayscale_amount: f32) -> Rgba<u8> {
757 let gray = self.r as f32 * 0.299f32 + self.g as f32 * 0.587 + self.b as f32 * 0.114;
758 let new_r = self.r as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
759 let new_g = self.g as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
760 let new_b = self.b as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
761 Rgba::<u8>::new(
762 new_r.round() as u8,
763 new_g.round() as u8,
764 new_b.round() as u8,
765 self.a,
766 )
767 }
768
769 #[inline]
774 pub fn to_linear(&self, transfer_function: TransferFunction) -> Rgba<f32> {
775 let rgba = self.to_rgba_f32();
776 Rgba::<f32>::new(
777 transfer_function.linearize(rgba.r),
778 transfer_function.linearize(rgba.g),
779 transfer_function.linearize(rgba.b),
780 rgba.a,
781 )
782 }
783
784 #[inline]
789 pub fn from_linear(linear_rgb: Rgba<f32>, transfer_function: TransferFunction) -> Rgba<u8> {
790 let gamma = Rgba::<f32>::new(
791 transfer_function.gamma(linear_rgb.r),
792 transfer_function.gamma(linear_rgb.g),
793 transfer_function.gamma(linear_rgb.b),
794 linear_rgb.a,
795 );
796 gamma.to_rgba8()
797 }
798}
799
800pub trait ToRgba8 {
801 fn to_rgba8(&self) -> Rgba<u8>;
802}
803
804pub trait ToRgbaF16 {
805 fn to_rgba_f16(&self) -> Rgba<f16>;
806}
807
808pub trait ToRgb565 {
809 fn to_rgb_565(&self) -> Rgb565;
810}
811
812pub trait ToRgbaF32 {
813 fn to_rgba_f32(&self) -> Rgba<f32>;
814}
815
816impl ToRgbaF32 for Rgba<u8> {
817 #[inline]
818 fn to_rgba_f32(&self) -> Rgba<f32> {
819 const SCALE_U8: f32 = 1f32 / 255f32;
820 Rgba::<f32>::new(
821 self.r as f32 * SCALE_U8,
822 self.g as f32 * SCALE_U8,
823 self.b as f32 * SCALE_U8,
824 self.a as f32 * SCALE_U8,
825 )
826 }
827}
828
829impl ToRgba8 for Rgba<f32> {
830 #[inline]
831 #[allow(clippy::manual_clamp)]
832 fn to_rgba8(&self) -> Rgba<u8> {
833 Rgba {
834 r: (self.r * 255f32).min(255f32).max(0f32) as u8,
835 g: (self.g * 255f32).min(255f32).max(0f32) as u8,
836 b: (self.b * 255f32).min(255f32).max(0f32) as u8,
837 a: (self.a * 255f32).min(255f32).max(0f32) as u8,
838 }
839 }
840}
841
842impl ToRgba8 for Rgba<f16> {
843 #[inline]
844 #[allow(clippy::manual_clamp)]
845 fn to_rgba8(&self) -> Rgba<u8> {
846 Rgba {
847 r: (self.r.to_f32() * 255f32).min(255f32).max(0f32) as u8,
848 g: (self.g.to_f32() * 255f32).min(255f32).max(0f32) as u8,
849 b: (self.b.to_f32() * 255f32).min(255f32).max(0f32) as u8,
850 a: (self.a.to_f32() * 255f32).min(255f32).max(0f32) as u8,
851 }
852 }
853}
854
855impl ToRgbaF16 for Rgba<f32> {
856 #[inline]
857 fn to_rgba_f16(&self) -> Rgba<f16> {
858 Rgba {
859 r: f16::from_f32(self.r),
860 g: f16::from_f32(self.g),
861 b: f16::from_f32(self.b),
862 a: f16::from_f32(self.a),
863 }
864 }
865}
866
867static SCALE_U8_F32: f32 = 1f32 / 255f32;
868
869impl ToRgbaF16 for Rgba<u8> {
870 #[inline]
871 fn to_rgba_f16(&self) -> Rgba<f16> {
872 Rgba {
873 r: f16::from_f32(self.r as f32 * SCALE_U8_F32),
874 g: f16::from_f32(self.g as f32 * SCALE_U8_F32),
875 b: f16::from_f32(self.b as f32 * SCALE_U8_F32),
876 a: f16::from_f32(self.a as f32 * SCALE_U8_F32),
877 }
878 }
879}
880
881#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
882pub struct Rgb565 {
884 pub rgb565: u16,
885}
886
887impl Rgb565 {
888 pub fn new(color: u16) -> Rgb565 {
889 Rgb565 { rgb565: color }
890 }
891}
892
893impl ToRgba8 for Rgb565 {
894 #[inline]
895 fn to_rgba8(&self) -> Rgba<u8> {
896 let red8 = ((self.rgb565 & 0b1111100000000000) >> 8) as u8;
897 let green8 = ((self.rgb565 & 0b11111100000) >> 3) as u8;
898 let blue8 = ((self.rgb565 & 0b11111) << 3) as u8;
899 Rgba::<u8>::new(red8, green8, blue8, u8::MAX)
900 }
901}
902
903static SCALE_RGB565_5BIT: f32 = 1f32 / 31f32;
904static SCALE_RGB565_6BIT: f32 = 1f32 / 63f32;
905
906impl ToRgbaF16 for Rgb565 {
907 #[inline]
908 fn to_rgba_f16(&self) -> Rgba<f16> {
909 let red5 = (self.rgb565 & 0b1111100000000000) as f32 * SCALE_RGB565_5BIT;
910 let green6 = (self.rgb565 & 0b11111100000) as f32 * SCALE_RGB565_6BIT;
911 let blue5 = (self.rgb565 & 0b11111) as f32 * SCALE_RGB565_5BIT;
912 Rgba::<f16>::from_rgb(
913 f16::from_f32(red5),
914 f16::from_f32(green6),
915 f16::from_f32(blue5),
916 )
917 }
918}
919
920impl ToRgb565 for Rgba<u8> {
921 #[inline]
922 fn to_rgb_565(&self) -> Rgb565 {
923 let red565 = ((self.r as u16) >> 3) << 11;
924 let green565 = ((self.g as u16) >> 2) << 5;
925 let blue565 = (self.b as u16) >> 3;
926 Rgb565 {
927 rgb565: red565 | green565 | blue565,
928 }
929 }
930}
931
932impl ToRgb565 for Rgba<f16> {
933 #[inline]
934 #[allow(clippy::manual_clamp)]
935 fn to_rgb_565(&self) -> Rgb565 {
936 let red5 = (self.r.to_f32() * 31f32).min(31f32).max(0f32) as u16;
937 let green6 = (self.g.to_f32() * 63f32).min(63f32).max(0f32) as u16;
938 let blue5 = (self.b.to_f32() * 31f32).min(31f32).max(0f32) as u16;
939 Rgb565 {
940 rgb565: red5 | green6 | blue5,
941 }
942 }
943}
944
945impl ToRgb565 for Rgba<f32> {
946 #[inline]
947 #[allow(clippy::manual_clamp)]
948 fn to_rgb_565(&self) -> Rgb565 {
949 let red5 = (self.r * 31f32).min(31f32).max(0f32) as u16;
950 let green6 = (self.g * 63f32).min(63f32).max(0f32) as u16;
951 let blue5 = (self.b * 31f32).min(31f32).max(0f32) as u16;
952 Rgb565 {
953 rgb565: red5 | green6 | blue5,
954 }
955 }
956}
957
958#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
959pub struct Rgba1010102 {
961 pub rgba: u32,
962}
963
964impl Rgba1010102 {
965 #[inline]
966 pub fn new(color: u32) -> Rgba1010102 {
967 Rgba1010102 { rgba: color }
968 }
969}
970
971impl ToRgba8 for Rgba1010102 {
972 #[inline]
973 fn to_rgba8(&self) -> Rgba<u8> {
974 let mask = (1u32 << 10u32) - 1u32;
975 let r = (self.rgba) & mask;
976 let g = (self.rgba >> 10) & mask;
977 let b = (self.rgba >> 20) & mask;
978 let a = (self.rgba >> 30) & 0b00000011;
979 Rgba::<u8>::new(
980 (r >> 2) as u8,
981 (g >> 2) as u8,
982 (b >> 2) as u8,
983 (a << 6) as u8,
984 )
985 }
986}
987
988static SCALE_RGBA10: f32 = 1f32 / 1023f32;
989static SCALE_RGBA10ALPHA: f32 = 1f32 / 3f32;
990
991impl ToRgbaF16 for Rgba1010102 {
992 #[inline]
993 fn to_rgba_f16(&self) -> Rgba<f16> {
994 let mask = (1u32 << 10u32) - 1u32;
995 let r = (self.rgba) & mask;
996 let g = (self.rgba >> 10) & mask;
997 let b = (self.rgba >> 20) & mask;
998 let a = (self.rgba >> 30) & 0b00000011;
999 Rgba::<f16>::new(
1000 f16::from_f32(r as f32 * SCALE_RGBA10),
1001 f16::from_f32(g as f32 * SCALE_RGBA10),
1002 f16::from_f32(b as f32 * SCALE_RGBA10),
1003 f16::from_f32(a as f32 * SCALE_RGBA10ALPHA),
1004 )
1005 }
1006}
1007
1008pub trait ToRgba1010102 {
1009 #[allow(dead_code)]
1010 fn to_rgba1010102(&self) -> Rgba1010102;
1011}
1012
1013impl ToRgba1010102 for Rgba<u8> {
1014 #[inline]
1015 fn to_rgba1010102(&self) -> Rgba1010102 {
1016 let r = (self.r as u32) << 2;
1017 let g = (self.g as u32) << 2;
1018 let b = (self.b as u32) << 2;
1019 let a = (self.a as u32) >> 6;
1020 let rgba1010102 = (a << 30) | (r << 20) | (g << 10) | b;
1021 Rgba1010102 { rgba: rgba1010102 }
1022 }
1023}
1024
1025impl ToRgba1010102 for Rgba<f16> {
1026 #[inline]
1027 #[allow(clippy::manual_clamp)]
1028 fn to_rgba1010102(&self) -> Rgba1010102 {
1029 let r = (self.r.to_f32() * 1023f32).min(1023f32).max(0f32) as u32;
1030 let g = (self.g.to_f32() * 1023f32).min(1023f32).max(0f32) as u32;
1031 let b = (self.b.to_f32() * 1023f32).min(1023f32).max(0f32) as u32;
1032 let a = (self.a.to_f32() * 3f32).min(3f32).max(0f32) as u32;
1033 let rgba1010102 = (a << 30) | (r << 20) | (g << 10) | b;
1034 Rgba1010102 { rgba: rgba1010102 }
1035 }
1036}
1037
1038impl<T> Index<usize> for Rgba<T> {
1039 type Output = T;
1040
1041 #[inline]
1042 fn index(&self, index: usize) -> &T {
1043 match index {
1044 0 => &self.r,
1045 1 => &self.g,
1046 2 => &self.b,
1047 3 => &self.a,
1048 _ => panic!("Index out of bounds for Rgba"),
1049 }
1050 }
1051}
1052
1053impl<T> IndexMut<usize> for Rgba<T> {
1054 #[inline]
1055 fn index_mut(&mut self, index: usize) -> &mut T {
1056 match index {
1057 0 => &mut self.r,
1058 1 => &mut self.g,
1059 2 => &mut self.b,
1060 3 => &mut self.a,
1061 _ => panic!("Index out of bounds for Lab"),
1062 }
1063 }
1064}
1065
1066impl<T> Add for Rgba<T>
1067where
1068 T: Add<Output = T>,
1069{
1070 type Output = Rgba<T>;
1071
1072 #[inline]
1073 fn add(self, rhs: Self) -> Self::Output {
1074 Rgba::new(
1075 self.r + rhs.r,
1076 self.g + rhs.g,
1077 self.b + rhs.b,
1078 self.a + rhs.a,
1079 )
1080 }
1081}
1082
1083impl<T> Sub for Rgba<T>
1084where
1085 T: Sub<Output = T>,
1086{
1087 type Output = Rgba<T>;
1088
1089 #[inline]
1090 fn sub(self, rhs: Self) -> Self::Output {
1091 Rgba::new(
1092 self.r - rhs.r,
1093 self.g - rhs.g,
1094 self.b - rhs.b,
1095 self.a - rhs.a,
1096 )
1097 }
1098}
1099
1100impl<T> Mul for Rgba<T>
1101where
1102 T: Mul<Output = T>,
1103{
1104 type Output = Rgba<T>;
1105
1106 #[inline]
1107 fn mul(self, rhs: Self) -> Self::Output {
1108 Rgba::new(
1109 self.r * rhs.r,
1110 self.g * rhs.g,
1111 self.b * rhs.b,
1112 self.a * rhs.a,
1113 )
1114 }
1115}
1116
1117impl<T> Div for Rgba<T>
1118where
1119 T: Div<Output = T>,
1120{
1121 type Output = Rgba<T>;
1122 #[inline]
1123 fn div(self, rhs: Self) -> Self::Output {
1124 Rgba::new(
1125 self.r / rhs.r,
1126 self.g / rhs.g,
1127 self.b / rhs.b,
1128 self.a / rhs.a,
1129 )
1130 }
1131}
1132
1133impl<T> AddAssign for Rgba<T>
1134where
1135 T: AddAssign<T>,
1136{
1137 #[inline]
1138 fn add_assign(&mut self, rhs: Self) {
1139 self.r += rhs.r;
1140 self.g += rhs.g;
1141 self.b += rhs.b;
1142 self.a += rhs.a;
1143 }
1144}
1145
1146impl<T> DivAssign for Rgba<T>
1147where
1148 T: DivAssign<T>,
1149{
1150 #[inline]
1151 fn div_assign(&mut self, rhs: Self) {
1152 self.r /= rhs.r;
1153 self.g /= rhs.g;
1154 self.b /= rhs.b;
1155 self.a /= rhs.a;
1156 }
1157}
1158
1159macro_rules! generated_div_assign_definition_rgba {
1160 ($T: ty) => {
1161 impl<T> DivAssign<$T> for Rgba<T>
1162 where
1163 T: DivAssign<$T> + Copy,
1164 {
1165 fn div_assign(&mut self, rhs: $T) {
1166 self.r /= rhs;
1167 self.g /= rhs;
1168 self.b /= rhs;
1169 self.a /= rhs;
1170 }
1171 }
1172 };
1173}
1174
1175generated_div_assign_definition_rgba!(u8);
1176generated_div_assign_definition_rgba!(u16);
1177generated_div_assign_definition_rgba!(i16);
1178generated_div_assign_definition_rgba!(u32);
1179generated_div_assign_definition_rgba!(i32);
1180generated_div_assign_definition_rgba!(f32);
1181generated_div_assign_definition_rgba!(f64);
1182
1183impl<T> Rgba<T>
1184where
1185 T: Num + PartialOrd + Copy + Bounded + Ord,
1186{
1187 #[inline]
1189 pub fn clamp(&self, min: T, max: T) -> Rgba<T> {
1190 Rgba::new(
1191 clamp(self.r, min, max),
1192 clamp(self.g, min, max),
1193 clamp(self.b, min, max),
1194 clamp(self.a, min, max),
1195 )
1196 }
1197
1198 #[inline]
1200 pub fn min(&self, other_min: T) -> Rgba<T> {
1201 Rgba::new(
1202 min(self.r, other_min),
1203 min(self.g, other_min),
1204 min(self.b, other_min),
1205 min(self.a, other_min),
1206 )
1207 }
1208
1209 #[inline]
1211 pub fn max(&self, other_max: T) -> Rgba<T> {
1212 Rgba::new(
1213 max(self.r, other_max),
1214 max(self.g, other_max),
1215 max(self.b, other_max),
1216 max(self.a, other_max),
1217 )
1218 }
1219
1220 #[inline]
1222 pub fn clamp_p(&self, min: Rgba<T>, max: Rgba<T>) -> Rgba<T> {
1223 Rgba::new(
1224 clamp(self.r, min.r, max.r),
1225 clamp(self.g, min.g, max.g),
1226 clamp(self.b, min.b, max.b),
1227 clamp(self.a, min.a, max.a),
1228 )
1229 }
1230
1231 #[inline]
1233 pub fn min_p(&self, other_min: Rgba<T>) -> Rgba<T> {
1234 Rgba::new(
1235 min(self.r, other_min.r),
1236 min(self.g, other_min.g),
1237 min(self.b, other_min.b),
1238 min(self.a, other_min.a),
1239 )
1240 }
1241
1242 #[inline]
1244 pub fn max_p(&self, other_max: Rgba<T>) -> Rgba<T> {
1245 Rgba::new(
1246 max(self.r, other_max.r),
1247 max(self.g, other_max.g),
1248 max(self.b, other_max.b),
1249 max(self.a, other_max.a),
1250 )
1251 }
1252}
1253
1254impl<T> Neg for Rgba<T>
1255where
1256 T: Neg<Output = T>,
1257{
1258 type Output = Rgba<T>;
1259 fn neg(self) -> Self::Output {
1260 Rgba::new(-self.r, -self.g, -self.b, -self.a)
1261 }
1262}
1263
1264impl<T> Rgba<T>
1265where
1266 T: Float + 'static,
1267 f32: AsPrimitive<T>,
1268{
1269 #[inline]
1270 pub fn sqrt(&self) -> Rgba<T> {
1271 let zeros = 0f32.as_();
1272 Rgba::new(
1273 if self.r.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
1274 0f32.as_()
1275 } else {
1276 self.r.sqrt()
1277 },
1278 if self.g.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
1279 0f32.as_()
1280 } else {
1281 self.g.sqrt()
1282 },
1283 if self.b.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
1284 0f32.as_()
1285 } else {
1286 self.b.sqrt()
1287 },
1288 if self.a.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
1289 0f32.as_()
1290 } else {
1291 self.a.sqrt()
1292 },
1293 )
1294 }
1295
1296 #[inline]
1297 pub fn cbrt(&self) -> Rgba<T> {
1298 Rgba::new(self.r.cbrt(), self.g.cbrt(), self.b.cbrt(), self.a.cbrt())
1299 }
1300}
1301
1302impl<T> Pow<T> for Rgba<T>
1303where
1304 T: Float,
1305{
1306 type Output = Rgba<T>;
1307
1308 #[inline]
1309 fn pow(self, rhs: T) -> Self::Output {
1310 Rgba::<T>::new(
1311 self.r.powf(rhs),
1312 self.g.powf(rhs),
1313 self.b.powf(rhs),
1314 self.a.powf(self.a),
1315 )
1316 }
1317}
1318
1319impl<T> Pow<Rgba<T>> for Rgba<T>
1320where
1321 T: Float,
1322{
1323 type Output = Rgba<T>;
1324
1325 #[inline]
1326 fn pow(self, rhs: Rgba<T>) -> Self::Output {
1327 Rgba::<T>::new(
1328 self.r.powf(rhs.r),
1329 self.g.powf(rhs.g),
1330 self.b.powf(rhs.b),
1331 self.a.powf(rhs.a),
1332 )
1333 }
1334}
1335
1336impl<T> EuclideanDistance for Rgba<T>
1337where
1338 T: AsPrimitive<f32>,
1339{
1340 fn euclidean_distance(&self, other: Self) -> f32 {
1341 let dr = self.r.as_() - other.r.as_();
1342 let dg = self.g.as_() - other.g.as_();
1343 let db = self.b.as_() - other.b.as_();
1344 let da = self.a.as_() - other.a.as_();
1345 (dr * dr + dg * dg + db * db + da * da).sqrt()
1346 }
1347}
1348
1349impl<T> TaxicabDistance for Rgba<T>
1350where
1351 T: AsPrimitive<f32>,
1352{
1353 fn taxicab_distance(&self, other: Self) -> f32 {
1354 let dr = self.r.as_() - other.r.as_();
1355 let dg = self.g.as_() - other.g.as_();
1356 let db = self.b.as_() - other.b.as_();
1357 let da = self.a.as_() - other.a.as_();
1358 dr.abs() + dg.abs() + db.abs() + da.abs()
1359 }
1360}