1use std::rc::Rc;
3
4#[derive(Debug, Clone, Copy, Default)]
7pub struct Rgba(u32);
8impl From<u32> for Rgba {
9 #[inline]
10 fn from(rgb: u32) -> Self {
11 Self(rgb)
12 }
13}
14impl From<(u8, u8, u8)> for Rgba {
15 #[inline]
16 fn from(rgb: (u8, u8, u8)) -> Self {
17 Self::from_tuple_rgb(rgb)
18 }
19}
20impl From<(u8, u8, u8, u8)> for Rgba {
21 #[inline]
22 fn from(rgba: (u8, u8, u8, u8)) -> Self {
23 Self::from_tuple_rgba(rgba)
24 }
25}
26impl From<(f32, f32, f32)> for Rgba {
27 #[inline]
28 fn from((r, g, b): (f32, f32, f32)) -> Self {
29 let r = (r * 255.9).floor() as u8;
30 let g = (g * 255.9).floor() as u8;
31 let b = (b * 255.9).floor() as u8;
32 (r, g, b).into()
33 }
34}
35impl From<(f32, f32, f32, f32)> for Rgba {
36 #[inline]
37 fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
38 let r = (r * 255.9).floor() as u8;
39 let g = (g * 255.9).floor() as u8;
40 let b = (b * 255.9).floor() as u8;
41 let a = (a * 255.9).floor() as u8;
42 (r, g, b, a).into()
43 }
44}
45impl From<Rgba> for (u8, u8, u8, u8) {
46 fn from(rgba: Rgba) -> (u8, u8, u8, u8) {
47 rgba.as_tuple_rgba()
48 }
49}
50impl From<Rgba> for String {
51 fn from(rgba: Rgba) -> String {
52 let (r, g, b, alpha) = rgba.as_tuple_rgba();
53 if alpha == 255 {
54 format!("#{:02x}{:02x}{:02x}", r, g, b)
55 } else {
56 format!("rgba({},{},{},{})", r, g, b, alpha)
57 }
58 }
59}
60impl Rgba {
61 pub fn of_rgba(rgba: u32) -> Self {
62 let r: Self = (rgba & 0xffffff).into();
63 r.set_alpha((rgba >> 24) as u8)
64 }
65 fn from_tuple_rgb((r, g, b): (u8, u8, u8)) -> Self {
66 let rgb = (b as u32) | ((g as u32) << 8) | ((r as u32) << 16);
67 Self(rgb)
68 }
69 fn from_tuple_rgba((r, g, b, a): (u8, u8, u8, u8)) -> Self {
70 let rgba = (b as u32) | ((g as u32) << 8) | ((r as u32) << 16) | (((255 - a) as u32) << 24);
71 Self(rgba)
72 }
73
74 fn as_tuple_rgba(self) -> (u8, u8, u8, u8) {
75 (
76 ((self.0 >> 16) & 0xff) as u8, ((self.0 >> 8) & 0xff) as u8, (self.0 & 0xff) as u8, 255 - (((self.0 >> 24) & 0xff) as u8), )
81 }
82 pub fn alpha(self) -> u8 {
83 255 - (((self.0 >> 24) & 0xff) as u8)
84 }
85 pub fn set_alpha(mut self, alpha: u8) -> Self {
86 self.0 = (self.0 & 0xffffff) | (((255 - alpha) as u32) << 24);
87 self
88 }
89}
90#[derive(Debug, Clone)]
91pub struct Color {
92 text: Rc<String>,
94 rgba: Rgba,
96}
97impl Color {
98 #[inline]
99 #[must_use]
100 fn new<I: Into<String>, J: Into<Rgba>>(text: I, rgba: J) -> Self {
101 let text = text.into().into();
102 let rgba = rgba.into();
103 Self { text, rgba }
104 }
105 #[inline]
106 #[must_use]
107 pub fn set_alpha(mut self, alpha: u8) -> Self {
108 self.rgba = self.rgba.set_alpha(alpha);
109 self
110 }
111 #[inline]
112 #[must_use]
113 pub fn of_rgb<I: Into<Rgba>>(rgba: I) -> Self {
114 let rgba = rgba.into();
115 let text = Rc::new(rgba.into());
116 Self { text, rgba }
117 }
118 pub fn name_is_none(name: &str) -> Option<Self> {
119 match name {
120 "None" | "none" | "NONE" => Some(Self::new("none", (0, 0, 0, 255))),
121 _ => None,
122 }
123 }
124 pub fn as_str(&self) -> Rc<String> {
125 if self.rgba.alpha() == 255 {
126 self.text.clone()
127 } else {
128 Rc::new(self.rgba.into())
129 }
130 }
131}
132pub struct ColorDatabase<'a> {
133 pub colors: &'a [(&'a str, u32)],
134}
135impl<'a> ColorDatabase<'a> {
136 fn find_color_exact_index(&self, name: &str) -> Option<usize> {
137 for (i, (n, _)) in self.colors.iter().enumerate() {
138 if *n == name {
139 return Some(i);
140 }
141 }
142 None
143 }
144 fn canonicalize_name(name: &str) -> Option<String> {
145 let mut r = String::new();
146 for mut c in name.chars() {
147 if c == '_' {
148 continue;
149 }
150 c.make_ascii_lowercase();
151 r.push(c);
152 }
153 Some(r)
154 }
155 fn find_color_index(&self, name: &str) -> Option<usize> {
156 Self::canonicalize_name(name).and_then(|s| self.find_color_exact_index(&s))
157 }
158 pub fn find_color_name(&self, name: &str) -> Option<&str> {
159 self.find_color_index(name).map(|i| self.colors[i].0)
160 }
161 pub fn find_color_rgb(&self, name: &str) -> Option<u32> {
162 self.find_color_index(name).map(|i| self.colors[i].1)
163 }
164 pub fn find_color(&self, name: &str) -> Option<Color> {
165 if let Some(color_none) = Color::name_is_none(name) {
166 Some(color_none)
167 } else {
168 self.find_color_index(name)
169 .map(|i| Color::new(self.colors[i].0, self.colors[i].1))
170 }
171 }
172}
173impl<'a> From<(&str, &'a ColorDatabase<'a>)> for Color {
174 #[inline]
175 fn from((s, db): (&str, &'a ColorDatabase<'a>)) -> Self {
176 db.find_color(s)
177 .unwrap_or_else(|| panic!("Color must be found in the database, but '{}' was not", s))
178 }
179}
180impl<'a> From<(&Color, &'a ColorDatabase<'a>)> for Color {
181 #[inline]
182 fn from((c, _db): (&Color, &'a ColorDatabase<'a>)) -> Self {
183 c.clone()
184 }
185}
186impl<'a, I: Into<Rgba>> From<(I, &'a ColorDatabase<'a>)> for Color {
187 #[inline]
188 fn from((rgb, _db): (I, &'a ColorDatabase<'a>)) -> Self {
189 Color::of_rgb(rgb.into())
190 }
191}