colorutils_rs/
rgba.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use 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)]
24/// Represents any RGBA values, Rgba<u8>, Rgba<u16> etc.
25pub struct Rgba<T> {
26    /// Red component
27    pub r: T,
28    /// Green component
29    pub g: T,
30    /// Blue component
31    pub b: T,
32    /// Alpha component
33    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    /// Using alpha blend over algorithm where current color is on bottom ( destination )
151    #[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    /// Using alpha blend over algorithm where current color is on bottom ( destination )
168    /// aR = aA + aB·(1−aA)
169    /// xR = 1/aR · [ (1−aB)·xaA + (1−aA)·xaB + aA·aB·f(xA,xB) ]
170    #[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    /// The output of the ADD operator is the same for both bounded and unbounded source interpretations.
213    /// Resulting alpha (aR) - min(1, aA+aB)
214    /// Resulting color (xR) - (xaA + xaB)/aR
215    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    /// The output of the ADD operator is the same for both bounded and unbounded source interpretations.
234    /// Resulting alpha (aR) - min(1, aA+aB)
235    /// Resulting color (xR) - (min(aA, 1−aB)·xA + xaB)/aR
236    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    /// The output of the ADD operator is the same for both bounded and unbounded source interpretations.
367    /// Resulting alpha (aR) - aA + aB − 2·aA·aB
368    /// Resulting color (xR) - (xaA·(1−aB) + xaB·(1−aA))/aR
369    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        // Cmin is always set to 0
433        let cmin = 0.0;
434
435        // Construct the new color
436        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    /// Using alpha blend over algorithm where current color is on bottom ( destination )
512    #[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    /// f(cA,cB) = set_lum(cA, lum(cB))
521    #[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    /// f(cA,cB) = set_lum(cB, lum(cA))
530    #[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    /// Converts gamma corrected RGBA to linear RGBA
770    ///
771    /// # Arguments
772    /// `transfer_function` - Transfer function to convert RGB into linear RGB
773    #[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    /// Converts gamma corrected RGB to linear RGB
785    ///
786    /// # Arguments
787    /// `transfer_function` - Transfer function to convert RGB into linear RGB
788    #[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)]
882/// Represents RGB 565 color in one u16
883pub 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)]
959/// Represents RGBA 1010102 in one u32 store
960pub 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    /// Clamp function to clamp each channel within a given range
1188    #[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    /// Min function to define min
1199    #[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    /// Max function to define max
1210    #[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    /// Clamp function to clamp each channel within a given range
1221    #[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    /// Min function to define min
1232    #[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    /// Max function to define max
1243    #[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}