colorutils_rs/
rgb.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::euclidean::EuclideanDistance;
8use crate::hsv::Hsv;
9use crate::lab::Lab;
10use crate::luv::Luv;
11use crate::oklch::Oklch;
12use crate::{
13    adjust_saturation, clip_color, color_add, color_burn, color_darken, color_difference,
14    color_dodge, color_exclusion, color_hard_light, color_hard_mix, color_lighten,
15    color_linear_burn, color_linear_light, color_pin_light, color_reflect, color_screen,
16    color_soft_light, color_soft_light_weight, color_vivid_light, pdf_lum, Hsl, Jzazbz, LAlphaBeta,
17    LCh, Oklab, Rgba, Sigmoidal, TaxicabDistance, TransferFunction, Xyz,
18};
19use num_traits::{AsPrimitive, Bounded, Float, Num, Pow};
20use std::cmp::{max, min, Ordering};
21use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
22
23#[repr(C)]
24#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
25/// Represents any RGB values, Rgb<u8>, Rgb<u16> etc.
26pub struct Rgb<T> {
27    /// Red component
28    pub r: T,
29    /// Green component
30    pub g: T,
31    /// Blue component
32    pub b: T,
33}
34
35impl Rgb<u8> {
36    /// Converts gamma corrected RGB to linear RGB
37    ///
38    /// # Arguments
39    /// `transfer_function` - Transfer function to convert RGB into linear RGB
40    #[inline]
41    pub fn to_linear(&self, transfer_function: TransferFunction) -> Rgb<f32> {
42        self.to_rgb_f32().linearize(transfer_function)
43    }
44
45    /// Converts gamma corrected RGB to linear RGB
46    ///
47    /// # Arguments
48    /// `transfer_function` - Transfer function to convert RGB into linear RGB
49    #[inline]
50    pub fn from_linear(linear_rgb: Rgb<f32>, transfer_function: TransferFunction) -> Rgb<u8> {
51        linear_rgb.gamma(transfer_function).to_u8()
52    }
53
54    /// Converts rgb to Jzazbz
55    /// Here is luminance always considered 200 nits
56    ///
57    /// # Arguments
58    /// `display_luminance` - display luminance
59    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
60    #[inline]
61    pub fn to_jzazbz(&self, transfer_function: TransferFunction) -> Jzazbz {
62        Jzazbz::from_rgb(*self, transfer_function)
63    }
64
65    /// Converts rgb to Jzazbz
66    ///
67    /// # Arguments
68    /// `display_luminance` - display luminance
69    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
70    #[inline]
71    pub fn to_jzazbz_with_luminance(
72        &self,
73        display_luminance: f32,
74        transfer_function: TransferFunction,
75    ) -> Jzazbz {
76        Jzazbz::from_rgb_with_luminance(*self, display_luminance, transfer_function)
77    }
78
79    /// Converts rgb to XYZ
80    ///
81    /// # Arguments
82    /// `transfer_function` - Transfer function to convert RGB into linear RGB
83    #[inline]
84    pub fn to_xyz(&self, matrix: &[[f32; 3]; 3], transfer_function: TransferFunction) -> Xyz {
85        Xyz::from_rgb(*self, matrix, transfer_function)
86    }
87
88    /// Converts rgb to HSL
89    #[inline]
90    pub fn to_hsl(&self) -> Hsl {
91        Hsl::from_rgb(*self)
92    }
93
94    /// Converts rgb to HSV
95    #[inline]
96    pub fn to_hsv(&self) -> Hsv {
97        Hsv::from(*self)
98    }
99
100    /// Converts rgb to CIELAB
101    #[inline]
102    pub fn to_lab(&self) -> Lab {
103        Lab::from_rgb(*self)
104    }
105
106    /// Converts rgb to CIELUV
107    #[inline]
108    pub fn to_luv(&self) -> Luv {
109        Luv::from_rgb(*self)
110    }
111
112    /// Converts rgb to CIELCH
113    #[inline]
114    pub fn to_lch(&self) -> LCh {
115        LCh::from_rgb(*self)
116    }
117
118    /// Converts rgb to RGB f32
119    #[inline]
120    pub fn to_rgb_f32(&self) -> Rgb<f32> {
121        const SCALE: f32 = 1f32 / 255f32;
122        Rgb::<f32>::new(
123            self.r as f32 * SCALE,
124            self.g as f32 * SCALE,
125            self.b as f32 * SCALE,
126        )
127    }
128
129    /// Converts rgb to *Oklab*
130    ///
131    /// # Arguments
132    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
133    #[inline]
134    pub fn to_oklab(&self, transfer_function: TransferFunction) -> Oklab {
135        Oklab::from_rgb(*self, transfer_function)
136    }
137
138    /// Converts rgb to *Oklch*
139    ///
140    /// # Arguments
141    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
142    #[inline]
143    pub fn to_oklch(&self, transfer_function: TransferFunction) -> Oklch {
144        Oklch::from_rgb(*self, transfer_function)
145    }
146
147    /// Converts rgb to S-shaped sigmoidized components
148    #[inline]
149    pub fn to_sigmoidal(&self) -> Sigmoidal {
150        Sigmoidal::from_rgb(*self)
151    }
152
153    /// Converts rgb to *lαβ*
154    ///
155    /// # Arguments
156    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
157    #[inline]
158    pub fn to_lalphabeta(&self, transfer_function: TransferFunction) -> LAlphaBeta {
159        LAlphaBeta::from_rgb(*self, transfer_function)
160    }
161
162    #[inline]
163    pub fn blend_add(&self, other: Rgb<u8>) -> Rgb<u8> {
164        let in_color = self.to_rgb_f32();
165        let other_color = other.to_rgb_f32();
166        let blended = in_color.blend_add(other_color);
167        blended.to_u8()
168    }
169
170    #[inline]
171    pub fn blend_hsl_color(&self, other: Rgb<u8>) -> Rgb<u8> {
172        let in_color = self.to_rgb_f32();
173        let other_color = other.to_rgb_f32();
174        let blended = in_color.blend_hsl_color(other_color);
175        blended.to_u8()
176    }
177
178    #[inline]
179    pub fn blend_substract(&self, other: Rgb<u8>) -> Rgb<u8> {
180        let in_color = self.to_rgb_f32();
181        let other_color = other.to_rgb_f32();
182        let blended = in_color.blend_substract(other_color);
183        blended.to_u8()
184    }
185
186    #[inline]
187    pub fn blend_lighten(&self, other: Rgb<u8>) -> Rgb<u8> {
188        let in_color = self.to_rgb_f32();
189        let other_color = other.to_rgb_f32();
190        let blended = in_color.blend_lighten(other_color);
191        blended.to_u8()
192    }
193
194    #[inline]
195    pub fn blend_darken(&self, other: Rgb<u8>) -> Rgb<u8> {
196        let in_color = self.to_rgb_f32();
197        let other_color = other.to_rgb_f32();
198        let blended = in_color.blend_darken(other_color);
199        blended.to_u8()
200    }
201
202    #[inline]
203    pub fn blend_color_burn(&self, other: Rgb<u8>) -> Rgb<u8> {
204        let in_color = self.to_rgb_f32();
205        let other_color = other.to_rgb_f32();
206        let blended = in_color.blend_color_burn(other_color);
207        blended.to_u8()
208    }
209
210    #[inline]
211    pub fn blend_color_dodge(&self, other: Rgb<u8>) -> Rgb<u8> {
212        let in_color = self.to_rgb_f32();
213        let other_color = other.to_rgb_f32();
214        let blended = in_color.blend_color_dodge(other_color);
215        blended.to_u8()
216    }
217
218    #[inline]
219    pub fn blend_screen(&self, other: Rgb<u8>) -> Rgb<u8> {
220        let in_color = self.to_rgb_f32();
221        let other_color = other.to_rgb_f32();
222        let blended = in_color.blend_screen(other_color);
223        blended.to_u8()
224    }
225
226    #[inline]
227    pub fn blend_linear_light(&self, other: Rgb<u8>) -> Rgb<u8> {
228        let in_color = self.to_rgb_f32();
229        let other_color = other.to_rgb_f32();
230        let blended = in_color.blend_linear_light(other_color);
231        blended.to_u8()
232    }
233
234    #[inline]
235    pub fn blend_vivid_light(&self, other: Rgb<u8>) -> Rgb<u8> {
236        let in_color = self.to_rgb_f32();
237        let other_color = other.to_rgb_f32();
238        let blended = in_color.blend_vivid_light(other_color);
239        blended.to_u8()
240    }
241
242    #[inline]
243    pub fn blend_pin_light(&self, other: Rgb<u8>) -> Rgb<u8> {
244        let in_color = self.to_rgb_f32();
245        let other_color = other.to_rgb_f32();
246        let blended = in_color.blend_pin_light(other_color);
247        blended.to_u8()
248    }
249
250    #[inline]
251    pub fn blend_hard_mix(&self, other: Rgb<u8>) -> Rgb<u8> {
252        let in_color = self.to_rgb_f32();
253        let other_color = other.to_rgb_f32();
254        let blended = in_color.blend_hard_mix(other_color);
255        blended.to_u8()
256    }
257
258    #[inline]
259    pub fn blend_soft_light(&self, other: Rgb<u8>) -> Rgb<u8> {
260        let in_color = self.to_rgb_f32();
261        let other_color = other.to_rgb_f32();
262        let blended = in_color.blend_soft_light(other_color);
263        blended.to_u8()
264    }
265
266    #[inline]
267    pub fn blend_exclusion(&self, other: Rgb<u8>) -> Rgb<u8> {
268        let in_color = self.to_rgb_f32();
269        let other_color = other.to_rgb_f32();
270        let blended = in_color.blend_exclusion(other_color);
271        blended.to_u8()
272    }
273
274    #[inline]
275    pub fn saturation(&self, saturation: f32) -> Rgb<u8> {
276        let in_color = self.to_rgb_f32();
277        let saturated = in_color.saturation(saturation);
278        saturated.to_u8()
279    }
280}
281
282impl From<Rgb<f32>> for Rgb<u8> {
283    #[inline]
284    fn from(value: Rgb<f32>) -> Self {
285        value.to_u8()
286    }
287}
288
289impl Rgb<f32> {
290    #[inline]
291    pub fn apply(&self, gen: fn(f32) -> f32) -> Self {
292        Self {
293            r: gen(self.r),
294            g: gen(self.g),
295            b: gen(self.b),
296        }
297    }
298
299    #[inline]
300    pub fn gamma(&self, transfer_function: TransferFunction) -> Self {
301        Self {
302            r: transfer_function.gamma(self.r),
303            g: transfer_function.gamma(self.g),
304            b: transfer_function.gamma(self.b),
305        }
306    }
307
308    #[inline]
309    pub fn linearize(&self, transfer_function: TransferFunction) -> Self {
310        Self {
311            r: transfer_function.linearize(self.r),
312            g: transfer_function.linearize(self.g),
313            b: transfer_function.linearize(self.b),
314        }
315    }
316
317    #[inline]
318    pub fn to_u8(&self) -> Rgb<u8> {
319        Rgb::<u8>::new(
320            (self.r * 255f32).max(0f32).round().min(255f32) as u8,
321            (self.g * 255f32).max(0f32).round().min(255f32) as u8,
322            (self.b * 255f32).max(0f32).round().min(255f32) as u8,
323        )
324    }
325
326    #[inline]
327    pub fn clip_color(&self) -> Rgb<f32> {
328        let (r, g, b) = clip_color!(self);
329        Rgb::<f32>::new(r, g, b)
330    }
331
332    #[inline]
333    fn pdf_set_lum(&self, new_lum: f32) -> Rgb<f32> {
334        let d = new_lum - pdf_lum!(self);
335        let r = self.r + d;
336        let g = self.g + d;
337        let b = self.b + d;
338        let new_color = Rgb::<f32>::new(r, g, b);
339        new_color.clip_color()
340    }
341
342    #[inline]
343    pub fn blend_hsl_color(&self, backdrop: Rgb<f32>) -> Rgb<f32> {
344        let lum = pdf_lum!(backdrop);
345        self.pdf_set_lum(lum)
346    }
347
348    #[inline]
349    /// The output of the ADD operator is the same for both bounded and unbounded source interpretations.
350    /// Resulting color (xR) - (xaA + xaB)
351    pub fn blend_add(&self, other: Rgb<f32>) -> Rgb<f32> {
352        let new_r = color_add!(self.r, other.r);
353        let new_g = color_add!(self.g, other.g);
354        let new_b = color_add!(self.b, other.b);
355        Rgb::<f32>::new(new_r, new_g, new_b)
356    }
357
358    #[inline]
359    pub fn blend_substract(&self, other: Rgb<f32>) -> Rgb<f32> {
360        let new_r = color_linear_burn!(self.r, other.r);
361        let new_g = color_linear_burn!(self.g, other.g);
362        let new_b = color_linear_burn!(self.b, other.b);
363        Rgb::<f32>::new(new_r, new_g, new_b)
364    }
365
366    #[inline]
367    pub fn blend_lighten(&self, other: Rgb<f32>) -> Rgb<f32> {
368        let new_r = color_lighten!(self.r, other.r);
369        let new_g = color_lighten!(self.g, other.g);
370        let new_b = color_lighten!(self.b, other.b);
371        Rgb::<f32>::new(new_r, new_g, new_b)
372    }
373
374    #[inline]
375    pub fn blend_darken(&self, other: Rgb<f32>) -> Rgb<f32> {
376        let new_r = color_darken!(self.r, other.r);
377        let new_g = color_darken!(self.g, other.g);
378        let new_b = color_darken!(self.b, other.b);
379        Rgb::<f32>::new(new_r, new_g, new_b)
380    }
381
382    #[inline]
383    pub fn blend_color_burn(&self, other: Rgb<f32>) -> Rgb<f32> {
384        let new_r = color_burn!(self.r, other.r);
385        let new_g = color_burn!(self.g, other.g);
386        let new_b = color_burn!(self.b, other.b);
387        Rgb::<f32>::new(new_r, new_g, new_b)
388    }
389
390    #[inline]
391    pub fn blend_color_dodge(&self, other: Rgb<f32>) -> Rgb<f32> {
392        let new_r = color_dodge!(self.r, other.r);
393        let new_g = color_dodge!(self.g, other.g);
394        let new_b = color_dodge!(self.b, other.b);
395        Rgb::<f32>::new(new_r, new_g, new_b)
396    }
397
398    #[inline]
399    pub fn blend_screen(&self, other: Rgb<f32>) -> Rgb<f32> {
400        let new_r = color_screen!(self.r, other.r);
401        let new_g = color_screen!(self.g, other.g);
402        let new_b = color_screen!(self.b, other.b);
403        Rgb::<f32>::new(new_r, new_g, new_b)
404    }
405
406    #[inline]
407    pub fn blend_linear_light(&self, other: Rgb<f32>) -> Rgb<f32> {
408        let new_r = color_linear_light!(self.r, other.r);
409        let new_g = color_linear_light!(self.g, other.g);
410        let new_b = color_linear_light!(self.b, other.b);
411        Rgb::<f32>::new(new_r, new_g, new_b)
412    }
413
414    #[inline]
415    pub fn blend_vivid_light(&self, other: Rgb<f32>) -> Rgb<f32> {
416        let new_r = color_vivid_light!(self.r, other.r);
417        let new_g = color_vivid_light!(self.g, other.g);
418        let new_b = color_vivid_light!(self.b, other.b);
419        Rgb::<f32>::new(new_r, new_g, new_b)
420    }
421
422    #[inline]
423    pub fn blend_pin_light(&self, other: Rgb<f32>) -> Rgb<f32> {
424        let new_r = color_pin_light!(self.r, other.r);
425        let new_g = color_pin_light!(self.g, other.g);
426        let new_b = color_pin_light!(self.b, other.b);
427        Rgb::<f32>::new(new_r, new_g, new_b)
428    }
429
430    #[inline]
431    pub fn blend_hard_mix(&self, other: Rgb<f32>) -> Rgb<f32> {
432        let new_r = color_hard_mix!(self.r, other.r);
433        let new_g = color_hard_mix!(self.g, other.g);
434        let new_b = color_hard_mix!(self.b, other.b);
435        Rgb::<f32>::new(new_r, new_g, new_b)
436    }
437
438    #[inline]
439    pub fn blend_reflect(&self, other: Rgb<f32>) -> Rgb<f32> {
440        let new_r = color_reflect!(self.r, other.r);
441        let new_g = color_reflect!(self.g, other.g);
442        let new_b = color_reflect!(self.b, other.b);
443        Rgb::<f32>::new(new_r, new_g, new_b)
444    }
445
446    #[inline]
447    pub fn blend_difference(&self, other: Rgb<f32>) -> Rgb<f32> {
448        let new_r = color_difference!(self.r, other.r);
449        let new_g = color_difference!(self.g, other.g);
450        let new_b = color_difference!(self.b, other.b);
451        Rgb::<f32>::new(new_r, new_g, new_b)
452    }
453
454    #[inline]
455    pub fn blend_hard_light(&self, other: Rgb<f32>) -> Rgb<f32> {
456        let new_r = color_hard_light!(self.r, other.r);
457        let new_g = color_hard_light!(self.g, other.g);
458        let new_b = color_hard_light!(self.b, other.b);
459        Rgb::<f32>::new(new_r, new_g, new_b)
460    }
461
462    #[inline]
463    pub fn blend_soft_light(&self, other: Rgb<f32>) -> Rgb<f32> {
464        let new_r = color_soft_light!(self.r, other.r);
465        let new_g = color_soft_light!(self.g, other.g);
466        let new_b = color_soft_light!(self.b, other.b);
467        Rgb::<f32>::new(new_r, new_g, new_b)
468    }
469
470    #[inline]
471    pub fn blend_exclusion(&self, other: Rgb<f32>) -> Rgb<f32> {
472        let new_r = color_exclusion!(self.r, other.r);
473        let new_g = color_exclusion!(self.g, other.g);
474        let new_b = color_exclusion!(self.b, other.b);
475        Rgb::<f32>::new(new_r, new_g, new_b)
476    }
477
478    #[inline]
479    pub fn saturation(&self, saturation: f32) -> Rgb<f32> {
480        let (new_r, new_g, new_b) = adjust_saturation!(self, saturation);
481        Rgb::<f32>::new(new_r, new_g, new_b)
482    }
483
484    #[inline]
485    pub fn grayscale(&self, grayscale_amount: f32) -> Rgb<f32> {
486        let gray = self.r * 0.299f32 + self.g * 0.587 + self.b * 0.114;
487        let new_r = self.r * (1.0 - grayscale_amount) + gray * grayscale_amount;
488        let new_g = self.g * (1.0 - grayscale_amount) + gray * grayscale_amount;
489        let new_b = self.b * (1.0 - grayscale_amount) + gray * grayscale_amount;
490        Rgb::<f32>::new(new_r, new_g, new_b)
491    }
492}
493
494impl From<Sigmoidal> for Rgb<u8> {
495    #[inline]
496    fn from(value: Sigmoidal) -> Self {
497        value.to_rgb()
498    }
499}
500
501impl<T> Rgb<T> {
502    pub fn new(r: T, g: T, b: T) -> Rgb<T> {
503        Rgb { r, g, b }
504    }
505}
506
507impl<T> Rgb<T>
508where
509    T: Copy,
510{
511    pub fn dup(v: T) -> Rgb<T> {
512        Rgb { r: v, g: v, b: v }
513    }
514}
515
516impl<T> Index<usize> for Rgb<T> {
517    type Output = T;
518
519    fn index(&self, index: usize) -> &T {
520        match index {
521            0 => &self.r,
522            1 => &self.g,
523            2 => &self.b,
524            _ => panic!("Index out of bounds for Rgb"),
525        }
526    }
527}
528
529impl<T> IndexMut<usize> for Rgb<T> {
530    fn index_mut(&mut self, index: usize) -> &mut T {
531        match index {
532            0 => &mut self.r,
533            1 => &mut self.g,
534            2 => &mut self.b,
535            _ => panic!("Index out of bounds for RGB"),
536        }
537    }
538}
539
540macro_rules! generated_float_definition_rgb {
541    ($T: ty) => {
542        impl Rgb<$T> {
543            #[inline]
544            pub fn zeroed() -> Rgb<$T> {
545                Rgb::<$T>::new(0., 0., 0.)
546            }
547
548            #[inline]
549            pub fn ones() -> Rgb<$T> {
550                Rgb::<$T>::new(1., 1., 1.)
551            }
552
553            #[inline]
554            pub fn white() -> Rgb<$T> {
555                Rgb::<$T>::ones()
556            }
557
558            #[inline]
559            pub fn black() -> Rgb<$T> {
560                Rgb::<$T>::zeroed()
561            }
562
563            #[inline]
564            pub fn contrast(&self, contrast: $T) -> Rgb<$T> {
565                let new_r = self.r * contrast + -0.5 * contrast + 0.5;
566                let new_g = self.g * contrast + -0.5 * contrast + 0.5;
567                let new_b = self.b * contrast + -0.5 * contrast + 0.5;
568                Rgb::<$T>::new(new_r, new_g, new_b)
569            }
570        }
571    };
572}
573
574generated_float_definition_rgb!(f32);
575generated_float_definition_rgb!(f64);
576
577macro_rules! generated_integral_definition_rgb {
578    ($T: ty) => {
579        impl Rgb<$T> {
580            #[inline]
581            pub fn zeroed() -> Rgb<$T> {
582                Rgb::<$T>::new(0, 0, 0)
583            }
584
585            #[inline]
586            pub fn capped() -> Rgb<$T> {
587                Rgb::<$T>::new(<$T>::MAX, <$T>::MAX, <$T>::MAX)
588            }
589
590            #[inline]
591            pub fn white() -> Rgb<$T> {
592                Rgb::<$T>::capped()
593            }
594
595            #[inline]
596            pub fn black() -> Rgb<$T> {
597                Rgb::<$T>::new(0, 0, 0)
598            }
599        }
600    };
601}
602
603generated_integral_definition_rgb!(u8);
604generated_integral_definition_rgb!(u16);
605generated_integral_definition_rgb!(i8);
606generated_integral_definition_rgb!(i16);
607generated_integral_definition_rgb!(i32);
608generated_integral_definition_rgb!(u32);
609
610macro_rules! generated_default_definition_rgb {
611    ($T: ty) => {
612        impl Default for Rgb<$T> {
613            fn default() -> Self {
614                Rgb::<$T>::zeroed()
615            }
616        }
617    };
618}
619
620generated_default_definition_rgb!(u8);
621generated_default_definition_rgb!(u16);
622generated_default_definition_rgb!(i8);
623generated_default_definition_rgb!(i16);
624generated_default_definition_rgb!(i32);
625generated_default_definition_rgb!(u32);
626generated_default_definition_rgb!(f32);
627generated_default_definition_rgb!(f64);
628
629impl Rgb<u8> {
630    #[inline]
631    #[allow(clippy::manual_clamp)]
632    pub fn contrast(&self, contrast: f32) -> Rgb<u8> {
633        let new_r = (self.r as f32 * contrast + -127.5f32 * contrast + 127.5f32)
634            .round()
635            .min(255f32)
636            .max(0f32);
637        let new_g = (self.g as f32 * contrast + -127.5f32 * contrast + 127.5f32)
638            .round()
639            .min(255f32)
640            .max(0f32);
641        let new_b = (self.b as f32 * contrast + -127.5f32 * contrast + 127.5f32)
642            .round()
643            .min(255f32)
644            .max(0f32);
645        Rgb::<u8>::new(new_r as u8, new_g as u8, new_b as u8)
646    }
647
648    #[inline]
649    pub fn grayscale(&self, grayscale_amount: f32) -> Rgb<u8> {
650        let gray = self.r as f32 * 0.299f32 + self.g as f32 * 0.587 + self.b as f32 * 0.114;
651        let new_r = self.r as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
652        let new_g = self.g as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
653        let new_b = self.b as f32 * (255f32 - grayscale_amount) + gray * grayscale_amount;
654        Rgb::<u8>::new(
655            new_r.round() as u8,
656            new_g.round() as u8,
657            new_b.round() as u8,
658        )
659    }
660}
661
662impl<T> Rgb<T>
663where
664    T: Copy,
665{
666    pub fn to_rgba(&self, a: T) -> Rgba<T> {
667        Rgba::<T>::new(self.r, self.g, self.b, a)
668    }
669}
670
671impl<T> EuclideanDistance for Rgb<T>
672where
673    T: Copy + AsPrimitive<f32>,
674{
675    fn euclidean_distance(&self, other: Rgb<T>) -> f32 {
676        let dr = self.r.as_() - other.r.as_();
677        let dg = self.g.as_() - other.g.as_();
678        let db = self.b.as_() - other.b.as_();
679        (dr * dr + dg * dg + db * db).sqrt()
680    }
681}
682
683impl<T> TaxicabDistance for Rgb<T>
684where
685    T: Copy + AsPrimitive<f32>,
686{
687    fn taxicab_distance(&self, other: Self) -> f32 {
688        let dr = self.r.as_() - other.r.as_();
689        let dg = self.g.as_() - other.g.as_();
690        let db = self.b.as_() - other.b.as_();
691        dr.abs() + dg.abs() + db.abs()
692    }
693}
694
695impl<T> Add for Rgb<T>
696where
697    T: Add<Output = T>,
698{
699    type Output = Rgb<T>;
700
701    #[inline]
702    fn add(self, rhs: Self) -> Self::Output {
703        Rgb::new(self.r + rhs.r, self.g + rhs.g, self.b + rhs.b)
704    }
705}
706
707impl<T> Sub for Rgb<T>
708where
709    T: Sub<Output = T>,
710{
711    type Output = Rgb<T>;
712
713    #[inline]
714    fn sub(self, rhs: Self) -> Self::Output {
715        Rgb::new(self.r - rhs.r, self.g - rhs.g, self.b - rhs.b)
716    }
717}
718
719impl<T> Div for Rgb<T>
720where
721    T: Div<Output = T>,
722{
723    type Output = Rgb<T>;
724
725    #[inline]
726    fn div(self, rhs: Self) -> Self::Output {
727        Rgb::new(self.r / rhs.r, self.g / rhs.g, self.b / rhs.b)
728    }
729}
730
731impl<T> Mul for Rgb<T>
732where
733    T: Mul<Output = T>,
734{
735    type Output = Rgb<T>;
736
737    #[inline]
738    fn mul(self, rhs: Self) -> Self::Output {
739        Rgb::new(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
740    }
741}
742
743impl<T> MulAssign for Rgb<T>
744where
745    T: MulAssign<T>,
746{
747    #[inline]
748    fn mul_assign(&mut self, rhs: Self) {
749        self.r *= rhs.r;
750        self.g *= rhs.g;
751        self.b *= rhs.b;
752    }
753}
754
755macro_rules! generated_mul_assign_definition_rgb {
756    ($T: ty) => {
757        impl<T> MulAssign<$T> for Rgb<T>
758        where
759            T: MulAssign<$T>,
760        {
761            #[inline]
762            fn mul_assign(&mut self, rhs: $T) {
763                self.r *= rhs;
764                self.g *= rhs;
765                self.b *= rhs;
766            }
767        }
768    };
769}
770
771generated_mul_assign_definition_rgb!(i8);
772generated_mul_assign_definition_rgb!(u8);
773generated_mul_assign_definition_rgb!(u16);
774generated_mul_assign_definition_rgb!(i16);
775generated_mul_assign_definition_rgb!(u32);
776generated_mul_assign_definition_rgb!(i32);
777generated_mul_assign_definition_rgb!(f32);
778generated_mul_assign_definition_rgb!(f64);
779
780impl<T> AddAssign for Rgb<T>
781where
782    T: AddAssign<T>,
783{
784    fn add_assign(&mut self, rhs: Self) {
785        self.r += rhs.r;
786        self.g += rhs.g;
787        self.b += rhs.b;
788    }
789}
790
791macro_rules! generated_add_assign_definition_rgb {
792    ($T: ty) => {
793        impl<T> AddAssign<$T> for Rgb<T>
794        where
795            T: AddAssign<$T>,
796        {
797            #[inline]
798            fn add_assign(&mut self, rhs: $T) {
799                self.r += rhs;
800                self.g += rhs;
801                self.b += rhs;
802            }
803        }
804    };
805}
806
807generated_add_assign_definition_rgb!(i8);
808generated_add_assign_definition_rgb!(u8);
809generated_add_assign_definition_rgb!(u16);
810generated_add_assign_definition_rgb!(i16);
811generated_add_assign_definition_rgb!(u32);
812generated_add_assign_definition_rgb!(i32);
813generated_add_assign_definition_rgb!(f32);
814generated_add_assign_definition_rgb!(f64);
815
816impl<T> DivAssign for Rgb<T>
817where
818    T: DivAssign<T>,
819{
820    #[inline]
821    fn div_assign(&mut self, rhs: Self) {
822        self.r /= rhs.r;
823        self.g /= rhs.g;
824        self.b /= rhs.b;
825    }
826}
827
828macro_rules! generated_div_assign_definition_rgb {
829    ($T: ty) => {
830        impl<T> DivAssign<$T> for Rgb<T>
831        where
832            T: DivAssign<$T>,
833        {
834            #[inline]
835            fn div_assign(&mut self, rhs: $T) {
836                self.r /= rhs;
837                self.g /= rhs;
838                self.b /= rhs;
839            }
840        }
841    };
842}
843
844generated_div_assign_definition_rgb!(u8);
845generated_div_assign_definition_rgb!(i8);
846generated_div_assign_definition_rgb!(u16);
847generated_div_assign_definition_rgb!(i16);
848generated_div_assign_definition_rgb!(u32);
849generated_div_assign_definition_rgb!(i32);
850generated_div_assign_definition_rgb!(f32);
851generated_div_assign_definition_rgb!(f64);
852
853impl<T> Neg for Rgb<T>
854where
855    T: Neg<Output = T>,
856{
857    type Output = Rgb<T>;
858
859    #[inline]
860    fn neg(self) -> Self::Output {
861        Rgb::new(-self.r, -self.g, -self.b)
862    }
863}
864
865impl<T> Rgb<T>
866where
867    T: Num + PartialOrd + Copy + Bounded + Ord,
868{
869    /// Clamp function to clamp each channel within a given range
870    #[inline]
871    #[allow(clippy::manual_clamp)]
872    pub fn clamp(&self, min_value: T, max_value: T) -> Rgb<T> {
873        Rgb::new(
874            min(max(self.r, min_value), max_value),
875            min(max(self.g, min_value), max_value),
876            min(max(self.b, min_value), max_value),
877        )
878    }
879
880    /// Min function to define min
881    #[inline]
882    pub fn min(&self, other_min: T) -> Rgb<T> {
883        Rgb::new(
884            min(self.r, other_min),
885            min(self.g, other_min),
886            min(self.b, other_min),
887        )
888    }
889
890    /// Max function to define max
891    #[inline]
892    pub fn max(&self, other_max: T) -> Rgb<T> {
893        Rgb::new(
894            max(self.r, other_max),
895            max(self.g, other_max),
896            max(self.b, other_max),
897        )
898    }
899
900    /// Clamp function to clamp each channel within a given range
901    #[inline]
902    #[allow(clippy::manual_clamp)]
903    pub fn clamp_p(&self, min_value: Rgb<T>, max_value: Rgb<T>) -> Rgb<T> {
904        Rgb::new(
905            max(min(self.r, max_value.r), min_value.r),
906            max(min(self.g, max_value.g), min_value.g),
907            max(min(self.b, max_value.b), min_value.b),
908        )
909    }
910
911    /// Min function to define min
912    #[inline]
913    pub fn min_p(&self, other_min: Rgb<T>) -> Rgb<T> {
914        Rgb::new(
915            min(self.r, other_min.r),
916            min(self.g, other_min.g),
917            min(self.b, other_min.b),
918        )
919    }
920
921    /// Max function to define max
922    #[inline]
923    pub fn max_p(&self, other_max: Rgb<T>) -> Rgb<T> {
924        Rgb::new(
925            max(self.r, other_max.r),
926            max(self.g, other_max.g),
927            max(self.b, other_max.b),
928        )
929    }
930}
931
932impl<T> Rgb<T>
933where
934    T: Float + 'static,
935    f32: AsPrimitive<T>,
936{
937    #[inline]
938    pub fn sqrt(&self) -> Rgb<T> {
939        let zeros = 0f32.as_();
940        Rgb::new(
941            if self.r.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
942                0f32.as_()
943            } else {
944                self.r.sqrt()
945            },
946            if self.g.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
947                0f32.as_()
948            } else {
949                self.g.sqrt()
950            },
951            if self.b.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
952                0f32.as_()
953            } else {
954                self.b.sqrt()
955            },
956        )
957    }
958
959    #[inline]
960    pub fn cbrt(&self) -> Rgb<T> {
961        Rgb::new(self.r.cbrt(), self.g.cbrt(), self.b.cbrt())
962    }
963}
964
965impl<T> Pow<T> for Rgb<T>
966where
967    T: Float,
968{
969    type Output = Rgb<T>;
970
971    #[inline]
972    fn pow(self, rhs: T) -> Self::Output {
973        Rgb::<T>::new(self.r.powf(rhs), self.g.powf(rhs), self.b.powf(rhs))
974    }
975}
976
977impl<T> Pow<Rgb<T>> for Rgb<T>
978where
979    T: Float,
980{
981    type Output = Rgb<T>;
982
983    #[inline]
984    fn pow(self, rhs: Rgb<T>) -> Self::Output {
985        Rgb::<T>::new(self.r.powf(rhs.r), self.g.powf(rhs.g), self.b.powf(rhs.b))
986    }
987}
988
989impl<T> Rgb<T> {
990    pub fn cast<V>(self) -> Rgb<V>
991    where
992        T: AsPrimitive<V>,
993        V: Copy + 'static,
994    {
995        Rgb::new(self.r.as_(), self.g.as_(), self.b.as_())
996    }
997}
998
999impl<T> Rgb<T>
1000where
1001    T: Float + 'static,
1002{
1003    pub fn round(self) -> Rgb<T> {
1004        Rgb::new(self.r.round(), self.g.round(), self.b.round())
1005    }
1006}