1use super::Mode::{Hsb, Hsl, Rgb};
43use crate::prelude::*;
44use std::{convert::TryFrom, result, str::FromStr};
45
46impl Color {
47 pub fn from_slice<T, S>(mode: ColorMode, slice: S) -> PixResult<Self>
71 where
72 T: Copy + Into<f64>,
73 S: AsRef<[T]>,
74 {
75 let slice = slice.as_ref();
76 let result = match *slice {
77 [gray] => Self::with_mode(mode, gray, gray, gray),
78 [gray, a] => Self::with_mode_alpha(mode, gray, gray, gray, a),
79 [v1, v2, v3] => Self::with_mode(mode, v1, v2, v3),
80 [v1, v2, v3, a] => Self::with_mode_alpha(mode, v1, v2, v3, a),
81 _ => return Err(PixError::InvalidColorSlice.into()),
82 };
83 Ok(result)
84 }
85
86 #[inline]
96 pub const fn from_hex(hex: u32) -> Self {
97 let [_, r, g, b] = hex.to_be_bytes();
98 Self::rgba(r, g, b, 255)
99 }
100
101 #[inline]
114 pub const fn from_hex_alpha(hex: u32) -> Self {
115 let [r, g, b, a] = hex.to_be_bytes();
116 Self::rgba(r, g, b, a)
117 }
118
119 #[inline]
129 pub const fn inverted(&self) -> Self {
130 let hex = self.as_hex();
131 Self::from_hex(0x00FF_FFFF ^ hex)
132 }
133
134 pub fn blended<A>(&self, bg: Color, alpha: A) -> Self
136 where
137 A: Into<f64>,
138 {
139 let a = alpha.into().clamp(0.0, 1.0);
140 let [v1, v2, v3, _] = convert_levels(bg.levels(), bg.mode(), self.mode());
141 let [bv1, bv2, bv3, _] = self.levels();
142
143 let blended = |bg: f64, fg: f64, alpha: f64| bg.mul_add(1.0 - alpha, fg * alpha);
144 let levels = clamp_levels([
145 blended(v1, bv1, a),
146 blended(v2, bv2, a),
147 blended(v3, bv3, a),
148 1.0,
149 ]);
150 Self {
151 mode: self.mode,
152 channels: calculate_channels(levels),
153 }
154 }
155
156 pub fn lerp<A>(&self, other: Color, amt: A) -> Self
174 where
175 A: Into<f64>,
176 {
177 let lerp = |start: f64, stop: f64, amt: f64| amt.mul_add(stop - start, start);
178
179 let amt = amt.into().clamp(0.0, 1.0);
180 let [v1, v2, v3, a] = self.levels();
181 let [ov1, ov2, ov3, oa] = convert_levels(other.levels(), other.mode(), self.mode());
182 let levels = clamp_levels([
183 lerp(v1, ov1, amt),
184 lerp(v2, ov2, amt),
185 lerp(v3, ov3, amt),
186 lerp(a, oa, amt),
187 ]);
188 Self {
189 mode: self.mode,
190 channels: calculate_channels(levels),
191 }
192 }
193}
194
195impl FromStr for Color {
196 type Err = PixError;
197
198 fn from_str(string: &str) -> result::Result<Self, Self::Err> {
222 if !string.starts_with('#') {
223 return Err(PixError::ParseColorError);
224 }
225
226 let mut channels: [u8; 4] = [0, 0, 0, 255];
227 let parse_hex =
228 |hex: &str| u8::from_str_radix(hex, 16).map_err(|_| PixError::ParseColorError);
229
230 let string = string.trim().to_lowercase();
231 match string.len() - 1 {
232 3 | 4 => {
233 for (i, _) in string[1..].char_indices() {
234 let hex = parse_hex(&string[i + 1..i + 2])?;
235 channels[i] = (hex << 4) | hex;
236 }
237 }
238 6 | 8 => {
239 for (i, _) in string[1..].char_indices().step_by(2) {
240 channels[i / 2] = parse_hex(&string[i + 1..i + 3])?;
241 }
242 }
243 _ => return Err(PixError::ParseColorError),
244 }
245
246 let [r, g, b, a] = channels;
247 Ok(Self::rgba(r, g, b, a))
248 }
249}
250
251impl TryFrom<&str> for Color {
252 type Error = PixError;
253 fn try_from(s: &str) -> result::Result<Self, Self::Error> {
255 Self::from_str(s)
256 }
257}
258
259pub(crate) const fn maxes(mode: ColorMode) -> [f64; 4] {
261 match mode {
262 Rgb => [255.0; 4],
263 Hsb | Hsl => [360.0, 100.0, 100.0, 1.0],
264 }
265}
266
267pub(crate) fn clamp_levels(levels: [f64; 4]) -> [f64; 4] {
269 [
270 levels[0].clamp(0.0, 1.0),
271 levels[1].clamp(0.0, 1.0),
272 levels[2].clamp(0.0, 1.0),
273 levels[3].clamp(0.0, 1.0),
274 ]
275}
276
277pub(crate) fn convert_levels(levels: [f64; 4], from: ColorMode, to: ColorMode) -> [f64; 4] {
279 match (from, to) {
280 (Hsb, Rgb) => hsb_to_rgb(levels),
281 (Hsl, Rgb) => hsl_to_rgb(levels),
282 (Rgb, Hsb) => rgb_to_hsb(levels),
283 (Rgb, Hsl) => rgb_to_hsl(levels),
284 (Hsb, Hsl) => hsb_to_hsl(levels),
285 (Hsl, Hsb) => hsl_to_hsb(levels),
286 (_, _) => levels,
287 }
288}
289
290#[allow(clippy::many_single_char_names)]
292pub(crate) fn rgb_to_hsb([r, g, b, a]: [f64; 4]) -> [f64; 4] {
293 let c_max = r.max(g).max(b);
294 let c_min = r.min(g).min(b);
295 let chr = c_max - c_min;
296 if chr.abs() < f64::EPSILON {
297 [0.0, 0.0, c_max, a]
298 } else {
299 let mut h = if (r - c_max).abs() < f64::EPSILON {
300 (g - b) / chr
302 } else if (g - c_max).abs() < f64::EPSILON {
303 2.0 + (b - r) / chr
305 } else {
306 4.0 + (r - g) / chr
308 };
309 if h < 0.0 {
310 h += 6.0;
311 } else if h >= 6.0 {
312 h -= 6.0;
313 }
314 let s = chr / c_max;
315 [h / 6.0, s, c_max, a]
316 }
317}
318
319#[allow(clippy::many_single_char_names)]
321pub(crate) fn rgb_to_hsl([r, g, b, a]: [f64; 4]) -> [f64; 4] {
322 let c_max = r.max(g).max(b);
323 let c_min = r.min(g).min(b);
324 let l = c_max + c_min;
325 let chr = c_max - c_min;
326 if chr.abs() < f64::EPSILON {
327 [0.0, 0.0, l / 2.0, a]
328 } else {
329 let mut h = if (r - c_max).abs() < f64::EPSILON {
330 (g - b) / chr
332 } else if (g - c_max).abs() < f64::EPSILON {
333 2.0 + (b - r) / chr
335 } else {
336 4.0 + (r - g) / chr
338 };
339 if h < 0.0 {
340 h += 6.0;
341 } else if h >= 6.0 {
342 h -= 6.0;
343 }
344 let s = if l < 1.0 { chr / l } else { chr / (2.0 - l) };
345 [h / 6.0, s, l / 2.0, a]
346 }
347}
348
349#[allow(clippy::many_single_char_names)]
351pub(crate) fn hsb_to_rgb([h, s, b, a]: [f64; 4]) -> [f64; 4] {
352 if b.abs() < f64::EPSILON {
353 [0.0, 0.0, 0.0, a]
354 } else if s.abs() < f64::EPSILON {
355 [b, b, b, a]
356 } else {
357 let h = h * 6.0;
358 let sector = h.floor().clamp(0.0, 2160.0) as usize;
359 let tint1 = b * (1.0 - s);
360 let tint2 = b * (1.0 - s * (h - sector as f64));
361 let tint3 = b * (1.0 - s * (1.0 + sector as f64 - h));
362 let (r, g, b) = match sector {
363 1 => (tint2, b, tint1),
365 2 => (tint1, b, tint3),
367 3 => (tint1, tint2, b),
369 4 => (tint3, tint1, b),
371 5 => (b, tint1, tint2),
373 _ => (b, tint3, tint1),
375 };
376 [r, g, b, a]
377 }
378}
379
380#[allow(clippy::many_single_char_names)]
382pub(crate) fn hsl_to_rgb([h, s, l, a]: [f64; 4]) -> [f64; 4] {
383 if s.abs() < f64::EPSILON {
384 [l, l, l, a]
385 } else {
386 let h = h * 6.0;
387 let b = if l < 0.5 {
388 (1.0 + s) * l
389 } else {
390 l + s - l * s
391 };
392 let zest = 2.0 * l - b;
393 let hzb_to_rgb = |h: f64, z: f64, b: f64| -> f64 {
394 let h = if h < 0.0 {
395 h + 6.0
396 } else if h >= 6.0 {
397 h - 6.0
398 } else {
399 h
400 };
401 match h {
402 _ if h < 1.0 => (b - z).mul_add(h, z),
404 _ if h < 3.0 => b,
406 _ if h < 4.0 => (b - z).mul_add(4.0 - h, z),
408 _ => z,
410 }
411 };
412 [
413 hzb_to_rgb(h + 2.0, zest, b),
414 hzb_to_rgb(h, zest, b),
415 hzb_to_rgb(h - 2.0, zest, b),
416 a,
417 ]
418 }
419}
420
421#[allow(clippy::many_single_char_names)]
423pub(crate) fn hsl_to_hsb([h, s, l, a]: [f64; 4]) -> [f64; 4] {
424 let b = if l < 0.5 {
425 (1.0 + s) * l
426 } else {
427 l + s - l * s
428 };
429 let s = 2.0 * (b - l) / b;
430 [h, s, b, a]
431}
432
433#[allow(clippy::many_single_char_names)]
435pub(crate) fn hsb_to_hsl([h, s, b, a]: [f64; 4]) -> [f64; 4] {
436 let l = (2.0 - s) * b / 2.0;
437 let s = match l {
438 _ if (l - 1.0).abs() < f64::EPSILON => 0.0,
439 _ if l < 0.5 => s / 2.0 - s,
440 _ => s * b / (2.0 - l * 2.0),
441 };
442 [h, s, l, a]
443}
444
445pub(crate) fn calculate_channels(levels: [f64; 4]) -> [u8; 4] {
447 let [r, g, b, a] = levels;
448 let [r_max, g_max, b_max, a_max] = maxes(Rgb);
449 [
450 (r * r_max).round().clamp(0.0, 255.0) as u8,
451 (g * g_max).round().clamp(0.0, 255.0) as u8,
452 (b * b_max).round().clamp(0.0, 255.0) as u8,
453 (a * a_max).round().clamp(0.0, 255.0) as u8,
454 ]
455}
456
457impl Color {
458 pub(crate) fn update_channels(&mut self, levels: [f64; 4], mode: ColorMode) {
460 let levels = convert_levels(levels, mode, Rgb);
461 self.channels = calculate_channels(levels);
462 }
463}
464
465macro_rules! impl_from {
466 ($($source: ty),*) => {
467 $(
468 impl From<$source> for Color {
469 #[doc = concat!("Convert [", stringify!($source), "] to grayscale `Color`")]
470 fn from(gray: $source) -> Self {
471 let gray = f64::from(gray);
472 Self::with_mode(Rgb, gray, gray, gray)
473 }
474 }
475
476 impl From<[$source; 1]> for Color {
477 #[doc = concat!("Convert [", stringify!($source), "] to grayscale `Color`")]
478 fn from([gray]: [$source; 1]) -> Self {
479 let gray = f64::from(gray);
480 Self::with_mode(Rgb, gray, gray, gray)
481 }
482 }
483
484 impl From<[$source; 2]> for Color {
485 #[doc = concat!("Convert `[", stringify!($source), "; 2]` to grayscale `Color` with alpha")]
486 fn from([gray, alpha]: [$source; 2]) -> Self {
487 let gray = f64::from(gray);
488 let alpha = f64::from(alpha);
489 Self::with_mode_alpha(Rgb, gray, gray, gray, alpha)
490 }
491 }
492
493 impl From<[$source; 3]> for Color {
494 #[doc = concat!("Convert `[", stringify!($source), "; 3]` to `Color` with max alpha")]
495 fn from([r, g, b]: [$source; 3]) -> Self {
496 Self::with_mode(Rgb, f64::from(r), f64::from(g), f64::from(b))
497 }
498 }
499
500 impl From<[$source; 4]> for Color {
501 #[doc = concat!("Convert `[", stringify!($source), "; 4]` to `Color`")]
502 fn from([r, g, b, a]: [$source; 4]) -> Self {
503 Self::with_mode_alpha(Rgb, f64::from(r), f64::from(g), f64::from(b), f64::from(a))
504 }
505 }
506 )*
507 };
508}
509
510impl_from!(i8, u8, i16, u16, f32);
511impl_from!(i32, u32, f64);
512
513#[cfg(test)]
514mod tests {
515 use crate::prelude::{hsb, hsl, rgb, Color};
516
517 macro_rules! assert_color_eq {
518 ($c1:expr, $c2:expr) => {
519 assert_eq!($c1.channels(), $c2.channels());
520 };
521 }
522
523 #[test]
524 fn test_slice_conversions() {
525 let _: Color = 50u8.into();
526 let _: Color = 50i8.into();
527 let _: Color = 50u16.into();
528 let _: Color = 50i16.into();
529 let _: Color = 50u32.into();
530 let _: Color = 50i32.into();
531 let _: Color = 50.0f32.into();
532 let _: Color = 50.0f64.into();
533
534 let _: Color = [50u8].into();
535 let _: Color = [50i8].into();
536 let _: Color = [50u16].into();
537 let _: Color = [50i16].into();
538 let _: Color = [50u32].into();
539 let _: Color = [50i32].into();
540 let _: Color = [50.0f32].into();
541 let _: Color = [50.0f64].into();
542
543 let _: Color = [50u8, 100].into();
544 let _: Color = [50i8, 100].into();
545 let _: Color = [50u16, 100].into();
546 let _: Color = [50i16, 100].into();
547 let _: Color = [50u32, 100].into();
548 let _: Color = [50i32, 100].into();
549 let _: Color = [50.0f32, 100.0].into();
550 let _: Color = [50.0f64, 100.0].into();
551
552 let _: Color = [50u8, 100, 55].into();
553 let _: Color = [50i8, 100, 55].into();
554 let _: Color = [50u16, 100, 55].into();
555 let _: Color = [50i16, 100, 55].into();
556 let _: Color = [50u32, 100, 55].into();
557 let _: Color = [50i32, 100, 55].into();
558 let _: Color = [50.0f32, 100.0, 55.0].into();
559 let _: Color = [50.0f64, 100.0, 55.0].into();
560
561 let _: Color = [50u8, 100, 55, 100].into();
562 let _: Color = [50i8, 100, 55, 100].into();
563 let _: Color = [50u16, 100, 55, 100].into();
564 let _: Color = [50i16, 100, 55, 100].into();
565 let _: Color = [50u32, 100, 55, 100].into();
566 let _: Color = [50i32, 100, 55, 100].into();
567 let _: Color = [50.0f32, 100.0, 55.0, 100.0].into();
568 let _: Color = [50.0f64, 100.0, 55.0, 100.0].into();
569 }
570
571 #[test]
572 fn test_hsb_to_rgb() {
573 assert_color_eq!(hsb!(0.0, 0.0, 0.0), rgb!(0, 0, 0));
574 assert_color_eq!(hsb!(0.0, 0.0, 100.0), rgb!(255, 255, 255));
575 assert_color_eq!(hsb!(0.0, 100.0, 100.0), rgb!(255, 0, 0));
576 assert_color_eq!(hsb!(120.0, 100.0, 100.0), rgb!(0, 255, 0));
577 assert_color_eq!(hsb!(240.0, 100.0, 100.0), rgb!(0, 0, 255));
578 assert_color_eq!(hsb!(60.0, 100.0, 100.0), rgb!(255, 255, 0));
579 assert_color_eq!(hsb!(180.0, 100.0, 100.0), rgb!(0, 255, 255));
580 assert_color_eq!(hsb!(300.0, 100.0, 100.0), rgb!(255, 0, 255));
581 assert_color_eq!(hsb!(0.0, 0.0, 75.0), rgb!(191, 191, 191));
582 assert_color_eq!(hsb!(0.0, 0.0, 50.0), rgb!(128, 128, 128));
583 assert_color_eq!(hsb!(0.0, 100.0, 50.0), rgb!(128, 0, 0));
584 assert_color_eq!(hsb!(60.0, 100.0, 50.0), rgb!(128, 128, 0));
585 assert_color_eq!(hsb!(120.0, 100.0, 50.0), rgb!(0, 128, 0));
586 assert_color_eq!(hsb!(300.0, 100.0, 50.0), rgb!(128, 0, 128));
587 assert_color_eq!(hsb!(180.0, 100.0, 50.0), rgb!(0, 128, 128));
588 assert_color_eq!(hsb!(240.0, 100.0, 50.0), rgb!(0, 0, 128));
589 }
590
591 #[test]
592 fn test_hsb_to_hsl() {
593 assert_color_eq!(hsb!(0.0, 0.0, 0.0), hsl!(0.0, 0.0, 0.0));
594 assert_color_eq!(hsb!(0.0, 0.0, 100.0), hsl!(0.0, 0.0, 100.0));
595 assert_color_eq!(hsb!(0.0, 100.0, 100.0), hsl!(0.0, 100.0, 50.0));
596 assert_color_eq!(hsb!(120.0, 100.0, 100.0), hsl!(120.0, 100.0, 50.0));
597 assert_color_eq!(hsb!(240.0, 100.0, 100.0), hsl!(240.0, 100.0, 50.0));
598 assert_color_eq!(hsb!(60.0, 100.0, 100.0), hsl!(60.0, 100.0, 50.0));
599 assert_color_eq!(hsb!(180.0, 100.0, 100.0), hsl!(180.0, 100.0, 50.0));
600 assert_color_eq!(hsb!(300.0, 100.0, 100.0), hsl!(300.0, 100.0, 50.0));
601 assert_color_eq!(hsb!(0.0, 0.0, 75.0), hsl!(0.0, 0.0, 75.0));
602 assert_color_eq!(hsb!(0.0, 0.0, 50.0), hsl!(0.0, 0.0, 50.0));
603 assert_color_eq!(hsb!(0.0, 100.0, 50.0), hsl!(0.0, 100.0, 25.0));
604 assert_color_eq!(hsb!(60.0, 100.0, 50.0), hsl!(60.0, 100.0, 25.0));
605 assert_color_eq!(hsb!(120.0, 100.0, 50.0), hsl!(120.0, 100.0, 25.0));
606 assert_color_eq!(hsb!(300.0, 100.0, 50.0), hsl!(300.0, 100.0, 25.0));
607 assert_color_eq!(hsb!(180.0, 100.0, 50.0), hsl!(180.0, 100.0, 25.0));
608 assert_color_eq!(hsb!(240.0, 100.0, 50.0), hsl!(240.0, 100.0, 25.0));
609 }
610
611 #[test]
612 fn test_hsl_to_rgb() {
613 assert_color_eq!(hsl!(0.0, 0.0, 0.0), rgb!(0, 0, 0));
614 assert_color_eq!(hsl!(0.0, 0.0, 100.0), rgb!(255, 255, 255));
615 assert_color_eq!(hsl!(0.0, 100.0, 100.0), rgb!(255, 255, 255));
616 assert_color_eq!(hsl!(120.0, 100.0, 100.0), rgb!(255, 255, 255));
617 assert_color_eq!(hsl!(240.0, 100.0, 100.0), rgb!(255, 255, 255));
618 assert_color_eq!(hsl!(60.0, 100.0, 100.0), rgb!(255, 255, 255));
619 assert_color_eq!(hsl!(180.0, 100.0, 100.0), rgb!(255, 255, 255));
620 assert_color_eq!(hsl!(300.0, 100.0, 100.0), rgb!(255, 255, 255));
621 assert_color_eq!(hsl!(0.0, 0.0, 75.0), rgb!(191, 191, 191));
622 assert_color_eq!(hsl!(0.0, 0.0, 50.0), rgb!(128, 128, 128));
623 assert_color_eq!(hsl!(0.0, 100.0, 50.0), rgb!(255, 0, 0));
624 assert_color_eq!(hsl!(60.0, 100.0, 50.0), rgb!(255, 255, 0));
625 assert_color_eq!(hsl!(120.0, 100.0, 50.0), rgb!(0, 255, 0));
626 assert_color_eq!(hsl!(300.0, 100.0, 50.0), rgb!(255, 0, 255));
627 assert_color_eq!(hsl!(180.0, 100.0, 50.0), rgb!(0, 255, 255));
628 assert_color_eq!(hsl!(240.0, 100.0, 50.0), rgb!(0, 0, 255));
629 }
630
631 #[test]
632 fn test_hsl_to_hsb() {
633 assert_color_eq!(hsl!(0.0, 0.0, 0.0), hsb!(0.0, 0.0, 0.0));
634 assert_color_eq!(hsl!(0.0, 0.0, 100.0), hsb!(0.0, 0.0, 100.0));
635 assert_color_eq!(hsl!(0.0, 100.0, 100.0), hsb!(0.0, 0.0, 100.0));
636 assert_color_eq!(hsl!(120.0, 100.0, 100.0), hsb!(120.0, 0.0, 100.0));
637 assert_color_eq!(hsl!(240.0, 100.0, 100.0), hsb!(240.0, 0.0, 100.0));
638 assert_color_eq!(hsl!(60.0, 100.0, 100.0), hsb!(60.0, 0.0, 100.0));
639 assert_color_eq!(hsl!(180.0, 100.0, 100.0), hsb!(180.0, 0.0, 100.0));
640 assert_color_eq!(hsl!(300.0, 100.0, 100.0), hsb!(300.0, 0.0, 100.0));
641 assert_color_eq!(hsl!(0.0, 0.0, 75.0), hsb!(0.0, 0.0, 75.0));
642 assert_color_eq!(hsl!(0.0, 0.0, 50.0), hsb!(0.0, 0.0, 50.0));
643 assert_color_eq!(hsl!(0.0, 100.0, 50.0), hsb!(0.0, 100.0, 100.0));
644 assert_color_eq!(hsl!(60.0, 100.0, 50.0), hsb!(60.0, 100.0, 100.0));
645 assert_color_eq!(hsl!(120.0, 100.0, 50.0), hsb!(120.0, 100.0, 100.0));
646 assert_color_eq!(hsl!(300.0, 100.0, 50.0), hsb!(300.0, 100.0, 100.0));
647 assert_color_eq!(hsl!(180.0, 100.0, 50.0), hsb!(180.0, 100.0, 100.0));
648 assert_color_eq!(hsl!(240.0, 100.0, 50.0), hsb!(240.0, 100.0, 100.0));
649 }
650
651 #[test]
652 fn test_rgb_to_hsb() {
653 assert_color_eq!(rgb!(0, 0, 0), hsb!(0.0, 0.0, 0.0));
654 assert_color_eq!(rgb!(255, 255, 255), hsb!(0.0, 0.0, 100.0));
655 assert_color_eq!(rgb!(255, 0, 0), hsb!(0.0, 100.0, 100.0));
656 assert_color_eq!(rgb!(0, 255, 0), hsb!(120.0, 100.0, 100.0));
657 assert_color_eq!(rgb!(0, 0, 255), hsb!(240.0, 100.0, 100.0));
658 assert_color_eq!(rgb!(255, 255, 0), hsb!(60.0, 100.0, 100.0));
659 assert_color_eq!(rgb!(0, 255, 255), hsb!(180.0, 100.0, 100.0));
660 assert_color_eq!(rgb!(255, 0, 255), hsb!(300.0, 100.0, 100.0));
661 assert_color_eq!(rgb!(191, 191, 191), hsb!(0.0, 0.0, 75.0));
662 assert_color_eq!(rgb!(128, 128, 128), hsb!(0.0, 0.0, 50.0));
663 assert_color_eq!(rgb!(128, 0, 0), hsb!(0.0, 100.0, 50.0));
664 assert_color_eq!(rgb!(128, 128, 0), hsb!(60.0, 100.0, 50.0));
665 assert_color_eq!(rgb!(0, 128, 0), hsb!(120.0, 100.0, 50.0));
666 assert_color_eq!(rgb!(128, 0, 128), hsb!(300.0, 100.0, 50.0));
667 assert_color_eq!(rgb!(0, 128, 128), hsb!(180.0, 100.0, 50.0));
668 assert_color_eq!(rgb!(0, 0, 128), hsb!(240.0, 100.0, 50.0));
669 }
670
671 #[test]
672 fn test_rgb_to_hsl() {
673 assert_color_eq!(rgb!(0, 0, 0), hsl!(0.0, 0.0, 0.0));
674 assert_color_eq!(rgb!(255, 255, 255), hsl!(0.0, 0.0, 100.0));
675 assert_color_eq!(rgb!(255, 0, 0), hsl!(0.0, 100.0, 50.0));
676 assert_color_eq!(rgb!(0, 255, 0), hsl!(120.0, 100.0, 50.0));
677 assert_color_eq!(rgb!(0, 0, 255), hsl!(240.0, 100.0, 50.0));
678 assert_color_eq!(rgb!(255, 255, 0), hsl!(60.0, 100.0, 50.0));
679 assert_color_eq!(rgb!(0, 255, 255), hsl!(180.0, 100.0, 50.0));
680 assert_color_eq!(rgb!(255, 0, 255), hsl!(300.0, 100.0, 50.0));
681 assert_color_eq!(rgb!(191, 191, 191), hsl!(0.0, 0.0, 75.0));
682 assert_color_eq!(rgb!(128, 128, 128), hsl!(0.0, 0.0, 50.0));
683 assert_color_eq!(rgb!(128, 0, 0), hsl!(0.0, 100.0, 25.0));
684 assert_color_eq!(rgb!(128, 128, 0), hsl!(60.0, 100.0, 25.0));
685 assert_color_eq!(rgb!(0, 128, 0), hsl!(120.0, 100.0, 25.0));
686 assert_color_eq!(rgb!(128, 0, 128), hsl!(300.0, 100.0, 25.0));
687 assert_color_eq!(rgb!(0, 128, 128), hsl!(180.0, 100.0, 25.0));
688 assert_color_eq!(rgb!(0, 0, 128), hsl!(240.0, 100.0, 25.0));
689 }
690}