1use std::convert::TryFrom;
2use std::fmt;
3use std::str::FromStr;
4
5#[cfg(feature = "rust-rgb")]
6use rgb::{RGB, RGBA};
7
8#[cfg(feature = "serde")]
9use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
10
11#[cfg(feature = "lab")]
12use lab::{LCh, Lab};
13
14use crate::utils::*;
15use crate::{parse, ParseColorError};
16
17#[cfg(feature = "named-colors")]
18use crate::NAMED_COLORS;
19
20#[derive(Debug, Clone, PartialEq, PartialOrd)]
21pub struct Color {
23 pub r: f32,
25 pub g: f32,
27 pub b: f32,
29 pub a: f32,
31}
32
33impl Color {
34 pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
41 Self { r, g, b, a }
42 }
43
44 pub fn to_array(&self) -> [f32; 4] {
48 [self.r, self.g, self.b, self.a]
49 }
50
51 pub fn to_rgba8(&self) -> [u8; 4] {
55 [
56 (self.r * 255.0 + 0.5) as u8,
57 (self.g * 255.0 + 0.5) as u8,
58 (self.b * 255.0 + 0.5) as u8,
59 (self.a * 255.0 + 0.5) as u8,
60 ]
61 }
62
63 pub fn to_rgba16(&self) -> [u16; 4] {
67 [
68 (self.r * 65535.0 + 0.5) as u16,
69 (self.g * 65535.0 + 0.5) as u16,
70 (self.b * 65535.0 + 0.5) as u16,
71 (self.a * 65535.0 + 0.5) as u16,
72 ]
73 }
74
75 pub fn clamp(&self) -> Self {
77 Self {
78 r: self.r.clamp(0.0, 1.0),
79 g: self.g.clamp(0.0, 1.0),
80 b: self.b.clamp(0.0, 1.0),
81 a: self.a.clamp(0.0, 1.0),
82 }
83 }
84
85 #[deprecated = "Use [new](#method.new) instead."]
86 pub fn from_rgb(r: f32, g: f32, b: f32) -> Self {
92 Self { r, g, b, a: 1.0 }
93 }
94
95 #[deprecated = "Use [new](#method.new) instead."]
96 pub fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
103 Self { r, g, b, a }
104 }
105
106 #[deprecated = "Use [from_rgba8](#method.from_rgba8) instead."]
107 pub fn from_rgb_u8(r: u8, g: u8, b: u8) -> Self {
113 Self {
114 r: r as f32 / 255.0,
115 g: g as f32 / 255.0,
116 b: b as f32 / 255.0,
117 a: 1.0,
118 }
119 }
120
121 #[deprecated = "Use [from_rgba8](#method.from_rgba8) instead."]
122 pub fn from_rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
129 Self {
130 r: r as f32 / 255.0,
131 g: g as f32 / 255.0,
132 b: b as f32 / 255.0,
133 a: a as f32 / 255.0,
134 }
135 }
136
137 pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
144 Self {
145 r: r as f32 / 255.0,
146 g: g as f32 / 255.0,
147 b: b as f32 / 255.0,
148 a: a as f32 / 255.0,
149 }
150 }
151
152 #[deprecated = "Use [from_linear_rgba](#method.from_linear_rgba) instead."]
153 pub fn from_linear_rgb(r: f32, g: f32, b: f32) -> Self {
159 Self::from_linear_rgba(r, g, b, 1.0)
160 }
161
162 pub fn from_linear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
169 fn from_linear(x: f32) -> f32 {
170 if x >= 0.0031308 {
171 return 1.055 * x.powf(1.0 / 2.4) - 0.055;
172 }
173 12.92 * x
174 }
175 Self::new(from_linear(r), from_linear(g), from_linear(b), a)
176 }
177
178 #[deprecated = "Use [from_linear_rgba8](#method.from_linear_rgba8) instead."]
179 pub fn from_linear_rgb_u8(r: u8, g: u8, b: u8) -> Self {
185 Self::from_linear_rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0)
186 }
187
188 #[deprecated = "Use [from_linear_rgba8](#method.from_linear_rgba8) instead."]
189 pub fn from_linear_rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
196 Self::from_linear_rgba(
197 r as f32 / 255.0,
198 g as f32 / 255.0,
199 b as f32 / 255.0,
200 a as f32 / 255.0,
201 )
202 }
203
204 pub fn from_linear_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
211 Self::from_linear_rgba(
212 r as f32 / 255.0,
213 g as f32 / 255.0,
214 b as f32 / 255.0,
215 a as f32 / 255.0,
216 )
217 }
218
219 #[deprecated = "Use [from_hsva](#method.from_hsva) instead."]
220 pub fn from_hsv(h: f32, s: f32, v: f32) -> Self {
226 Self::from_hsva(h, s, v, 1.0)
227 }
228
229 pub fn from_hsva(h: f32, s: f32, v: f32, a: f32) -> Self {
236 let (r, g, b) = hsv_to_rgb(normalize_angle(h), clamp0_1(s), clamp0_1(v));
237 Self::new(clamp0_1(r), clamp0_1(g), clamp0_1(b), clamp0_1(a))
238 }
239
240 #[deprecated = "Use [from_hsla](#method.from_hsla) instead."]
241 pub fn from_hsl(h: f32, s: f32, l: f32) -> Self {
247 Self::from_hsla(h, s, l, 1.0)
248 }
249
250 pub fn from_hsla(h: f32, s: f32, l: f32, a: f32) -> Self {
257 let (r, g, b) = hsl_to_rgb(normalize_angle(h), clamp0_1(s), clamp0_1(l));
258 Self::new(clamp0_1(r), clamp0_1(g), clamp0_1(b), clamp0_1(a))
259 }
260
261 #[deprecated = "Use [from_hwba](#method.from_hwba) instead."]
262 pub fn from_hwb(h: f32, w: f32, b: f32) -> Self {
268 Self::from_hwba(h, w, b, 1.0)
269 }
270
271 pub fn from_hwba(h: f32, w: f32, b: f32, a: f32) -> Self {
278 let (r, g, b) = hwb_to_rgb(normalize_angle(h), clamp0_1(w), clamp0_1(b));
279 Self::new(clamp0_1(r), clamp0_1(g), clamp0_1(b), a)
280 }
281
282 #[deprecated = "Use [from_oklaba](#method.from_oklaba) instead."]
283 pub fn from_oklab(l: f32, a: f32, b: f32) -> Self {
289 Self::from_oklaba(l, a, b, 1.0)
290 }
291
292 #[allow(clippy::excessive_precision)]
299 pub fn from_oklaba(l: f32, a: f32, b: f32, alpha: f32) -> Self {
300 let l_ = (l + 0.3963377774 * a + 0.2158037573 * b).powi(3);
301 let m_ = (l - 0.1055613458 * a - 0.0638541728 * b).powi(3);
302 let s_ = (l - 0.0894841775 * a - 1.2914855480 * b).powi(3);
303
304 let r = 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_;
305 let g = -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_;
306 let b = -0.0041960863 * l_ - 0.7034186147 * m_ + 1.7076147010 * s_;
307
308 Self::from_linear_rgba(r, g, b, alpha)
309 }
310
311 pub fn from_oklcha(l: f32, c: f32, h: f32, alpha: f32) -> Self {
318 Self::from_oklaba(l, c * h.cos(), c * h.sin(), alpha)
319 }
320
321 #[cfg(feature = "lab")]
322 pub fn from_laba(l: f32, a: f32, b: f32, alpha: f32) -> Self {
329 let [r, g, b] = Lab { l, a, b }.to_rgb_normalized();
330 Self::new(r, g, b, alpha)
331 }
332
333 #[cfg(feature = "lab")]
334 #[deprecated = "Use [from_laba](#method.from_laba) instead."]
335 pub fn from_lab(l: f32, a: f32, b: f32, alpha: f32) -> Self {
342 Self::from_laba(l, a, b, alpha)
343 }
344
345 #[cfg(feature = "lab")]
346 pub fn to_laba(&self) -> [f32; 4] {
348 let Lab { l, a, b } = Lab::from_rgb_normalized(&[self.r, self.g, self.b]);
349 [l, a, b, self.a]
350 }
351
352 #[cfg(feature = "lab")]
353 #[deprecated = "Use [to_laba](#method.to_laba) instead."]
354 pub fn to_lab(&self) -> [f32; 4] {
356 self.to_laba()
357 }
358
359 #[cfg(feature = "lab")]
360 pub fn interpolate_lab(&self, other: &Color, t: f32) -> Self {
362 let [l1, a1, b1, alpha1] = self.to_laba();
363 let [l2, a2, b2, alpha2] = other.to_laba();
364 Self::from_laba(
365 l1 + t * (l2 - l1),
366 a1 + t * (a2 - a1),
367 b1 + t * (b2 - b1),
368 alpha1 + t * (alpha2 - alpha1),
369 )
370 }
371
372 #[cfg(feature = "lab")]
373 pub fn from_lcha(l: f32, c: f32, h: f32, alpha: f32) -> Self {
380 let [r, g, b] = LCh { l, c, h }.to_lab().to_rgb_normalized();
381 Self::new(r, g, b, alpha)
382 }
383
384 #[cfg(feature = "lab")]
385 #[deprecated = "Use [from_lcha](#method.from_lcha) instead."]
386 pub fn from_lch(l: f32, c: f32, h: f32, alpha: f32) -> Self {
393 Self::from_lcha(l, c, h, alpha)
394 }
395
396 #[cfg(feature = "lab")]
397 pub fn to_lcha(&self) -> [f32; 4] {
399 let LCh { l, c, h } = LCh::from_lab(Lab::from_rgb_normalized(&[self.r, self.g, self.b]));
400 [l, c, h, self.a]
401 }
402
403 #[cfg(feature = "lab")]
404 #[deprecated = "Use [to_lcha](#method.to_lcha) instead."]
405 pub fn to_lch(&self) -> [f32; 4] {
407 self.to_lcha()
408 }
409
410 #[cfg(feature = "lab")]
411 pub fn interpolate_lch(&self, other: &Color, t: f32) -> Self {
413 let [l1, c1, h1, alpha1] = self.to_lcha();
414 let [l2, c2, h2, alpha2] = other.to_lcha();
415 Self::from_lcha(
416 l1 + t * (l2 - l1),
417 c1 + t * (c2 - c1),
418 interp_angle_rad(h1, h2, t),
419 alpha1 + t * (alpha2 - alpha1),
420 )
421 }
422
423 pub fn from_html<S: AsRef<str>>(s: S) -> Result<Self, ParseColorError> {
441 parse(s.as_ref())
442 }
443
444 #[cfg(feature = "named-colors")]
456 pub fn name(&self) -> Option<&'static str> {
457 let rgb = &self.to_rgba8()[0..3];
458 for (&k, &v) in NAMED_COLORS.entries() {
459 if v == rgb {
460 return Some(k);
461 }
462 }
463 None
464 }
465
466 #[deprecated]
467 pub fn rgba(&self) -> (f32, f32, f32, f32) {
471 (self.r, self.g, self.b, self.a)
472 }
473
474 #[deprecated = "Use [to_rgba8](#method.to_rgba8) instead."]
475 pub fn rgba_u8(&self) -> (u8, u8, u8, u8) {
479 (
480 (self.r * 255.0).round() as u8,
481 (self.g * 255.0).round() as u8,
482 (self.b * 255.0).round() as u8,
483 (self.a * 255.0).round() as u8,
484 )
485 }
486
487 pub fn to_hsva(&self) -> [f32; 4] {
494 let (h, s, v) = rgb_to_hsv(self.r, self.g, self.b);
495 [h, s, v, self.a]
496 }
497
498 pub fn to_hsla(&self) -> [f32; 4] {
505 let (h, s, l) = rgb_to_hsl(self.r, self.g, self.b);
506 [h, s, l, self.a]
507 }
508
509 pub fn to_hwba(&self) -> [f32; 4] {
516 let (h, w, b) = rgb_to_hwb(self.r, self.g, self.b);
517 [h, w, b, self.a]
518 }
519
520 pub fn to_linear_rgba(&self) -> [f32; 4] {
524 fn to_linear(x: f32) -> f32 {
525 if x >= 0.04045 {
526 return ((x + 0.055) / 1.055).powf(2.4);
527 }
528 x / 12.92
529 }
530 [
531 to_linear(self.r),
532 to_linear(self.g),
533 to_linear(self.b),
534 self.a,
535 ]
536 }
537
538 pub fn to_linear_rgba_u8(&self) -> [u8; 4] {
542 let [r, g, b, a] = self.to_linear_rgba();
543 [
544 (r * 255.0).round() as u8,
545 (g * 255.0).round() as u8,
546 (b * 255.0).round() as u8,
547 (a * 255.0).round() as u8,
548 ]
549 }
550
551 #[allow(clippy::excessive_precision)]
553 pub fn to_oklaba(&self) -> [f32; 4] {
554 let [r, g, b, _] = self.to_linear_rgba();
555 let l_ = (0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b).cbrt();
556 let m_ = (0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b).cbrt();
557 let s_ = (0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b).cbrt();
558 let l = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_;
559 let a = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_;
560 let b = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_;
561 [l, a, b, self.a]
562 }
563
564 pub fn to_hex_string(&self) -> String {
566 let [r, g, b, a] = self.to_rgba8();
567
568 if a < 255 {
569 return format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a);
570 }
571
572 format!("#{:02x}{:02x}{:02x}", r, g, b)
573 }
574
575 pub fn to_rgb_string(&self) -> String {
577 let [r, g, b, _] = self.to_rgba8();
578
579 if self.a < 1.0 {
580 return format!("rgba({},{},{},{})", r, g, b, self.a);
581 }
582
583 format!("rgb({},{},{})", r, g, b)
584 }
585
586 pub fn interpolate_rgb(&self, other: &Color, t: f32) -> Self {
588 Self {
589 r: self.r + t * (other.r - self.r),
590 g: self.g + t * (other.g - self.g),
591 b: self.b + t * (other.b - self.b),
592 a: self.a + t * (other.a - self.a),
593 }
594 }
595
596 pub fn interpolate_linear_rgb(&self, other: &Color, t: f32) -> Self {
598 let [r1, g1, b1, a1] = self.to_linear_rgba();
599 let [r2, g2, b2, a2] = other.to_linear_rgba();
600 Self::from_linear_rgba(
601 r1 + t * (r2 - r1),
602 g1 + t * (g2 - g1),
603 b1 + t * (b2 - b1),
604 a1 + t * (a2 - a1),
605 )
606 }
607
608 pub fn interpolate_hsv(&self, other: &Color, t: f32) -> Self {
610 let [h1, s1, v1, a1] = self.to_hsva();
611 let [h2, s2, v2, a2] = other.to_hsva();
612 Self::from_hsva(
613 interp_angle(h1, h2, t),
614 s1 + t * (s2 - s1),
615 v1 + t * (v2 - v1),
616 a1 + t * (a2 - a1),
617 )
618 }
619
620 pub fn interpolate_oklab(&self, other: &Color, t: f32) -> Self {
622 let [l1, a1, b1, alpha1] = self.to_oklaba();
623 let [l2, a2, b2, alpha2] = other.to_oklaba();
624 Self::from_oklaba(
625 l1 + t * (l2 - l1),
626 a1 + t * (a2 - a1),
627 b1 + t * (b2 - b1),
628 alpha1 + t * (alpha2 - alpha1),
629 )
630 }
631}
632
633impl Default for Color {
634 fn default() -> Self {
635 Self {
636 r: 0.0,
637 g: 0.0,
638 b: 0.0,
639 a: 1.0,
640 }
641 }
642}
643
644impl fmt::Display for Color {
645 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
646 write!(f, "RGBA({},{},{},{})", self.r, self.g, self.b, self.a)
647 }
648}
649
650impl FromStr for Color {
651 type Err = ParseColorError;
652
653 fn from_str(s: &str) -> Result<Self, Self::Err> {
654 parse(s)
655 }
656}
657
658impl TryFrom<&str> for Color {
659 type Error = ParseColorError;
660
661 fn try_from(s: &str) -> Result<Self, Self::Error> {
662 parse(s)
663 }
664}
665
666impl From<(f32, f32, f32, f32)> for Color {
667 fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
668 Self { r, g, b, a }
669 }
670}
671
672impl From<(f32, f32, f32)> for Color {
673 fn from((r, g, b): (f32, f32, f32)) -> Self {
674 Self { r, g, b, a: 1.0 }
675 }
676}
677
678impl From<[f32; 4]> for Color {
679 fn from([r, g, b, a]: [f32; 4]) -> Self {
680 Self { r, g, b, a }
681 }
682}
683
684impl From<[f32; 3]> for Color {
685 fn from([r, g, b]: [f32; 3]) -> Self {
686 Self { r, g, b, a: 1.0 }
687 }
688}
689
690impl From<[f64; 4]> for Color {
691 fn from([r, g, b, a]: [f64; 4]) -> Self {
692 Self {
693 r: r as f32,
694 g: g as f32,
695 b: b as f32,
696 a: a as f32,
697 }
698 }
699}
700
701impl From<[f64; 3]> for Color {
702 fn from([r, g, b]: [f64; 3]) -> Self {
703 Self {
704 r: r as f32,
705 g: g as f32,
706 b: b as f32,
707 a: 1.0,
708 }
709 }
710}
711
712impl From<(u8, u8, u8, u8)> for Color {
713 fn from((r, g, b, a): (u8, u8, u8, u8)) -> Self {
714 Self::from_rgba8(r, g, b, a)
715 }
716}
717
718impl From<(u8, u8, u8)> for Color {
719 fn from((r, g, b): (u8, u8, u8)) -> Self {
720 Self::from_rgba8(r, g, b, 255)
721 }
722}
723
724impl From<[u8; 4]> for Color {
725 fn from([r, g, b, a]: [u8; 4]) -> Self {
726 Self::from_rgba8(r, g, b, a)
727 }
728}
729
730impl From<[u8; 3]> for Color {
731 fn from([r, g, b]: [u8; 3]) -> Self {
732 Self::from_rgba8(r, g, b, 255)
733 }
734}
735
736#[cfg(feature = "rust-rgb")]
738impl From<RGB<f32>> for Color {
739 fn from(item: RGB<f32>) -> Self {
740 Self::new(item.r, item.g, item.b, 1.0)
741 }
742}
743
744#[cfg(feature = "rust-rgb")]
746impl From<RGBA<f32>> for Color {
747 fn from(item: RGBA<f32>) -> Self {
748 Self::new(item.r, item.g, item.b, item.a)
749 }
750}
751
752#[cfg(feature = "serde")]
754impl Serialize for Color {
755 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
756 serializer.serialize_str(&self.to_hex_string())
757 }
758}
759
760#[cfg(feature = "serde")]
762impl<'de> Deserialize<'de> for Color {
763 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
764 deserializer.deserialize_str(ColorVisitor)
765 }
766}
767
768#[cfg(feature = "serde")]
769struct ColorVisitor;
770
771#[cfg(feature = "serde")]
772impl Visitor<'_> for ColorVisitor {
773 type Value = Color;
774
775 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
776 f.write_str("a valid css color")
777 }
778
779 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
780 where
781 E: serde::de::Error,
782 {
783 Color::from_str(v).map_err(serde::de::Error::custom)
784 }
785}
786
787#[cfg(test)]
788mod tests {
789 use super::*;
790
791 #[cfg(feature = "rust-rgb")]
792 #[test]
793 fn test_convert_rust_rgb_to_color() {
794 let rgb = RGB::new(0.0, 0.5, 1.0);
795 assert_eq!(Color::new(0.0, 0.5, 1.0, 1.0), Color::from(rgb));
796
797 let rgba = RGBA::new(1.0, 0.5, 0.0, 0.5);
798 assert_eq!(Color::new(1.0, 0.5, 0.0, 0.5), Color::from(rgba));
799 }
800
801 #[cfg(feature = "serde")]
802 #[test]
803 fn test_serde_serialize_to_hex() {
804 let color = Color::new(1.0, 1.0, 0.5, 0.5);
805 serde_test::assert_ser_tokens(&color, &[serde_test::Token::Str("#ffff8080")]);
806 }
807
808 #[cfg(all(feature = "serde", feature = "named-colors"))]
809 #[test]
810 fn test_serde_deserialize_from_string() {
811 let named = Color::new(1.0, 1.0, 0.0, 1.0);
812 serde_test::assert_de_tokens(&named, &[serde_test::Token::Str("yellow")]);
813
814 let hex = Color::new(0.0, 1.0, 0.0, 1.0);
815 serde_test::assert_de_tokens(&hex, &[serde_test::Token::Str("#00ff00ff")]);
816
817 let rgb = Color::new(0.0, 1.0, 0.0, 1.0);
818 serde_test::assert_de_tokens(&rgb, &[serde_test::Token::Str("rgba(0,255,0,1)")]);
819 }
820}