1use 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)]
25pub struct Rgb<T> {
27 pub r: T,
29 pub g: T,
31 pub b: T,
33}
34
35impl Rgb<u8> {
36 #[inline]
41 pub fn to_linear(&self, transfer_function: TransferFunction) -> Rgb<f32> {
42 self.to_rgb_f32().linearize(transfer_function)
43 }
44
45 #[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 #[inline]
61 pub fn to_jzazbz(&self, transfer_function: TransferFunction) -> Jzazbz {
62 Jzazbz::from_rgb(*self, transfer_function)
63 }
64
65 #[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 #[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 #[inline]
90 pub fn to_hsl(&self) -> Hsl {
91 Hsl::from_rgb(*self)
92 }
93
94 #[inline]
96 pub fn to_hsv(&self) -> Hsv {
97 Hsv::from(*self)
98 }
99
100 #[inline]
102 pub fn to_lab(&self) -> Lab {
103 Lab::from_rgb(*self)
104 }
105
106 #[inline]
108 pub fn to_luv(&self) -> Luv {
109 Luv::from_rgb(*self)
110 }
111
112 #[inline]
114 pub fn to_lch(&self) -> LCh {
115 LCh::from_rgb(*self)
116 }
117
118 #[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 #[inline]
134 pub fn to_oklab(&self, transfer_function: TransferFunction) -> Oklab {
135 Oklab::from_rgb(*self, transfer_function)
136 }
137
138 #[inline]
143 pub fn to_oklch(&self, transfer_function: TransferFunction) -> Oklch {
144 Oklch::from_rgb(*self, transfer_function)
145 }
146
147 #[inline]
149 pub fn to_sigmoidal(&self) -> Sigmoidal {
150 Sigmoidal::from_rgb(*self)
151 }
152
153 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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}