1use 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)]
20pub struct Rgb<T> {
22 pub r: T,
24 pub g: T,
26 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 #[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 #[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 #[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 #[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 #[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 #[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}