floem_peniko/
color.rs

1// Copyright 2022 The peniko authors and piet authors.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4// Borrows code heavily from the piet (https://github.com/linebender/piet/) Color
5// type.
6
7/// 32-bit RGBA color.
8#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
9pub struct Color {
10    /// Red component.
11    pub r: u8,
12    /// Green component.
13    pub g: u8,
14    /// Blue component.
15    pub b: u8,
16    /// Alpha component.
17    pub a: u8,
18}
19
20impl Color {
21    /// Creates a new RGB color with 255 alpha.
22    pub const fn rgb8(r: u8, g: u8, b: u8) -> Self {
23        Self { r, g, b, a: 255 }
24    }
25
26    /// Creates a new RGBA color.
27    pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
28        Self { r, g, b, a }
29    }
30
31    /// Create a color from three floating point values, each in the range 0.0 to 1.0.
32    ///
33    /// The interpretation is the same as rgb8, and no greater precision is
34    /// (currently) assumed.
35    pub fn rgb(r: f64, g: f64, b: f64) -> Self {
36        Self::rgba(r, g, b, 1.0)
37    }
38
39    /// Create a color from four floating point values, each in the range 0.0 to 1.0.
40    ///
41    /// The interpretation is the same as rgba32, and no greater precision is
42    /// (currently) assumed.
43    pub fn rgba(r: f64, g: f64, b: f64, a: f64) -> Self {
44        let r = (r.clamp(0.0, 1.0) * 255.0).round() as u8;
45        let g = (g.clamp(0.0, 1.0) * 255.0).round() as u8;
46        let b = (b.clamp(0.0, 1.0) * 255.0).round() as u8;
47        let a = (a.clamp(0.0, 1.0) * 255.0).round() as u8;
48        Self { r, g, b, a }
49    }
50
51    /// Create a color from a CIEL\*a\*b\* polar (also known as CIE HCL)
52    /// specification.
53    ///
54    /// The `h` parameter is an angle in degrees, with 0 roughly magenta, 90
55    /// roughly yellow, 180 roughly cyan, and 270 roughly blue. The `l`
56    /// parameter is perceptual luminance, with 0 black and 100 white.
57    /// The `c` parameter is a chrominance concentration, with 0 grayscale
58    /// and a nominal maximum of 127 (in the future, higher values might
59    /// be useful, for high gamut contexts).
60    ///
61    /// Currently this is just converted into sRGB, but in the future as we
62    /// support high-gamut colorspaces, it can be used to specify more colors
63    /// or existing colors with a higher accuracy.
64    ///
65    /// Currently out-of-gamut values are clipped to the nearest sRGB color,
66    /// which is perhaps not ideal (the clipping might change the hue). See
67    /// <https://github.com/d3/d3-color/issues/33> for discussion.
68    pub fn hlc(h: f64, l: f64, c: f64) -> Self {
69        Self::hlca(h, l, c, 1.0)
70    }
71
72    /// Create a color from a CIEL\*a\*b\* polar specification and alpha.
73    ///
74    /// The `a` value represents alpha in the range 0.0 to 1.0.
75    #[allow(non_snake_case)]
76    #[allow(clippy::many_single_char_names)]
77    #[allow(clippy::unreadable_literal)]
78    pub fn hlca(h: f64, l: f64, c: f64, a: f64) -> Color {
79        let alpha = a;
80        // The reverse transformation from Lab to XYZ, see
81        // https://en.wikipedia.org/wiki/CIELAB_color_space
82        fn f_inv(t: f64) -> f64 {
83            let d = 6. / 29.;
84            if t > d {
85                t.powi(3)
86            } else {
87                3. * d * d * (t - 4. / 29.)
88            }
89        }
90        let th = h * (std::f64::consts::PI / 180.);
91        let a = c * th.cos();
92        let b = c * th.sin();
93        let ll = (l + 16.) * (1. / 116.);
94        // Produce raw XYZ values
95        let X = f_inv(ll + a * (1. / 500.));
96        let Y = f_inv(ll);
97        let Z = f_inv(ll - b * (1. / 200.));
98        // This matrix is the concatenation of three sources.
99        // First, the white point is taken to be ICC standard D50, so
100        // the diagonal matrix of [0.9642, 1, 0.8249]. Note that there
101        // is some controversy around this value. However, it matches
102        // the other matrices, thus minimizing chroma error.
103        //
104        // Second, an adaption matrix from D50 to D65. This is the
105        // inverse of the recommended D50 to D65 adaptation matrix
106        // from the W3C sRGB spec:
107        // https://www.w3.org/Graphics/Color/srgb
108        //
109        // Finally, the conversion from XYZ to linear sRGB values,
110        // also taken from the W3C sRGB spec.
111        let r_lin = 3.02172918 * X - 1.61692294 * Y - 0.40480625 * Z;
112        let g_lin = -0.94339358 * X + 1.91584267 * Y + 0.02755094 * Z;
113        let b_lin = 0.06945666 * X - 0.22903204 * Y + 1.15957526 * Z;
114        fn gamma(u: f64) -> f64 {
115            if u <= 0.0031308 {
116                12.92 * u
117            } else {
118                1.055 * u.powf(1. / 2.4) - 0.055
119            }
120        }
121        Color::rgba(gamma(r_lin), gamma(g_lin), gamma(b_lin), alpha)
122    }
123
124    /// Parses a color from a string.
125    ///
126    /// Currently accepts CSS style hexidecimal colors of the forms #RGB, #RGBA,
127    /// #RRGGBB, #RRGGBBAA or the name of an SVG color such as "aliceblue".
128    pub fn parse(s: &str) -> Option<Self> {
129        parse_color(s)
130    }
131
132    /// Returns the color with the alpha component multiplied by the specified
133    /// factor.
134    pub fn with_alpha_factor(self, alpha: f32) -> Self {
135        let mut result = self;
136        result.a = ((result.a as f32) * alpha).round() as u8;
137        result
138    }
139
140    /// Returns the color as a packed premultiplied value.
141    pub fn to_premul_u32(self) -> u32 {
142        let a = self.a as f64 * (1.0 / 255.0);
143        let r = (self.r as f64 * a).round() as u32;
144        let g = (self.g as f64 * a).round() as u32;
145        let b = (self.b as f64 * a).round() as u32;
146        (r << 24) | (g << 16) | (b << 8) | self.a as u32
147    }
148}
149
150/// Named SVG colors.
151impl Color {
152    /// Alice blue (240, 248, 255, 255)
153    pub const ALICE_BLUE: Color = Color::rgba8(240, 248, 255, 255);
154    /// Antique white (250, 235, 215, 255)
155    pub const ANTIQUE_WHITE: Color = Color::rgba8(250, 235, 215, 255);
156    /// Aqua (0, 255, 255, 255)
157    pub const AQUA: Color = Color::rgba8(0, 255, 255, 255);
158    /// Aquamarine (127, 255, 212, 255)
159    pub const AQUAMARINE: Color = Color::rgba8(127, 255, 212, 255);
160    /// Azure (240, 255, 255, 255)
161    pub const AZURE: Color = Color::rgba8(240, 255, 255, 255);
162    /// Beige (245, 245, 220, 255)
163    pub const BEIGE: Color = Color::rgba8(245, 245, 220, 255);
164    /// Bisque (255, 228, 196, 255)
165    pub const BISQUE: Color = Color::rgba8(255, 228, 196, 255);
166    /// Black (0, 0, 0, 255)
167    pub const BLACK: Color = Color::rgba8(0, 0, 0, 255);
168    /// Blanched almond (255, 235, 205, 255)
169    pub const BLANCHED_ALMOND: Color = Color::rgba8(255, 235, 205, 255);
170    /// Blue (0, 0, 255, 255)
171    pub const BLUE: Color = Color::rgba8(0, 0, 255, 255);
172    /// Blue violet (138, 43, 226, 255)
173    pub const BLUE_VIOLET: Color = Color::rgba8(138, 43, 226, 255);
174    /// Brown (165, 42, 42, 255)
175    pub const BROWN: Color = Color::rgba8(165, 42, 42, 255);
176    /// Burlywood (222, 184, 135, 255)
177    pub const BURLYWOOD: Color = Color::rgba8(222, 184, 135, 255);
178    /// Cadet blue (95, 158, 160, 255)
179    pub const CADET_BLUE: Color = Color::rgba8(95, 158, 160, 255);
180    /// Chartreuse (127, 255, 0, 255)
181    pub const CHARTREUSE: Color = Color::rgba8(127, 255, 0, 255);
182    /// Chocolate (210, 105, 30, 255)
183    pub const CHOCOLATE: Color = Color::rgba8(210, 105, 30, 255);
184    /// Coral (255, 127, 80, 255)
185    pub const CORAL: Color = Color::rgba8(255, 127, 80, 255);
186    /// Cornflower blue (100, 149, 237, 255)
187    pub const CORNFLOWER_BLUE: Color = Color::rgba8(100, 149, 237, 255);
188    /// Cornsilk (255, 248, 220, 255)
189    pub const CORNSILK: Color = Color::rgba8(255, 248, 220, 255);
190    /// Crimson (220, 20, 60, 255)
191    pub const CRIMSON: Color = Color::rgba8(220, 20, 60, 255);
192    /// Cyan (0, 255, 255, 255)
193    pub const CYAN: Color = Color::rgba8(0, 255, 255, 255);
194    /// Dark blue (0, 0, 139, 255)
195    pub const DARK_BLUE: Color = Color::rgba8(0, 0, 139, 255);
196    /// Dark cyan (0, 139, 139, 255)
197    pub const DARK_CYAN: Color = Color::rgba8(0, 139, 139, 255);
198    /// Dark goldenrod (184, 134, 11, 255)
199    pub const DARK_GOLDENROD: Color = Color::rgba8(184, 134, 11, 255);
200    /// Dark gray (169, 169, 169, 255)
201    pub const DARK_GRAY: Color = Color::rgba8(169, 169, 169, 255);
202    /// Dark green (0, 100, 0, 255)
203    pub const DARK_GREEN: Color = Color::rgba8(0, 100, 0, 255);
204    /// Dark khaki (189, 183, 107, 255)
205    pub const DARK_KHAKI: Color = Color::rgba8(189, 183, 107, 255);
206    /// Dark magenta (139, 0, 139, 255)
207    pub const DARK_MAGENTA: Color = Color::rgba8(139, 0, 139, 255);
208    /// Dark olive green (85, 107, 47, 255)
209    pub const DARK_OLIVE_GREEN: Color = Color::rgba8(85, 107, 47, 255);
210    /// Dark orange (255, 140, 0, 255)
211    pub const DARK_ORANGE: Color = Color::rgba8(255, 140, 0, 255);
212    /// Dark orchid (153, 50, 204, 255)
213    pub const DARK_ORCHID: Color = Color::rgba8(153, 50, 204, 255);
214    /// Dark red (139, 0, 0, 255)
215    pub const DARK_RED: Color = Color::rgba8(139, 0, 0, 255);
216    /// Dark salmon (233, 150, 122, 255)
217    pub const DARK_SALMON: Color = Color::rgba8(233, 150, 122, 255);
218    /// Dark sea green (143, 188, 143, 255)
219    pub const DARK_SEA_GREEN: Color = Color::rgba8(143, 188, 143, 255);
220    /// Dark slate blue (72, 61, 139, 255)
221    pub const DARK_SLATE_BLUE: Color = Color::rgba8(72, 61, 139, 255);
222    /// Dark slate gray (47, 79, 79, 255)
223    pub const DARK_SLATE_GRAY: Color = Color::rgba8(47, 79, 79, 255);
224    /// Dark turquoise (0, 206, 209, 255)
225    pub const DARK_TURQUOISE: Color = Color::rgba8(0, 206, 209, 255);
226    /// Dark violet (148, 0, 211, 255)
227    pub const DARK_VIOLET: Color = Color::rgba8(148, 0, 211, 255);
228    /// Deep pink (255, 20, 147, 255)
229    pub const DEEP_PINK: Color = Color::rgba8(255, 20, 147, 255);
230    /// Deep sky blue (0, 191, 255, 255)
231    pub const DEEP_SKY_BLUE: Color = Color::rgba8(0, 191, 255, 255);
232    /// Dim gray (105, 105, 105, 255)
233    pub const DIM_GRAY: Color = Color::rgba8(105, 105, 105, 255);
234    /// Dodger blue (30, 144, 255, 255)
235    pub const DODGER_BLUE: Color = Color::rgba8(30, 144, 255, 255);
236    /// Firebrick (178, 34, 34, 255)
237    pub const FIREBRICK: Color = Color::rgba8(178, 34, 34, 255);
238    /// Floral white (255, 250, 240, 255)
239    pub const FLORAL_WHITE: Color = Color::rgba8(255, 250, 240, 255);
240    /// Forest green (34, 139, 34, 255)
241    pub const FOREST_GREEN: Color = Color::rgba8(34, 139, 34, 255);
242    /// Fuchsia (255, 0, 255, 255)
243    pub const FUCHSIA: Color = Color::rgba8(255, 0, 255, 255);
244    /// Gainsboro (220, 220, 220, 255)
245    pub const GAINSBORO: Color = Color::rgba8(220, 220, 220, 255);
246    /// Ghost white (248, 248, 255, 255)
247    pub const GHOST_WHITE: Color = Color::rgba8(248, 248, 255, 255);
248    /// Gold (255, 215, 0, 255)
249    pub const GOLD: Color = Color::rgba8(255, 215, 0, 255);
250    /// Goldenrod (218, 165, 32, 255)
251    pub const GOLDENROD: Color = Color::rgba8(218, 165, 32, 255);
252    /// Gray (128, 128, 128, 255)
253    pub const GRAY: Color = Color::rgba8(128, 128, 128, 255);
254    /// Green (0, 128, 0, 255)
255    pub const GREEN: Color = Color::rgba8(0, 128, 0, 255);
256    /// Green yellow (173, 255, 47, 255)
257    pub const GREEN_YELLOW: Color = Color::rgba8(173, 255, 47, 255);
258    /// Honeydew (240, 255, 240, 255)
259    pub const HONEYDEW: Color = Color::rgba8(240, 255, 240, 255);
260    /// Hot pink (255, 105, 180, 255)
261    pub const HOT_PINK: Color = Color::rgba8(255, 105, 180, 255);
262    /// Indian red (205, 92, 92, 255)
263    pub const INDIAN_RED: Color = Color::rgba8(205, 92, 92, 255);
264    /// Indigo (75, 0, 130, 255)
265    pub const INDIGO: Color = Color::rgba8(75, 0, 130, 255);
266    /// Ivory (255, 255, 240, 255)
267    pub const IVORY: Color = Color::rgba8(255, 255, 240, 255);
268    /// Khaki (240, 230, 140, 255)
269    pub const KHAKI: Color = Color::rgba8(240, 230, 140, 255);
270    /// Lavender (230, 230, 250, 255)
271    pub const LAVENDER: Color = Color::rgba8(230, 230, 250, 255);
272    /// Lavender blush (255, 240, 245, 255)
273    pub const LAVENDER_BLUSH: Color = Color::rgba8(255, 240, 245, 255);
274    /// Lawn green (124, 252, 0, 255)
275    pub const LAWN_GREEN: Color = Color::rgba8(124, 252, 0, 255);
276    /// Lemon chiffon (255, 250, 205, 255)
277    pub const LEMON_CHIFFON: Color = Color::rgba8(255, 250, 205, 255);
278    /// Light blue (173, 216, 230, 255)
279    pub const LIGHT_BLUE: Color = Color::rgba8(173, 216, 230, 255);
280    /// Light coral (240, 128, 128, 255)
281    pub const LIGHT_CORAL: Color = Color::rgba8(240, 128, 128, 255);
282    /// Light cyan (224, 255, 255, 255)
283    pub const LIGHT_CYAN: Color = Color::rgba8(224, 255, 255, 255);
284    /// Light goldenrod yellow (250, 250, 210, 255)
285    pub const LIGHT_GOLDENROD_YELLOW: Color = Color::rgba8(250, 250, 210, 255);
286    /// Light gray (211, 211, 211, 255)
287    pub const LIGHT_GRAY: Color = Color::rgba8(211, 211, 211, 255);
288    /// Light green (144, 238, 144, 255)
289    pub const LIGHT_GREEN: Color = Color::rgba8(144, 238, 144, 255);
290    /// Light pink (255, 182, 193, 255)
291    pub const LIGHT_PINK: Color = Color::rgba8(255, 182, 193, 255);
292    /// Light salmon (255, 160, 122, 255)
293    pub const LIGHT_SALMON: Color = Color::rgba8(255, 160, 122, 255);
294    /// Light sea green (32, 178, 170, 255)
295    pub const LIGHT_SEA_GREEN: Color = Color::rgba8(32, 178, 170, 255);
296    /// Light sky blue (135, 206, 250, 255)
297    pub const LIGHT_SKY_BLUE: Color = Color::rgba8(135, 206, 250, 255);
298    /// Light slate gray (119, 136, 153, 255)
299    pub const LIGHT_SLATE_GRAY: Color = Color::rgba8(119, 136, 153, 255);
300    /// Light steel blue (176, 196, 222, 255)
301    pub const LIGHT_STEEL_BLUE: Color = Color::rgba8(176, 196, 222, 255);
302    /// Light yellow (255, 255, 224, 255)
303    pub const LIGHT_YELLOW: Color = Color::rgba8(255, 255, 224, 255);
304    /// Lime (0, 255, 0, 255)
305    pub const LIME: Color = Color::rgba8(0, 255, 0, 255);
306    /// Lime green (50, 205, 50, 255)
307    pub const LIME_GREEN: Color = Color::rgba8(50, 205, 50, 255);
308    /// Linen (250, 240, 230, 255)
309    pub const LINEN: Color = Color::rgba8(250, 240, 230, 255);
310    /// Magenta (255, 0, 255, 255)
311    pub const MAGENTA: Color = Color::rgba8(255, 0, 255, 255);
312    /// Maroon (128, 0, 0, 255)
313    pub const MAROON: Color = Color::rgba8(128, 0, 0, 255);
314    /// Medium aquamarine (102, 205, 170, 255)
315    pub const MEDIUM_AQUAMARINE: Color = Color::rgba8(102, 205, 170, 255);
316    /// Medium blue (0, 0, 205, 255)
317    pub const MEDIUM_BLUE: Color = Color::rgba8(0, 0, 205, 255);
318    /// Medium orchid (186, 85, 211, 255)
319    pub const MEDIUM_ORCHID: Color = Color::rgba8(186, 85, 211, 255);
320    /// Medium purple (147, 112, 219, 255)
321    pub const MEDIUM_PURPLE: Color = Color::rgba8(147, 112, 219, 255);
322    /// Medium sea green (60, 179, 113, 255)
323    pub const MEDIUM_SEA_GREEN: Color = Color::rgba8(60, 179, 113, 255);
324    /// Medium slate blue (123, 104, 238, 255)
325    pub const MEDIUM_SLATE_BLUE: Color = Color::rgba8(123, 104, 238, 255);
326    /// Medium spring green (0, 250, 154, 255)
327    pub const MEDIUM_SPRING_GREEN: Color = Color::rgba8(0, 250, 154, 255);
328    /// Medium turquoise (72, 209, 204, 255)
329    pub const MEDIUM_TURQUOISE: Color = Color::rgba8(72, 209, 204, 255);
330    /// Medium violet red (199, 21, 133, 255)
331    pub const MEDIUM_VIOLET_RED: Color = Color::rgba8(199, 21, 133, 255);
332    /// Midnight blue (25, 25, 112, 255)
333    pub const MIDNIGHT_BLUE: Color = Color::rgba8(25, 25, 112, 255);
334    /// Mint cream (245, 255, 250, 255)
335    pub const MINT_CREAM: Color = Color::rgba8(245, 255, 250, 255);
336    /// Misty rose (255, 228, 225, 255)
337    pub const MISTY_ROSE: Color = Color::rgba8(255, 228, 225, 255);
338    /// Moccasin (255, 228, 181, 255)
339    pub const MOCCASIN: Color = Color::rgba8(255, 228, 181, 255);
340    /// Navajo white (255, 222, 173, 255)
341    pub const NAVAJO_WHITE: Color = Color::rgba8(255, 222, 173, 255);
342    /// Navy (0, 0, 128, 255)
343    pub const NAVY: Color = Color::rgba8(0, 0, 128, 255);
344    /// Old lace (253, 245, 230, 255)
345    pub const OLD_LACE: Color = Color::rgba8(253, 245, 230, 255);
346    /// Olive (128, 128, 0, 255)
347    pub const OLIVE: Color = Color::rgba8(128, 128, 0, 255);
348    /// Olive drab (107, 142, 35, 255)
349    pub const OLIVE_DRAB: Color = Color::rgba8(107, 142, 35, 255);
350    /// Orange (255, 165, 0, 255)
351    pub const ORANGE: Color = Color::rgba8(255, 165, 0, 255);
352    /// Orange red (255, 69, 0, 255)
353    pub const ORANGE_RED: Color = Color::rgba8(255, 69, 0, 255);
354    /// Orchid (218, 112, 214, 255)
355    pub const ORCHID: Color = Color::rgba8(218, 112, 214, 255);
356    /// Pale goldenrod (238, 232, 170, 255)
357    pub const PALE_GOLDENROD: Color = Color::rgba8(238, 232, 170, 255);
358    /// Pale green (152, 251, 152, 255)
359    pub const PALE_GREEN: Color = Color::rgba8(152, 251, 152, 255);
360    /// Pale turquoise (175, 238, 238, 255)
361    pub const PALE_TURQUOISE: Color = Color::rgba8(175, 238, 238, 255);
362    /// Pale violet red (219, 112, 147, 255)
363    pub const PALE_VIOLET_RED: Color = Color::rgba8(219, 112, 147, 255);
364    /// Papaya whip (255, 239, 213, 255)
365    pub const PAPAYA_WHIP: Color = Color::rgba8(255, 239, 213, 255);
366    /// Peach puff (255, 218, 185, 255)
367    pub const PEACH_PUFF: Color = Color::rgba8(255, 218, 185, 255);
368    /// Peru (205, 133, 63, 255)
369    pub const PERU: Color = Color::rgba8(205, 133, 63, 255);
370    /// Pink (255, 192, 203, 255)
371    pub const PINK: Color = Color::rgba8(255, 192, 203, 255);
372    /// Plum (221, 160, 221, 255)
373    pub const PLUM: Color = Color::rgba8(221, 160, 221, 255);
374    /// Powder blue (176, 224, 230, 255)
375    pub const POWDER_BLUE: Color = Color::rgba8(176, 224, 230, 255);
376    /// Purple (128, 0, 128, 255)
377    pub const PURPLE: Color = Color::rgba8(128, 0, 128, 255);
378    /// Rebecca purple (102, 51, 153, 255)
379    pub const REBECCA_PURPLE: Color = Color::rgba8(102, 51, 153, 255);
380    /// Red (255, 0, 0, 255)
381    pub const RED: Color = Color::rgba8(255, 0, 0, 255);
382    /// Rosy brown (188, 143, 143, 255)
383    pub const ROSY_BROWN: Color = Color::rgba8(188, 143, 143, 255);
384    /// Royal blue (65, 105, 225, 255)
385    pub const ROYAL_BLUE: Color = Color::rgba8(65, 105, 225, 255);
386    /// Saddle brown (139, 69, 19, 255)
387    pub const SADDLE_BROWN: Color = Color::rgba8(139, 69, 19, 255);
388    /// Salmon (250, 128, 114, 255)
389    pub const SALMON: Color = Color::rgba8(250, 128, 114, 255);
390    /// Sandy brown (244, 164, 96, 255)
391    pub const SANDY_BROWN: Color = Color::rgba8(244, 164, 96, 255);
392    /// Sea green (46, 139, 87, 255)
393    pub const SEA_GREEN: Color = Color::rgba8(46, 139, 87, 255);
394    /// Seashell (255, 245, 238, 255)
395    pub const SEASHELL: Color = Color::rgba8(255, 245, 238, 255);
396    /// Sienna (160, 82, 45, 255)
397    pub const SIENNA: Color = Color::rgba8(160, 82, 45, 255);
398    /// Silver (192, 192, 192, 255)
399    pub const SILVER: Color = Color::rgba8(192, 192, 192, 255);
400    /// Sky blue (135, 206, 235, 255)
401    pub const SKY_BLUE: Color = Color::rgba8(135, 206, 235, 255);
402    /// Slate blue (106, 90, 205, 255)
403    pub const SLATE_BLUE: Color = Color::rgba8(106, 90, 205, 255);
404    /// Slate gray (112, 128, 144, 255)
405    pub const SLATE_GRAY: Color = Color::rgba8(112, 128, 144, 255);
406    /// Snow (255, 250, 250, 255)
407    pub const SNOW: Color = Color::rgba8(255, 250, 250, 255);
408    /// Spring green (0, 255, 127, 255)
409    pub const SPRING_GREEN: Color = Color::rgba8(0, 255, 127, 255);
410    /// Steel blue (70, 130, 180, 255)
411    pub const STEEL_BLUE: Color = Color::rgba8(70, 130, 180, 255);
412    /// Tan (210, 180, 140, 255)
413    pub const TAN: Color = Color::rgba8(210, 180, 140, 255);
414    /// Teal (0, 128, 128, 255)
415    pub const TEAL: Color = Color::rgba8(0, 128, 128, 255);
416    /// Thistle (216, 191, 216, 255)
417    pub const THISTLE: Color = Color::rgba8(216, 191, 216, 255);
418    /// Tomato (255, 99, 71, 255)
419    pub const TOMATO: Color = Color::rgba8(255, 99, 71, 255);
420    /// Transparent (0, 0, 0, 0)
421    pub const TRANSPARENT: Color = Color::rgba8(0, 0, 0, 0);
422    /// Turquoise (64, 224, 208, 255)
423    pub const TURQUOISE: Color = Color::rgba8(64, 224, 208, 255);
424    /// Violet (238, 130, 238, 255)
425    pub const VIOLET: Color = Color::rgba8(238, 130, 238, 255);
426    /// Wheat (245, 222, 179, 255)
427    pub const WHEAT: Color = Color::rgba8(245, 222, 179, 255);
428    /// White (255, 255, 255, 255)
429    pub const WHITE: Color = Color::rgba8(255, 255, 255, 255);
430    /// White smoke (245, 245, 245, 255)
431    pub const WHITE_SMOKE: Color = Color::rgba8(245, 245, 245, 255);
432    /// Yellow (255, 255, 0, 255)
433    pub const YELLOW: Color = Color::rgba8(255, 255, 0, 255);
434    /// Yellow green (154, 205, 50, 255)
435    pub const YELLOW_GREEN: Color = Color::rgba8(154, 205, 50, 255);
436}
437
438impl From<[u8; 3]> for Color {
439    fn from(rgb: [u8; 3]) -> Self {
440        Self::rgb8(rgb[0], rgb[1], rgb[2])
441    }
442}
443
444impl From<[u8; 4]> for Color {
445    fn from(rgba: [u8; 4]) -> Self {
446        Self::rgba8(rgba[0], rgba[1], rgba[2], rgba[3])
447    }
448}
449
450fn parse_color(s: &str) -> Option<Color> {
451    let s = s.trim();
452    if let Some(stripped) = s.strip_prefix('#') {
453        Some(color_from_4bit_hex(get_4bit_hex_channels(stripped)?))
454    } else {
455        Some(match s {
456            "aliceblue" => Color::ALICE_BLUE,
457            "antiquewhite" => Color::ANTIQUE_WHITE,
458            "aqua" => Color::AQUA,
459            "aquamarine" => Color::AQUAMARINE,
460            "azure" => Color::AZURE,
461            "beige" => Color::BEIGE,
462            "bisque" => Color::BISQUE,
463            "black" => Color::BLACK,
464            "blanchedalmond" => Color::BLANCHED_ALMOND,
465            "blue" => Color::BLUE,
466            "blueviolet" => Color::BLUE_VIOLET,
467            "brown" => Color::BROWN,
468            "burlywood" => Color::BURLYWOOD,
469            "cadetblue" => Color::CADET_BLUE,
470            "chartreuse" => Color::CHARTREUSE,
471            "chocolate" => Color::CHOCOLATE,
472            "coral" => Color::CORAL,
473            "cornflowerblue" => Color::CORNFLOWER_BLUE,
474            "cornsilk" => Color::CORNSILK,
475            "crimson" => Color::CRIMSON,
476            "cyan" => Color::CYAN,
477            "darkblue" => Color::DARK_BLUE,
478            "darkcyan" => Color::DARK_CYAN,
479            "darkgoldenrod" => Color::DARK_GOLDENROD,
480            "darkgray" => Color::DARK_GRAY,
481            "darkgreen" => Color::DARK_GREEN,
482            "darkkhaki" => Color::DARK_KHAKI,
483            "darkmagenta" => Color::DARK_MAGENTA,
484            "darkolivegreen" => Color::DARK_OLIVE_GREEN,
485            "darkorange" => Color::DARK_ORANGE,
486            "darkorchid" => Color::DARK_ORCHID,
487            "darkred" => Color::DARK_RED,
488            "darksalmon" => Color::DARK_SALMON,
489            "darkseagreen" => Color::DARK_SEA_GREEN,
490            "darkslateblue" => Color::DARK_SLATE_BLUE,
491            "darkslategray" => Color::DARK_SLATE_GRAY,
492            "darkturquoise" => Color::DARK_TURQUOISE,
493            "darkviolet" => Color::DARK_VIOLET,
494            "deeppink" => Color::DEEP_PINK,
495            "deepskyblue" => Color::DEEP_SKY_BLUE,
496            "dimgray" => Color::DIM_GRAY,
497            "dodgerblue" => Color::DODGER_BLUE,
498            "firebrick" => Color::FIREBRICK,
499            "floralwhite" => Color::FLORAL_WHITE,
500            "forestgreen" => Color::FOREST_GREEN,
501            "fuchsia" => Color::FUCHSIA,
502            "gainsboro" => Color::GAINSBORO,
503            "ghostwhite" => Color::GHOST_WHITE,
504            "gold" => Color::GOLD,
505            "goldenrod" => Color::GOLDENROD,
506            "gray" => Color::GRAY,
507            "green" => Color::GREEN,
508            "greenyellow" => Color::GREEN_YELLOW,
509            "honeydew" => Color::HONEYDEW,
510            "hotpink" => Color::HOT_PINK,
511            "indianred" => Color::INDIAN_RED,
512            "indigo" => Color::INDIGO,
513            "ivory" => Color::IVORY,
514            "khaki" => Color::KHAKI,
515            "lavender" => Color::LAVENDER,
516            "lavenderblush" => Color::LAVENDER_BLUSH,
517            "lawngreen" => Color::LAWN_GREEN,
518            "lemonchiffon" => Color::LEMON_CHIFFON,
519            "lightblue" => Color::LIGHT_BLUE,
520            "lightcoral" => Color::LIGHT_CORAL,
521            "lightcyan" => Color::LIGHT_CYAN,
522            "lightgoldenrodyellow" => Color::LIGHT_GOLDENROD_YELLOW,
523            "lightgray" => Color::LIGHT_GRAY,
524            "lightgreen" => Color::LIGHT_GREEN,
525            "lightpink" => Color::LIGHT_PINK,
526            "lightsalmon" => Color::LIGHT_SALMON,
527            "lightseagreen" => Color::LIGHT_SEA_GREEN,
528            "lightskyblue" => Color::LIGHT_SKY_BLUE,
529            "lightslategray" => Color::LIGHT_SLATE_GRAY,
530            "lightsteelblue" => Color::LIGHT_STEEL_BLUE,
531            "lightyellow" => Color::LIGHT_YELLOW,
532            "lime" => Color::LIME,
533            "limegreen" => Color::LIME_GREEN,
534            "linen" => Color::LINEN,
535            "magenta" => Color::MAGENTA,
536            "maroon" => Color::MAROON,
537            "mediumaquamarine" => Color::MEDIUM_AQUAMARINE,
538            "mediumblue" => Color::MEDIUM_BLUE,
539            "mediumorchid" => Color::MEDIUM_ORCHID,
540            "mediumpurple" => Color::MEDIUM_PURPLE,
541            "mediumseagreen" => Color::MEDIUM_SEA_GREEN,
542            "mediumslateblue" => Color::MEDIUM_SLATE_BLUE,
543            "mediumspringgreen" => Color::MEDIUM_SPRING_GREEN,
544            "mediumturquoise" => Color::MEDIUM_TURQUOISE,
545            "mediumvioletred" => Color::MEDIUM_VIOLET_RED,
546            "midnightblue" => Color::MIDNIGHT_BLUE,
547            "mintcream" => Color::MINT_CREAM,
548            "mistyrose" => Color::MISTY_ROSE,
549            "moccasin" => Color::MOCCASIN,
550            "navajowhite" => Color::NAVAJO_WHITE,
551            "navy" => Color::NAVY,
552            "oldlace" => Color::OLD_LACE,
553            "olive" => Color::OLIVE,
554            "olivedrab" => Color::OLIVE_DRAB,
555            "orange" => Color::ORANGE,
556            "orangered" => Color::ORANGE_RED,
557            "orchid" => Color::ORCHID,
558            "palegoldenrod" => Color::PALE_GOLDENROD,
559            "palegreen" => Color::PALE_GREEN,
560            "paleturquoise" => Color::PALE_TURQUOISE,
561            "palevioletred" => Color::PALE_VIOLET_RED,
562            "papayawhip" => Color::PAPAYA_WHIP,
563            "peachpuff" => Color::PEACH_PUFF,
564            "peru" => Color::PERU,
565            "pink" => Color::PINK,
566            "plum" => Color::PLUM,
567            "powderblue" => Color::POWDER_BLUE,
568            "purple" => Color::PURPLE,
569            "rebeccapurple" => Color::REBECCA_PURPLE,
570            "red" => Color::RED,
571            "rosybrown" => Color::ROSY_BROWN,
572            "royalblue" => Color::ROYAL_BLUE,
573            "saddlebrown" => Color::SADDLE_BROWN,
574            "salmon" => Color::SALMON,
575            "sandybrown" => Color::SANDY_BROWN,
576            "seagreen" => Color::SEA_GREEN,
577            "seashell" => Color::SEASHELL,
578            "sienna" => Color::SIENNA,
579            "silver" => Color::SILVER,
580            "skyblue" => Color::SKY_BLUE,
581            "slateblue" => Color::SLATE_BLUE,
582            "slategray" => Color::SLATE_GRAY,
583            "snow" => Color::SNOW,
584            "springgreen" => Color::SPRING_GREEN,
585            "steelblue" => Color::STEEL_BLUE,
586            "tan" => Color::TAN,
587            "teal" => Color::TEAL,
588            "thistle" => Color::THISTLE,
589            "tomato" => Color::TOMATO,
590            "transparent" => Color::TRANSPARENT,
591            "turquoise" => Color::TURQUOISE,
592            "violet" => Color::VIOLET,
593            "wheat" => Color::WHEAT,
594            "white" => Color::WHITE,
595            "whitesmoke" => Color::WHITE_SMOKE,
596            "yellow" => Color::YELLOW,
597            "yellowgreen" => Color::YELLOW_GREEN,
598            _ => return None,
599        })
600    }
601}
602
603// The following hex color parsing code taken from piet:
604
605const fn get_4bit_hex_channels(hex_str: &str) -> Option<[u8; 8]> {
606    let mut four_bit_channels = match hex_str.as_bytes() {
607        &[b'#', r, g, b] | &[r, g, b] => [r, r, g, g, b, b, b'f', b'f'],
608        &[b'#', r, g, b, a] | &[r, g, b, a] => [r, r, g, g, b, b, a, a],
609        &[b'#', r0, r1, g0, g1, b0, b1] | &[r0, r1, g0, g1, b0, b1] => {
610            [r0, r1, g0, g1, b0, b1, b'f', b'f']
611        }
612        &[b'#', r0, r1, g0, g1, b0, b1, a0, a1] | &[r0, r1, g0, g1, b0, b1, a0, a1] => {
613            [r0, r1, g0, g1, b0, b1, a0, a1]
614        }
615        _ => return None,
616    };
617
618    // convert to hex in-place
619    // this is written without a for loop to satisfy `const`
620    let mut i = 0;
621    while i < four_bit_channels.len() {
622        let ascii = four_bit_channels[i];
623        let as_hex = match hex_from_ascii_byte(ascii) {
624            Ok(hex) => hex,
625            Err(_) => return None,
626        };
627        four_bit_channels[i] = as_hex;
628        i += 1;
629    }
630    Some(four_bit_channels)
631}
632
633const fn color_from_4bit_hex(components: [u8; 8]) -> Color {
634    let [r0, r1, g0, g1, b0, b1, a0, a1] = components;
635    Color::rgba8(r0 << 4 | r1, g0 << 4 | g1, b0 << 4 | b1, a0 << 4 | a1)
636}
637
638const fn hex_from_ascii_byte(b: u8) -> Result<u8, u8> {
639    match b {
640        b'0'..=b'9' => Ok(b - b'0'),
641        b'A'..=b'F' => Ok(b - b'A' + 10),
642        b'a'..=b'f' => Ok(b - b'a' + 10),
643        _ => Err(b),
644    }
645}