1use crate::*;
2
3#[cfg(feature = "crossterm")]
4use crossterm::style::Color as CC;
5
6#[derive(Clone, Copy, Debug)]
8pub enum Color {
9 Ansi(AnsiColor),
10 Hsl(Hsl),
11 Rgb(Rgb),
12}
13
14impl Color {
15 pub fn ansi(self) -> AnsiColor {
16 match self {
17 Self::Ansi(ansi) => ansi,
18 Self::Hsl(hsl) => hsl.to_ansi(),
19 Self::Rgb(rgb) => rgb.to_ansi(),
20 }
21 }
22 pub fn hsl(self) -> Hsl {
23 match self {
24 Self::Ansi(ansi) => ansi.to_hsl(),
25 Self::Hsl(hsl) => hsl,
26 Self::Rgb(rgb) => rgb.to_hsl(),
27 }
28 }
29 pub fn rgb(self) -> Rgb {
30 match self {
31 Self::Ansi(ansi) => ansi.to_rgb(),
32 Self::Hsl(hsl) => hsl.to_rgb(),
33 Self::Rgb(rgb) => rgb,
34 }
35 }
36 pub fn luma(self) -> f32 {
37 self.rgb().luma()
38 }
39 pub fn blend<C1: Into<Color>, C2: Into<Color>>(c1: C1, w1: f32, c2: C2, w2: f32) -> Self {
41 let c1: Color = c1.into();
42 let c2: Color = c2.into();
43 debug_assert!(w1 + w2 > 0.0);
44 let hsl1: Hsl = c1.hsl();
45 let hsl2: Hsl = c2.hsl();
46 let mixed_hsl = Hsl::mix(hsl1, w1, hsl2, w2);
47 let rgb1: Rgb = hsl1.to_rgb();
48 let rgb2: Rgb = hsl2.to_rgb();
49 let mixed_rgb = Rgb::mix(rgb1, w1, rgb2, w2);
50 let mixed_rgb_hsl = mixed_rgb.to_hsl();
51 let mixed = Hsl::mix(mixed_hsl, 0.5, mixed_rgb_hsl, 0.5);
52 Hsl {
53 h: mixed_rgb_hsl.h, s: mixed.s, l: mixed.l, }
57 .into()
58 }
59}
60
61impl From<AnsiColor> for Color {
62 fn from(ansi: AnsiColor) -> Self {
63 Self::Ansi(ansi)
64 }
65}
66impl From<Rgb> for Color {
67 fn from(rgb: Rgb) -> Self {
68 Self::Rgb(rgb)
69 }
70}
71impl From<Hsl> for Color {
72 fn from(rgb: Hsl) -> Self {
73 Self::Hsl(rgb)
74 }
75}
76impl From<u8> for Color {
77 fn from(code: u8) -> Self {
78 Self::Ansi(AnsiColor::new(code))
79 }
80}
81
82#[cfg(feature = "crossterm")]
83impl From<CC> for Color {
84 fn from(cc: CC) -> Self {
85 match cc {
86 CC::Reset => 0.into(),
87 CC::Black => 0.into(),
88 CC::DarkGrey => 8.into(),
89 CC::Red => 9.into(),
90 CC::DarkRed => 1.into(),
91 CC::Green => 10.into(),
92 CC::DarkGreen => 2.into(),
93 CC::Yellow => 11.into(),
94 CC::DarkYellow => 3.into(),
95 CC::Blue => 12.into(),
96 CC::DarkBlue => 4.into(),
97 CC::Magenta => 13.into(),
98 CC::DarkMagenta => 5.into(),
99 CC::Cyan => 14.into(),
100 CC::DarkCyan => 6.into(),
101 CC::White => 15.into(),
102 CC::Grey => 7.into(),
103 CC::Rgb { r, g, b } => Color::Rgb(Rgb { r, g, b }),
104 CC::AnsiValue(code) => code.into(),
105 }
106 }
107}
108
109#[cfg(feature = "crossterm")]
110impl Into<CC> for Color {
111 fn into(self) -> CC {
112 match self {
113 Self::Ansi(AnsiColor { code }) => CC::AnsiValue(code),
114 Self::Rgb(Rgb { r, g, b }) => CC::Rgb { r, g, b },
115 Self::Hsl(hsl) => {
116 let Rgb { r, g, b } = hsl.to_rgb();
117 CC::Rgb { r, g, b }
118 }
119 }
120 }
121}
122
123#[test]
125fn test_ansi_to_rgb_to_ansi() {
126 for code in 16..=255 {
129 let c1 = AnsiColor { code };
130 let c2 = c1.to_rgb();
131 let c3 = c2.to_ansi();
132 assert_eq!(c1, c3);
133 }
134}
135#[test]
137fn test_ansi_to_hsl_to_ansi() {
138 for code in 16..=255 {
141 let c1 = AnsiColor { code };
142 let c2 = c1.to_hsl();
143 let c3 = c2.to_ansi();
144 assert_eq!(c1, c3);
145 }
146}
147#[test]
148fn test_rgb_to_hsl() {
149 assert!(Rgb::new(255, 0, 0).to_hsl().near(Hsl::new(0.0, 1.0, 0.5))); assert!(Rgb::new(255, 255, 0)
151 .to_hsl()
152 .near(Hsl::new(60.0, 1.0, 0.5))); assert!(Rgb::new(255, 255, 255)
154 .to_hsl()
155 .near(Hsl::new(0.0, 0.0, 1.0))); }
157#[test]
159fn test_hsl_to_rgb_to_hsl() {
160 let red = Hsl::new(0.0, 1.0, 0.5);
161 let yellow = Hsl::new(60.0, 1.0, 0.5);
162 let white = Hsl::new(0.0, 0.0, 1.0);
163 assert!(red.to_rgb().to_hsl().near(red));
164 assert!(yellow.to_rgb().to_hsl().near(yellow));
165 assert!(white.to_rgb().to_hsl().near(white));
166}