1use crate::channel::{
6 AngularChannelScalar, ChannelCast, ChannelFormatCast, ColorChannel, PosNormalBoundedChannel,
7 PosNormalChannelScalar,
8};
9use crate::chromaticity::ChromaticityCoordinates;
10use crate::color;
11use crate::color::{Broadcast, Color, FromTuple, HomogeneousColor};
12use crate::convert;
13use crate::encoding::EncodableColor;
14use crate::hsl;
15use crate::hsv;
16use crate::hwb;
17use crate::tags::RgbTag;
18use angle;
19#[cfg(feature = "approx")]
20use approx;
21use num_traits;
22use num_traits::cast;
23use std::fmt;
24use std::mem;
25use std::slice;
26
27#[repr(C)]
28#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
29pub struct Rgb<T> {
56 red: PosNormalBoundedChannel<T>,
57 green: PosNormalBoundedChannel<T>,
58 blue: PosNormalBoundedChannel<T>,
59}
60
61impl<T> Rgb<T>
62where
63 T: PosNormalChannelScalar,
64{
65 pub fn new(red: T, green: T, blue: T) -> Self {
67 Rgb {
68 red: PosNormalBoundedChannel::new(red),
69 green: PosNormalBoundedChannel::new(green),
70 blue: PosNormalBoundedChannel::new(blue),
71 }
72 }
73
74 impl_color_color_cast_square!(
75 Rgb { red, green, blue },
76 chan_traits = { PosNormalChannelScalar }
77 );
78
79 pub fn red(&self) -> T {
81 self.red.0.clone()
82 }
83 pub fn green(&self) -> T {
85 self.green.0.clone()
86 }
87 pub fn blue(&self) -> T {
89 self.blue.0.clone()
90 }
91 pub fn red_mut(&mut self) -> &mut T {
93 &mut self.red.0
94 }
95 pub fn green_mut(&mut self) -> &mut T {
97 &mut self.green.0
98 }
99 pub fn blue_mut(&mut self) -> &mut T {
101 &mut self.blue.0
102 }
103 pub fn set_red(&mut self, val: T) {
105 self.red.0 = val;
106 }
107 pub fn set_green(&mut self, val: T) {
109 self.green.0 = val;
110 }
111 pub fn set_blue(&mut self, val: T) {
113 self.blue.0 = val;
114 }
115}
116
117impl<T> Rgb<T>
118where
119 T: PosNormalChannelScalar + num_traits::Float,
120{
121 pub fn chromaticity_coordinates(&self) -> ChromaticityCoordinates<T> {
124 let alpha = cast::<_, T>(0.5).unwrap()
125 * (cast::<_, T>(2.0).unwrap() * self.red() - self.green() - self.blue());
126
127 let beta = cast::<_, T>(3.0).unwrap().sqrt()
128 * cast::<_, T>(0.5).unwrap()
129 * (self.green() - self.blue());
130
131 ChromaticityCoordinates { alpha, beta }
132 }
133}
134
135impl<T> Color for Rgb<T>
136where
137 T: PosNormalChannelScalar,
138{
139 type Tag = RgbTag;
140 type ChannelsTuple = (T, T, T);
141
142 #[inline]
143 fn num_channels() -> u32 {
144 3
145 }
146
147 fn to_tuple(self) -> Self::ChannelsTuple {
148 (self.red.0, self.green.0, self.blue.0)
149 }
150}
151
152impl<T> FromTuple for Rgb<T>
153where
154 T: PosNormalChannelScalar,
155{
156 fn from_tuple(values: Self::ChannelsTuple) -> Self {
157 Rgb::new(values.0, values.1, values.2)
158 }
159}
160
161impl<T> HomogeneousColor for Rgb<T>
162where
163 T: PosNormalChannelScalar,
164{
165 type ChannelFormat = T;
166
167 impl_color_homogeneous_color_square!(Rgb<T> {red, green, blue});
168}
169
170impl<T> Broadcast for Rgb<T>
171where
172 T: PosNormalChannelScalar,
173{
174 impl_color_broadcast!(Rgb<T> {red, green, blue}, chan=PosNormalBoundedChannel);
175}
176
177impl<T> color::Color3 for Rgb<T> where T: PosNormalChannelScalar {}
178
179impl<T> color::Invert for Rgb<T>
180where
181 T: PosNormalChannelScalar,
182{
183 impl_color_invert!(Rgb { red, green, blue });
184}
185
186impl<T> color::Bounded for Rgb<T>
187where
188 T: PosNormalChannelScalar,
189{
190 impl_color_bounded!(Rgb { red, green, blue });
191}
192
193impl<T> color::Lerp for Rgb<T>
194where
195 T: PosNormalChannelScalar + color::Lerp,
196{
197 type Position = <T as color::Lerp>::Position;
198 impl_color_lerp_square!(Rgb { red, green, blue });
199}
200
201impl<T> color::Flatten for Rgb<T>
202where
203 T: PosNormalChannelScalar,
204{
205 impl_color_as_slice!(T);
206 impl_color_from_slice_square!(Rgb<T> {red:PosNormalBoundedChannel - 0,
207 green:PosNormalBoundedChannel - 1, blue:PosNormalBoundedChannel - 2});
208}
209
210impl<T> EncodableColor for Rgb<T> where T: PosNormalChannelScalar {}
211
212#[cfg(feature = "approx")]
213impl<T> approx::AbsDiffEq for Rgb<T>
214where
215 T: PosNormalChannelScalar + approx::AbsDiffEq,
216 T::Epsilon: Clone,
217{
218 impl_abs_diff_eq!({red, green, blue});
219}
220#[cfg(feature = "approx")]
221impl<T> approx::RelativeEq for Rgb<T>
222where
223 T: PosNormalChannelScalar + approx::RelativeEq,
224 T::Epsilon: Clone,
225{
226 impl_rel_eq!({red, green, blue});
227}
228#[cfg(feature = "approx")]
229impl<T> approx::UlpsEq for Rgb<T>
230where
231 T: PosNormalChannelScalar + approx::UlpsEq,
232 T::Epsilon: Clone,
233{
234 impl_ulps_eq!({red, green, blue});
235}
236
237impl<T> Default for Rgb<T>
238where
239 T: PosNormalChannelScalar + num_traits::Zero,
240{
241 impl_color_default!(Rgb {
242 red: PosNormalBoundedChannel,
243 green: PosNormalBoundedChannel,
244 blue: PosNormalBoundedChannel
245 });
246}
247
248impl<T> fmt::Display for Rgb<T>
249where
250 T: PosNormalChannelScalar + fmt::Display,
251{
252 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253 write!(f, "Rgb({}, {}, {})", self.red, self.green, self.blue)
254 }
255}
256
257fn get_hue_factor_and_ordered_chans<T>(color: &Rgb<T>) -> (T, T, T, T, T)
258where
259 T: PosNormalChannelScalar + num_traits::Float,
260{
261 let mut scaling_factor = T::zero();
262 let (mut c1, mut c2, mut c3) = color.clone().to_tuple();
263
264 if c2 < c3 {
265 mem::swap(&mut c2, &mut c3);
266 scaling_factor = cast(-1.0).unwrap();
267 }
268 let min_chan = if c1 < c2 {
269 mem::swap(&mut c1, &mut c2);
270 scaling_factor = cast::<_, T>(-1.0 / 3.0).unwrap() - scaling_factor;
271 c2.min(c3)
272 } else {
273 c3
274 };
275
276 (scaling_factor, c1, c2, c3, min_chan)
277}
278
279fn make_hue_from_factor_and_ordered_chans<T>(
280 c1: &T,
281 c2: &T,
282 c3: &T,
283 min_chan: &T,
284 scale_factor: &T,
285) -> T
286where
287 T: PosNormalChannelScalar + num_traits::Float,
288{
289 let epsilon = cast(1e-10).unwrap();
290 let hue_scalar =
291 *scale_factor + (*c2 - *c3) / (cast::<_, T>(6.0).unwrap() * (*c1 - *min_chan) + epsilon);
292
293 hue_scalar.abs()
294}
295
296impl<T> convert::GetChroma for Rgb<T>
297where
298 T: PosNormalChannelScalar,
299{
300 type ChromaType = T;
301 fn get_chroma(&self) -> T {
302 let (mut c1, mut c2, mut c3) = self.clone().to_tuple();
303 if c2 < c3 {
304 mem::swap(&mut c2, &mut c3);
305 }
306 if c1 < c2 {
307 mem::swap(&mut c1, &mut c3);
308 }
309 if c2 < c3 {
310 mem::swap(&mut c2, &mut c3);
311 }
312 c1 - c3
313 }
314}
315
316impl<T> convert::GetHue for Rgb<T>
317where
318 T: PosNormalChannelScalar + num_traits::Float,
319{
320 type InternalAngle = angle::Turns<T>;
321 fn get_hue<U>(&self) -> U
322 where
323 U: angle::Angle<Scalar = <Self::InternalAngle as angle::Angle>::Scalar>
324 + angle::FromAngle<angle::Turns<T>>,
325 {
326 let (scale_factor, c1, c2, c3, min_chan) = get_hue_factor_and_ordered_chans(self);
327 let hue_scalar =
328 make_hue_from_factor_and_ordered_chans(&c1, &c2, &c3, &min_chan, &scale_factor);
329
330 U::from_angle(angle::Turns(hue_scalar.abs()))
331 }
332}
333
334impl<T, A> convert::FromColor<Rgb<T>> for hsv::Hsv<T, A>
335where
336 T: PosNormalChannelScalar + num_traits::Float,
337 A: AngularChannelScalar + angle::FromAngle<angle::Turns<T>>,
338{
339 fn from_color(from: &Rgb<T>) -> Self {
340 let epsilon = cast(1e-10).unwrap();
341 let (scaling_factor, c1, c2, c3, min_chan) = get_hue_factor_and_ordered_chans(from);
342 let max_chan = c1;
343 let chroma = c1 - min_chan;
344 let hue = make_hue_from_factor_and_ordered_chans(&c1, &c2, &c3, &min_chan, &scaling_factor);
345 let value = max_chan;
346 let saturation = chroma / (value + epsilon);
347
348 hsv::Hsv::new(A::from_angle(angle::Turns(hue)), saturation, value)
349 }
350}
351
352impl<T, A> convert::FromColor<Rgb<T>> for hsl::Hsl<T, A>
353where
354 T: PosNormalChannelScalar + num_traits::Float,
355 A: AngularChannelScalar + angle::FromAngle<angle::Turns<T>>,
356{
357 fn from_color(from: &Rgb<T>) -> Self {
358 let epsilon = cast(1e-10).unwrap();
359 let (scaling_factor, c1, c2, c3, min_channel) = get_hue_factor_and_ordered_chans(from);
360 let max_channel = c1;
361 let chroma = max_channel - min_channel;
362 let hue =
363 make_hue_from_factor_and_ordered_chans(&c1, &c2, &c3, &min_channel, &scaling_factor);
364 let lightness = cast::<_, T>(0.5).unwrap() * (max_channel + min_channel);
365 let one: T = cast(1.0).unwrap();
366 let sat_denom = one - (cast::<_, T>(2.0).unwrap() * lightness - one).abs() + epsilon;
367
368 let saturation = chroma / sat_denom;
369
370 hsl::Hsl::new(A::from_angle(angle::Turns(hue)), saturation, lightness)
371 }
372}
373
374impl<T, A> convert::FromColor<Rgb<T>> for hwb::Hwb<T, A>
375where
376 T: PosNormalChannelScalar + num_traits::Float,
377 A: AngularChannelScalar + angle::FromAngle<angle::Turns<T>>,
378{
379 fn from_color(from: &Rgb<T>) -> Self {
380 let (scaling_factor, c1, c2, c3, min_channel) = get_hue_factor_and_ordered_chans(from);
381 let max_channel = c1;
382 let chroma = max_channel - min_channel;
383 let hue =
384 make_hue_from_factor_and_ordered_chans(&c1, &c2, &c3, &min_channel, &scaling_factor);
385
386 let blackness = cast::<_, T>(1.0).unwrap() - max_channel;
387 let whiteness = cast::<_, T>(1.0).unwrap() - (blackness + chroma);
388
389 hwb::Hwb::new(A::from_angle(angle::Turns(hue)), whiteness, blackness)
390 }
391}
392
393#[cfg(test)]
394mod test {
395 use super::*;
396 use crate::color::*;
397 use crate::convert::*;
398 use crate::hsl::Hsl;
399 use crate::hsv::Hsv;
400 use crate::test;
401 use angle::*;
402 use approx::*;
403
404 #[test]
405 fn test_construct() {
406 {
407 let color = Rgb::new(0u8, 0, 0);
408 assert_eq!(color.red(), 0u8);
409 assert_eq!(color.green(), 0u8);
410 assert_eq!(color.blue(), 0u8);
411
412 let c2 = color.clone();
413 assert_eq!(color, c2);
414
415 let c3 = Rgb::new(120u8, 100u8, 255u8);
416 assert_eq!(c3.red(), 120u8);
417 assert_eq!(c3.green(), 100u8);
418 assert_eq!(c3.blue(), 255u8);
419 assert_eq!(c3.as_slice(), &[120u8, 100, 255]);
420 }
421 {
422 let color: Rgb<u8> = Rgb::default();
423 assert_eq!(color.red(), 0u8);
424 assert_eq!(color.green(), 0u8);
425 assert_eq!(color.blue(), 0u8);
426 }
427 {
428 let color = Rgb::broadcast(0.5_f32);
429 assert_ulps_eq!(color, Rgb::new(0.5_f32, 0.5, 0.5));
430 }
431 {
432 let color = Rgb::from_slice(&[120u8, 240, 10]);
433 assert_eq!(color, Rgb::new(120u8, 240, 10));
434 assert_eq!(color.to_tuple(), (120u8, 240, 10));
435 }
436 {
437 let c1 = Rgb::from_tuple((0.8f32, 0.5, 0.3));
438 assert_ulps_eq!(c1, Rgb::new(0.8f32, 0.5, 0.3));
439 }
440 }
441
442 #[test]
443 fn test_lerp_int() {
444 let c1 = Rgb::new(100u8, 200u8, 0u8);
445 let c2 = Rgb::new(200u8, 0u8, 255u8);
446
447 assert_eq!(c1.lerp(&c2, 0.5_f64), Rgb::new(150u8, 100, 127));
448 assert_eq!(c1.lerp(&c2, 0.0_f64), c1);
449 assert_eq!(c1.lerp(&c2, 1.0_f64), c2);
450 }
451
452 #[test]
453 fn test_lerp_float() {
454 let c1 = Rgb::new(0.2_f32, 0.5, 1.0);
455 let c2 = Rgb::new(0.8_f32, 0.5, 0.1);
456
457 assert_ulps_eq!(c1.lerp(&c2, 0.5_f32), Rgb::new(0.5_f32, 0.5, 0.55));
458 assert_ulps_eq!(c1.lerp(&c2, 0.0_f32), Rgb::new(0.2_f32, 0.5, 1.0));
459 assert_ulps_eq!(c1.lerp(&c2, 1.0_f32), Rgb::new(0.8_f32, 0.5, 0.1));
460 }
461
462 #[test]
463 fn test_invert() {
464 let c = Rgb::new(200u8, 0, 255);
465 let c2 = Rgb::new(0.8_f32, 0.0, 0.25);
466
467 assert_eq!(c.invert(), Rgb::new(55u8, 255, 0));
468 assert_ulps_eq!(c2.invert(), Rgb::new(0.2_f32, 1.0, 0.75));
469 }
470
471 #[test]
472 fn test_chroma() {
473 let c = Rgb::new(200u8, 150, 100);
474 assert_eq!(c.get_chroma(), 100u8);
475
476 let c2 = Rgb::new(1.0_f32, 0.0, 0.25);
477 assert_ulps_eq!(c2.get_chroma(), 1.0_f32);
478
479 let c3 = Rgb::new(0.5_f32, 0.5, 0.5);
480 assert_ulps_eq!(c3.get_chroma(), 0.0_f32);
481 }
482
483 #[test]
484 fn test_hue() {
485 let c1 = Rgb::new(1.0_f32, 0.0, 0.0);
486 assert_ulps_eq!(c1.get_hue(), Deg(0.0));
487 assert_ulps_eq!(Rgb::new(0.0, 1.0_f32, 0.0).get_hue(), Deg(120.0));
488 assert_ulps_eq!(Rgb::new(0.0, 0.0_f32, 1.0).get_hue(), Deg(240.0));
489 assert_relative_eq!(Rgb::new(0.5, 0.5, 0.0).get_hue(), Deg(60.0), epsilon = 1e-6);
490 assert_relative_eq!(
491 Rgb::new(0.5, 0.0, 0.5).get_hue(),
492 Deg(300.0),
493 epsilon = 1e-6
494 );
495 }
496
497 #[test]
498 fn hsv_from_rgb() {
499 let test_data = test::build_hs_test_data();
500
501 for item in test_data.iter() {
502 let hsv: Hsv<_, Deg<_>> = Hsv::from_color(&item.rgb);
503 assert_relative_eq!(hsv, item.hsv, epsilon = 1e-3);
504 }
505
506 let c1 = Rgb::new(0.2, 0.2, 0.2);
507 assert_relative_eq!(Hsv::from_color(&c1), Hsv::new(Deg(0.0), 0.0, 0.2));
508 }
509
510 #[test]
511 fn hsl_from_rgb() {
512 let test_data = test::build_hs_test_data();
513
514 for item in test_data.iter() {
515 let hsl: Hsl<_, Deg<_>> = Hsl::from_color(&item.rgb);
516 assert_relative_eq!(hsl, item.hsl, epsilon = 1e-3);
517 }
518 }
519
520 #[test]
521 fn color_cast() {
522 let c1 = Rgb::new(0u8, 0, 0);
523 assert_eq!(c1.color_cast(), c1);
524 assert_eq!(c1.color_cast(), Rgb::new(0u16, 0, 0));
525 assert_eq!(c1.color_cast(), Rgb::new(0u32, 0, 0));
526 assert_relative_eq!(c1.color_cast(), Rgb::new(0.0f32, 0.0, 0.0));
527 assert_relative_eq!(c1.color_cast(), Rgb::new(0.0f64, 0.0, 0.0));
528
529 let c2 = Rgb::new(255u8, 127, 255);
530 assert_eq!(c2.color_cast(), c2);
531 assert_relative_eq!(
532 c2.color_cast(),
533 Rgb::new(1.0f32, 0.4980392, 1.0),
534 epsilon = 1e-6
535 );
536
537 let c3 = Rgb::new(65535u16, 0, 20000);
538 assert_eq!(c3.color_cast(), c3);
539 assert_relative_eq!(
540 c3.color_cast(),
541 Rgb::new(1.0f64, 0.0, 0.3051804),
542 epsilon = 1e-6
543 );
544 assert_eq!(c3.color_cast::<f32>().color_cast(), c3);
545
546 let c4 = Rgb::new(1.0f32, 0.25, 0.0);
547 assert_eq!(c4.color_cast(), c4);
548 assert_eq!(c4.color_cast(), Rgb::new(255u8, 63, 0));
549 assert_eq!(c4.color_cast(), Rgb::new(0xffffu16, 0x3fff, 0));
550
551 let c5 = Rgb::new(0.33f64, 0.50, 0.80);
552 assert_eq!(c5.color_cast(), c5);
553 assert_relative_eq!(
554 c5.color_cast(),
555 Rgb::new(0.33f32, 0.50, 0.80),
556 epsilon = 1e-6
557 );
558 assert_relative_eq!(c5.color_cast::<f64>().color_cast(), c5, epsilon = 1e-6);
559
560 let c6 = Rgb::new(0.60f32, 0.01, 0.99);
561 assert_eq!(c6.color_cast(), c6);
562 assert_eq!(c6.color_cast(), Rgb::new(153u8, 2, 253));
563 assert_relative_eq!(
564 c6.color_cast::<u16>()
565 .color_cast::<u32>()
566 .color_cast::<f32>()
567 .color_cast::<f64>(),
568 Rgb::new(0.60f64, 0.01, 0.99),
569 epsilon = 1e-4
570 );
571 }
572}