1use super::palette::Palette;
2use std::marker::PhantomData;
4
5pub trait Color {
7 fn rgb(&self) -> (u8, u8, u8);
9
10 fn alpha(&self) -> f64;
12}
13
14pub trait Mixable: Color {
16 fn mix(&self, alpha: f64) -> CompsitableColor<Self> {
18 CompsitableColor(self, alpha)
19 }
20}
21
22impl<T: Color + Sized> Mixable for T {}
23
24impl Color for Box<&dyn Color> {
25 fn rgb(&self) -> (u8, u8, u8) {
26 self.as_ref().rgb()
27 }
28
29 fn alpha(&self) -> f64 {
30 self.as_ref().alpha()
31 }
32}
33
34pub trait SimpleColor {
36 fn rgb(&self) -> (u8, u8, u8);
37}
38
39impl<T: SimpleColor> Color for T {
40 fn rgb(&self) -> (u8, u8, u8) {
41 SimpleColor::rgb(self)
42 }
43
44 fn alpha(&self) -> f64 {
45 1.0
46 }
47}
48
49pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
51
52impl<P: Palette> PaletteColor<P> {
53 pub fn pick(idx: usize) -> PaletteColor<P> {
55 PaletteColor(idx % P::COLORS.len(), PhantomData)
56 }
57}
58
59impl<P: Palette> SimpleColor for PaletteColor<P> {
60 fn rgb(&self) -> (u8, u8, u8) {
61 P::COLORS[self.0]
62 }
63}
64
65pub struct CompsitableColor<'a, T: Color + ?Sized>(&'a T, f64);
67
68impl<'a, T: Color> Color for CompsitableColor<'a, T> {
69 fn rgb(&self) -> (u8, u8, u8) {
70 (self.0).rgb()
71 }
72
73 fn alpha(&self) -> f64 {
74 (self.0).alpha() * self.1
75 }
76}
77
78pub struct RGBColor(pub u8, pub u8, pub u8);
80
81impl SimpleColor for RGBColor {
82 fn rgb(&self) -> (u8, u8, u8) {
83 (self.0, self.1, self.2)
84 }
85}
86
87macro_rules! predefined_color {
88 ($name:ident, $r:expr, $g:expr, $b:expr, $doc: expr) => {
89 #[doc = $doc]
90 pub struct $name;
91 impl SimpleColor for $name {
92 fn rgb(&self) -> (u8,u8,u8) {
93 return ($r, $g, $b);
94 }
95 }
96 };
97
98 ($name:ident, $r:expr, $g:expr, $b:expr, $a: expr, $doc: expr) => {
99 #[doc = $doc]
100 pub struct $name;
101 impl Color for $name {
102 fn rgb(&self) -> (u8,u8,u8) {
103 return ($r, $g, $b);
104 }
105 fn alpha(&self) -> f64 {
106 $a
107 }
108 }
109 }
110}
111
112predefined_color!(White, 255, 255, 255, "The predefined white color");
113predefined_color!(Black, 0, 0, 0, "The predefined black color");
114predefined_color!(Red, 255, 0, 0, "The predefined red color");
115predefined_color!(Green, 0, 255, 0, "The predefined green color");
116predefined_color!(Blue, 0, 0, 255, "The predefined blue color");
117predefined_color!(Yellow, 255, 255, 0, "The predefined yellow color");
118predefined_color!(Cyan, 0, 255, 255, "The predefined cyan color");
119predefined_color!(Magenta, 255, 0, 255, "The predefined magenta color");
120predefined_color!(Transparent, 0, 0, 0, 0.0, "The predefined transparent");
121
122pub struct HSLColor(pub f64, pub f64, pub f64);
124
125impl SimpleColor for HSLColor {
126 #[allow(clippy::many_single_char_names)]
127 fn rgb(&self) -> (u8, u8, u8) {
128 let (h, s, l) = (
129 self.0.min(1.0).max(0.0),
130 self.1.min(1.0).max(0.0),
131 self.2.min(1.0).max(0.0),
132 );
133
134 if s == 0.0 {
135 let value = (l * 255.0).round() as u8;
136 return (value, value, value);
137 }
138
139 let q = if l < 0.5 {
140 l * (1.0 + s)
141 } else {
142 l + s - l * s
143 };
144 let p = 2.0 * l - q;
145
146 let cvt = |mut t| {
147 if t < 0.0 {
148 t += 1.0;
149 }
150 if t > 1.0 {
151 t -= 1.0;
152 }
153 let value = if t < 1.0 / 6.0 {
154 p + (q - p) * 6.0 * t
155 } else if t < 1.0 / 2.0 {
156 q
157 } else if t < 2.0 / 3.0 {
158 p + (q - p) * (2.0 / 3.0 - t) * 6.0
159 } else {
160 p
161 };
162 (value * 255.0).round() as u8
163 };
164
165 (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0))
166 }
167}