svg_nd/
color_database.rs

1//a Imports
2use std::rc::Rc;
3
4//tp Rgba
5/// Stored as a u32 with (255-alpha) in top 8 bits, then R, then G, then B in bottom 8 bits
6#[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,         // r
77            ((self.0 >> 8) & 0xff) as u8,          // g
78            (self.0 & 0xff) as u8,                 // b
79            255 - (((self.0 >> 24) & 0xff) as u8), // alpha
80        )
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    /// String representation (if transparency is 0)
93    text: Rc<String>,
94    /// RGBA
95    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}