moxcms/
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::math::{FusedMultiplyAdd, m_clamp, m_max, m_min};
8use crate::mlaf::mlaf;
9use crate::{Matrix3f, Vector3, Xyz};
10use num_traits::{AsPrimitive, Bounded, Float, Num, Pow, Signed};
11use pxfm::{
12    f_exp, f_exp2, f_exp2f, f_exp10, f_exp10f, f_expf, f_log, f_log2, f_log2f, f_log10, f_log10f,
13    f_logf, f_pow, f_powf,
14};
15use std::cmp::Ordering;
16use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
17
18#[repr(C)]
19#[derive(Debug, PartialOrd, PartialEq, Clone, Copy, Default)]
20/// Represents any RGB values
21pub struct Rgb<T> {
22    /// Red component
23    pub r: T,
24    /// Green component
25    pub g: T,
26    /// Blue component
27    pub b: T,
28}
29
30impl<T> Rgb<T> {
31    pub fn new(r: T, g: T, b: T) -> Rgb<T> {
32        Rgb { r, g, b }
33    }
34}
35
36impl<T> Rgb<T>
37where
38    T: Copy,
39{
40    pub fn dup(v: T) -> Rgb<T> {
41        Rgb { r: v, g: v, b: v }
42    }
43
44    #[inline]
45    pub const fn to_vector(self) -> Vector3<T> {
46        Vector3 {
47            v: [self.r, self.g, self.b],
48        }
49    }
50}
51
52impl Rgb<f32> {
53    #[inline(always)]
54    pub fn apply(&self, matrix: Matrix3f) -> Rgb<f32> {
55        let new_r = mlaf(
56            mlaf(self.r * matrix.v[0][0], self.g, matrix.v[0][1]),
57            self.b,
58            matrix.v[0][2],
59        );
60
61        let new_g = mlaf(
62            mlaf(self.r * matrix.v[1][0], self.g, matrix.v[1][1]),
63            self.b,
64            matrix.v[1][2],
65        );
66
67        let new_b = mlaf(
68            mlaf(self.r * matrix.v[2][0], self.g, matrix.v[2][1]),
69            self.b,
70            matrix.v[2][2],
71        );
72
73        Rgb {
74            r: new_r,
75            g: new_g,
76            b: new_b,
77        }
78    }
79
80    #[inline(always)]
81    pub fn to_xyz(&self, matrix: Matrix3f) -> Xyz {
82        let new_self = self.apply(matrix);
83        Xyz {
84            x: new_self.r,
85            y: new_self.g,
86            z: new_self.b,
87        }
88    }
89
90    #[inline(always)]
91    pub fn is_out_of_gamut(&self) -> bool {
92        !(0.0..=1.0).contains(&self.r)
93            || !(0.0..=1.0).contains(&self.g)
94            || !(0.0..=1.0).contains(&self.b)
95    }
96}
97
98impl<T> Index<usize> for Rgb<T> {
99    type Output = T;
100
101    fn index(&self, index: usize) -> &T {
102        match index {
103            0 => &self.r,
104            1 => &self.g,
105            2 => &self.b,
106            _ => panic!("Index out of bounds for Rgb"),
107        }
108    }
109}
110
111impl<T> IndexMut<usize> for Rgb<T> {
112    fn index_mut(&mut self, index: usize) -> &mut T {
113        match index {
114            0 => &mut self.r,
115            1 => &mut self.g,
116            2 => &mut self.b,
117            _ => panic!("Index out of bounds for RGB"),
118        }
119    }
120}
121
122macro_rules! generated_float_definition_rgb {
123    ($T: ty) => {
124        impl Rgb<$T> {
125            #[inline]
126            pub fn zeroed() -> Rgb<$T> {
127                Rgb::<$T>::new(0., 0., 0.)
128            }
129
130            #[inline]
131            pub fn ones() -> Rgb<$T> {
132                Rgb::<$T>::new(1., 1., 1.)
133            }
134
135            #[inline]
136            pub fn white() -> Rgb<$T> {
137                Rgb::<$T>::ones()
138            }
139
140            #[inline]
141            pub fn black() -> Rgb<$T> {
142                Rgb::<$T>::zeroed()
143            }
144        }
145    };
146}
147
148generated_float_definition_rgb!(f32);
149generated_float_definition_rgb!(f64);
150
151macro_rules! generated_integral_definition_rgb {
152    ($T: ty) => {
153        impl Rgb<$T> {
154            #[inline]
155            pub fn zeroed() -> Rgb<$T> {
156                Rgb::<$T>::new(0, 0, 0)
157            }
158
159            #[inline]
160            pub fn capped() -> Rgb<$T> {
161                Rgb::<$T>::new(<$T>::MAX, <$T>::MAX, <$T>::MAX)
162            }
163
164            #[inline]
165            pub fn white() -> Rgb<$T> {
166                Rgb::<$T>::capped()
167            }
168
169            #[inline]
170            pub fn black() -> Rgb<$T> {
171                Rgb::<$T>::new(0, 0, 0)
172            }
173        }
174    };
175}
176
177generated_integral_definition_rgb!(u8);
178generated_integral_definition_rgb!(u16);
179generated_integral_definition_rgb!(i8);
180generated_integral_definition_rgb!(i16);
181generated_integral_definition_rgb!(i32);
182generated_integral_definition_rgb!(u32);
183
184pub trait FusedPow<T> {
185    fn f_pow(&self, power: T) -> Self;
186}
187
188pub trait FusedLog2<T> {
189    fn f_log2(&self) -> Self;
190}
191
192pub trait FusedLog10<T> {
193    fn f_log10(&self) -> Self;
194}
195
196pub trait FusedLog<T> {
197    fn f_log(&self) -> Self;
198}
199
200pub trait FusedExp<T> {
201    fn f_exp(&self) -> Self;
202}
203
204pub trait FusedExp2<T> {
205    fn f_exp2(&self) -> Self;
206}
207
208pub trait FusedExp10<T> {
209    fn f_exp10(&self) -> Self;
210}
211
212impl FusedPow<Rgb<f32>> for Rgb<f32> {
213    fn f_pow(&self, power: Rgb<f32>) -> Rgb<f32> {
214        Rgb::new(
215            f_powf(self.r, power.r),
216            f_powf(self.g, power.g),
217            f_powf(self.b, power.b),
218        )
219    }
220}
221
222impl FusedPow<Rgb<f64>> for Rgb<f64> {
223    fn f_pow(&self, power: Rgb<f64>) -> Rgb<f64> {
224        Rgb::new(
225            f_pow(self.r, power.r),
226            f_pow(self.g, power.g),
227            f_pow(self.b, power.b),
228        )
229    }
230}
231
232impl FusedLog2<Rgb<f32>> for Rgb<f32> {
233    #[inline]
234    fn f_log2(&self) -> Rgb<f32> {
235        Rgb::new(f_log2f(self.r), f_log2f(self.g), f_log2f(self.b))
236    }
237}
238
239impl FusedLog2<Rgb<f64>> for Rgb<f64> {
240    #[inline]
241    fn f_log2(&self) -> Rgb<f64> {
242        Rgb::new(f_log2(self.r), f_log2(self.g), f_log2(self.b))
243    }
244}
245
246impl FusedLog<Rgb<f32>> for Rgb<f32> {
247    #[inline]
248    fn f_log(&self) -> Rgb<f32> {
249        Rgb::new(f_logf(self.r), f_logf(self.g), f_logf(self.b))
250    }
251}
252
253impl FusedLog<Rgb<f64>> for Rgb<f64> {
254    #[inline]
255    fn f_log(&self) -> Rgb<f64> {
256        Rgb::new(f_log(self.r), f_log(self.g), f_log(self.b))
257    }
258}
259
260impl FusedLog10<Rgb<f32>> for Rgb<f32> {
261    #[inline]
262    fn f_log10(&self) -> Rgb<f32> {
263        Rgb::new(f_log10f(self.r), f_log10f(self.g), f_log10f(self.b))
264    }
265}
266
267impl FusedLog10<Rgb<f64>> for Rgb<f64> {
268    #[inline]
269    fn f_log10(&self) -> Rgb<f64> {
270        Rgb::new(f_log10(self.r), f_log10(self.g), f_log10(self.b))
271    }
272}
273
274impl FusedExp<Rgb<f32>> for Rgb<f32> {
275    #[inline]
276    fn f_exp(&self) -> Rgb<f32> {
277        Rgb::new(f_expf(self.r), f_expf(self.g), f_expf(self.b))
278    }
279}
280
281impl FusedExp<Rgb<f64>> for Rgb<f64> {
282    #[inline]
283    fn f_exp(&self) -> Rgb<f64> {
284        Rgb::new(f_exp(self.r), f_exp(self.g), f_exp(self.b))
285    }
286}
287
288impl FusedExp2<Rgb<f32>> for Rgb<f32> {
289    #[inline]
290    fn f_exp2(&self) -> Rgb<f32> {
291        Rgb::new(f_exp2f(self.r), f_exp2f(self.g), f_exp2f(self.b))
292    }
293}
294
295impl FusedExp2<Rgb<f64>> for Rgb<f64> {
296    #[inline]
297    fn f_exp2(&self) -> Rgb<f64> {
298        Rgb::new(f_exp2(self.r), f_exp2(self.g), f_exp2(self.b))
299    }
300}
301
302impl FusedExp10<Rgb<f32>> for Rgb<f32> {
303    #[inline]
304    fn f_exp10(&self) -> Rgb<f32> {
305        Rgb::new(f_exp10f(self.r), f_exp10f(self.g), f_exp10f(self.b))
306    }
307}
308
309impl FusedExp10<Rgb<f64>> for Rgb<f64> {
310    #[inline]
311    fn f_exp10(&self) -> Rgb<f64> {
312        Rgb::new(f_exp10(self.r), f_exp10(self.g), f_exp10(self.b))
313    }
314}
315
316impl<T> Rgb<T>
317where
318    T: Copy + AsPrimitive<f32>,
319{
320    pub fn euclidean_distance(&self, other: Rgb<T>) -> f32 {
321        let dr = self.r.as_() - other.r.as_();
322        let dg = self.g.as_() - other.g.as_();
323        let db = self.b.as_() - other.b.as_();
324        (dr * dr + dg * dg + db * db).sqrt()
325    }
326}
327
328impl<T> Rgb<T>
329where
330    T: Copy + AsPrimitive<f32>,
331{
332    pub fn taxicab_distance(&self, other: Self) -> f32 {
333        let dr = self.r.as_() - other.r.as_();
334        let dg = self.g.as_() - other.g.as_();
335        let db = self.b.as_() - other.b.as_();
336        dr.abs() + dg.abs() + db.abs()
337    }
338}
339
340impl<T> Add for Rgb<T>
341where
342    T: Add<Output = T>,
343{
344    type Output = Rgb<T>;
345
346    #[inline]
347    fn add(self, rhs: Self) -> Self::Output {
348        Rgb::new(self.r + rhs.r, self.g + rhs.g, self.b + rhs.b)
349    }
350}
351
352impl<T> Sub for Rgb<T>
353where
354    T: Sub<Output = T>,
355{
356    type Output = Rgb<T>;
357
358    #[inline]
359    fn sub(self, rhs: Self) -> Self::Output {
360        Rgb::new(self.r - rhs.r, self.g - rhs.g, self.b - rhs.b)
361    }
362}
363
364impl<T: Copy + Clone> Sub<T> for Rgb<T>
365where
366    T: Sub<Output = T>,
367{
368    type Output = Rgb<T>;
369
370    #[inline]
371    fn sub(self, rhs: T) -> Self::Output {
372        Rgb::new(self.r - rhs, self.g - rhs, self.b - rhs)
373    }
374}
375
376impl<T: Copy + Clone> Add<T> for Rgb<T>
377where
378    T: Add<Output = T>,
379{
380    type Output = Rgb<T>;
381
382    #[inline]
383    fn add(self, rhs: T) -> Self::Output {
384        Rgb::new(self.r + rhs, self.g + rhs, self.b + rhs)
385    }
386}
387
388impl<T: Copy + Clone> Rgb<T>
389where
390    T: Signed,
391{
392    #[inline]
393    pub fn abs(self) -> Self {
394        Rgb::new(self.r.abs(), self.g.abs(), self.b.abs())
395    }
396}
397
398impl<T> Div for Rgb<T>
399where
400    T: Div<Output = T>,
401{
402    type Output = Rgb<T>;
403
404    #[inline]
405    fn div(self, rhs: Self) -> Self::Output {
406        Rgb::new(self.r / rhs.r, self.g / rhs.g, self.b / rhs.b)
407    }
408}
409
410impl<T: Clone + Copy> Div<T> for Rgb<T>
411where
412    T: Div<Output = T>,
413{
414    type Output = Rgb<T>;
415
416    #[inline]
417    fn div(self, rhs: T) -> Self::Output {
418        Rgb::new(self.r / rhs, self.g / rhs, self.b / rhs)
419    }
420}
421
422impl<T> Mul for Rgb<T>
423where
424    T: Mul<Output = T>,
425{
426    type Output = Rgb<T>;
427
428    #[inline]
429    fn mul(self, rhs: Self) -> Self::Output {
430        Rgb::new(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
431    }
432}
433
434impl<T: Clone + Copy> Mul<T> for Rgb<T>
435where
436    T: Mul<Output = T>,
437{
438    type Output = Rgb<T>;
439
440    #[inline]
441    fn mul(self, rhs: T) -> Self::Output {
442        Rgb::new(self.r * rhs, self.g * rhs, self.b * rhs)
443    }
444}
445
446impl<T> MulAssign for Rgb<T>
447where
448    T: MulAssign<T>,
449{
450    #[inline]
451    fn mul_assign(&mut self, rhs: Self) {
452        self.r *= rhs.r;
453        self.g *= rhs.g;
454        self.b *= rhs.b;
455    }
456}
457
458macro_rules! generated_mul_assign_definition_rgb {
459    ($T: ty) => {
460        impl<T> MulAssign<$T> for Rgb<T>
461        where
462            T: MulAssign<$T>,
463        {
464            #[inline]
465            fn mul_assign(&mut self, rhs: $T) {
466                self.r *= rhs;
467                self.g *= rhs;
468                self.b *= rhs;
469            }
470        }
471    };
472}
473
474generated_mul_assign_definition_rgb!(i8);
475generated_mul_assign_definition_rgb!(u8);
476generated_mul_assign_definition_rgb!(u16);
477generated_mul_assign_definition_rgb!(i16);
478generated_mul_assign_definition_rgb!(u32);
479generated_mul_assign_definition_rgb!(i32);
480generated_mul_assign_definition_rgb!(f32);
481generated_mul_assign_definition_rgb!(f64);
482
483impl<T> AddAssign for Rgb<T>
484where
485    T: AddAssign<T>,
486{
487    #[inline]
488    fn add_assign(&mut self, rhs: Self) {
489        self.r += rhs.r;
490        self.g += rhs.g;
491        self.b += rhs.b;
492    }
493}
494
495macro_rules! generated_add_assign_definition_rgb {
496    ($T: ty) => {
497        impl<T: Copy> AddAssign<$T> for Rgb<T>
498        where
499            T: AddAssign<$T>,
500        {
501            #[inline]
502            fn add_assign(&mut self, rhs: $T) {
503                self.r += rhs;
504                self.g += rhs;
505                self.b += rhs;
506            }
507        }
508    };
509}
510
511generated_add_assign_definition_rgb!(i8);
512generated_add_assign_definition_rgb!(u8);
513generated_add_assign_definition_rgb!(u16);
514generated_add_assign_definition_rgb!(i16);
515generated_add_assign_definition_rgb!(u32);
516generated_add_assign_definition_rgb!(i32);
517generated_add_assign_definition_rgb!(f32);
518generated_add_assign_definition_rgb!(f64);
519
520impl<T> DivAssign for Rgb<T>
521where
522    T: DivAssign<T>,
523{
524    #[inline]
525    fn div_assign(&mut self, rhs: Self) {
526        self.r /= rhs.r;
527        self.g /= rhs.g;
528        self.b /= rhs.b;
529    }
530}
531
532macro_rules! generated_div_assign_definition_rgb {
533    ($T: ty) => {
534        impl<T: Copy> DivAssign<$T> for Rgb<T>
535        where
536            T: DivAssign<$T>,
537        {
538            #[inline]
539            fn div_assign(&mut self, rhs: $T) {
540                self.r /= rhs;
541                self.g /= rhs;
542                self.b /= rhs;
543            }
544        }
545    };
546}
547
548generated_div_assign_definition_rgb!(u8);
549generated_div_assign_definition_rgb!(i8);
550generated_div_assign_definition_rgb!(u16);
551generated_div_assign_definition_rgb!(i16);
552generated_div_assign_definition_rgb!(u32);
553generated_div_assign_definition_rgb!(i32);
554generated_div_assign_definition_rgb!(f32);
555generated_div_assign_definition_rgb!(f64);
556
557impl<T> Neg for Rgb<T>
558where
559    T: Neg<Output = T>,
560{
561    type Output = Rgb<T>;
562
563    #[inline]
564    fn neg(self) -> Self::Output {
565        Rgb::new(-self.r, -self.g, -self.b)
566    }
567}
568
569impl<T> Rgb<T>
570where
571    T: FusedMultiplyAdd<T>,
572{
573    pub fn mla(&self, b: Rgb<T>, c: Rgb<T>) -> Rgb<T> {
574        Rgb::new(
575            self.r.mla(b.r, c.r),
576            self.g.mla(b.g, c.g),
577            self.b.mla(b.b, c.b),
578        )
579    }
580}
581
582impl<T> Rgb<T>
583where
584    T: Num + PartialOrd + Copy + Bounded,
585{
586    /// Clamp function to clamp each channel within a given range
587    #[inline]
588    #[allow(clippy::manual_clamp)]
589    pub fn clamp(&self, min_value: T, max_value: T) -> Rgb<T> {
590        Rgb::new(
591            m_clamp(self.r, min_value, max_value),
592            m_clamp(self.g, min_value, max_value),
593            m_clamp(self.b, min_value, max_value),
594        )
595    }
596
597    /// Min function to define min
598    #[inline]
599    pub fn min(&self, other_min: T) -> Rgb<T> {
600        Rgb::new(
601            m_min(self.r, other_min),
602            m_min(self.g, other_min),
603            m_min(self.b, other_min),
604        )
605    }
606
607    /// Max function to define max
608    #[inline]
609    pub fn max(&self, other_max: T) -> Rgb<T> {
610        Rgb::new(
611            m_max(self.r, other_max),
612            m_max(self.g, other_max),
613            m_max(self.b, other_max),
614        )
615    }
616
617    /// Clamp function to clamp each channel within a given range
618    #[inline]
619    #[allow(clippy::manual_clamp)]
620    pub fn clamp_p(&self, min_value: Rgb<T>, max_value: Rgb<T>) -> Rgb<T> {
621        Rgb::new(
622            m_clamp(self.r, max_value.r, min_value.r),
623            m_clamp(self.g, max_value.g, min_value.g),
624            m_clamp(self.b, max_value.b, min_value.b),
625        )
626    }
627
628    /// Min function to define min
629    #[inline]
630    pub fn min_p(&self, other_min: Rgb<T>) -> Rgb<T> {
631        Rgb::new(
632            m_min(self.r, other_min.r),
633            m_min(self.g, other_min.g),
634            m_min(self.b, other_min.b),
635        )
636    }
637
638    /// Max function to define max
639    #[inline]
640    pub fn max_p(&self, other_max: Rgb<T>) -> Rgb<T> {
641        Rgb::new(
642            m_max(self.r, other_max.r),
643            m_max(self.g, other_max.g),
644            m_max(self.b, other_max.b),
645        )
646    }
647}
648
649impl<T> Rgb<T>
650where
651    T: Float + 'static,
652    f32: AsPrimitive<T>,
653{
654    #[inline]
655    pub fn sqrt(&self) -> Rgb<T> {
656        let zeros = 0f32.as_();
657        Rgb::new(
658            if self.r.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
659                0f32.as_()
660            } else {
661                self.r.sqrt()
662            },
663            if self.g.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
664                0f32.as_()
665            } else {
666                self.g.sqrt()
667            },
668            if self.b.partial_cmp(&zeros).unwrap_or(Ordering::Less) == Ordering::Less {
669                0f32.as_()
670            } else {
671                self.b.sqrt()
672            },
673        )
674    }
675
676    #[inline]
677    pub fn cbrt(&self) -> Rgb<T> {
678        Rgb::new(self.r.cbrt(), self.g.cbrt(), self.b.cbrt())
679    }
680}
681
682impl<T> Pow<T> for Rgb<T>
683where
684    T: Float,
685{
686    type Output = Rgb<T>;
687
688    #[inline]
689    fn pow(self, rhs: T) -> Self::Output {
690        Rgb::<T>::new(self.r.powf(rhs), self.g.powf(rhs), self.b.powf(rhs))
691    }
692}
693
694impl<T> Pow<Rgb<T>> for Rgb<T>
695where
696    T: Float,
697{
698    type Output = Rgb<T>;
699
700    #[inline]
701    fn pow(self, rhs: Rgb<T>) -> Self::Output {
702        Rgb::<T>::new(self.r.powf(rhs.r), self.g.powf(rhs.g), self.b.powf(rhs.b))
703    }
704}
705
706impl<T> Rgb<T> {
707    pub fn cast<V>(self) -> Rgb<V>
708    where
709        T: AsPrimitive<V>,
710        V: Copy + 'static,
711    {
712        Rgb::new(self.r.as_(), self.g.as_(), self.b.as_())
713    }
714}
715
716impl<T> Rgb<T>
717where
718    T: Float + 'static,
719{
720    pub fn round(self) -> Rgb<T> {
721        Rgb::new(self.r.round(), self.g.round(), self.b.round())
722    }
723}