1#![allow(clippy::get_first)]
2#![allow(clippy::int_plus_one)]
3#![allow(clippy::len_zero)]
4#![cfg_attr(feature = "bench", feature(test))]
5
6use std::f32;
7use std::str::{self, FromStr};
8
9const NONE: f32 = 0_f32;
10
11#[doc(hidden)]
12pub type Rgba = Srgb;
13
14#[derive(Clone, Copy, Debug, PartialEq)]
16pub struct Srgb {
17 pub red: f32,
19 pub green: f32,
21 pub blue: f32,
23 pub alpha: f32,
25}
26
27impl Srgb {
28 pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Srgb {
29 Srgb {
30 red,
31 green,
32 blue,
33 alpha,
34 }
35 }
36
37 fn from_rgb8(red: u8, green: u8, blue: u8) -> Srgb {
38 Srgb::from_rgba8(red, green, blue, 255)
39 }
40
41 fn from_rgba8(red: u8, green: u8, blue: u8, alpha: u8) -> Srgb {
42 Srgb {
43 red: red as f32 / 255.,
44 green: green as f32 / 255.,
45 blue: blue as f32 / 255.,
46 alpha: alpha as f32 / 255.,
47 }
48 }
49}
50
51#[derive(Debug)]
52pub struct ParseColorError;
53
54impl FromStr for Srgb {
55 type Err = ParseColorError;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 parse_css_color(s.as_bytes()).map_err(|_| ParseColorError)
59 }
60}
61
62fn parse_css_color(input: &[u8]) -> Result<Srgb, ()> {
64 if let Ok(input) = consume_byte(input, b'#') {
65 parse_hex(input)
66 } else if let Ok(input) = consume_function(input, b"rgb") {
67 parse_rgb(input)
68 } else if let Ok(input) = consume_function(input, b"rgba") {
69 parse_rgb(input)
70 } else if let Ok(input) = consume_function(input, b"hsl") {
71 parse_hsl(input)
72 } else if let Ok(input) = consume_function(input, b"hsla") {
73 parse_hsl(input)
74 } else if let Ok(input) = consume_function(input, b"hwb") {
75 parse_hwb(input)
76 } else {
77 parse_named(input)
78 }
79}
80
81fn clamp_unit_f32(value: f32) -> f32 {
82 value.max(0.).min(1.)
83}
84
85fn normalize_hue(value: f32) -> f32 {
86 value - value.floor()
87}
88
89struct Hsla {
90 pub hue: f32,
91 pub saturation: f32,
92 pub lightness: f32,
93 pub alpha: f32,
94}
95
96impl From<Hsla> for Srgb {
98 fn from(hsla: Hsla) -> Self {
99 let t2 = if hsla.lightness <= 0.5 {
100 hsla.lightness * (hsla.saturation + 1.)
101 } else {
102 hsla.lightness + hsla.saturation - hsla.lightness * hsla.saturation
103 };
104 let t1 = hsla.lightness * 2. - t2;
105
106 let hue_to_rgb = |h6: f32| -> f32 {
107 if h6 < 1. {
108 (t2 - t1) * h6 + t1
109 } else if h6 < 3. {
110 t2
111 } else if h6 < 4. {
112 (t2 - t1) * (4. - h6) + t1
113 } else {
114 t1
115 }
116 };
117 let h6 = hsla.hue * 6.;
118 let h6_red = if h6 + 2. < 6. { h6 + 2. } else { h6 - 4. };
119 let h6_blue = if h6 - 2. >= 0. { h6 - 2. } else { h6 + 4. };
120 Srgb {
121 red: hue_to_rgb(h6_red),
122 green: hue_to_rgb(h6),
123 blue: hue_to_rgb(h6_blue),
124 alpha: hsla.alpha,
125 }
126 }
127}
128
129struct Hwba {
130 pub hue: f32,
131 pub whiteness: f32,
132 pub blackness: f32,
133 pub alpha: f32,
134}
135
136impl From<Hwba> for Srgb {
138 fn from(hwba: Hwba) -> Self {
139 if hwba.whiteness + hwba.blackness >= 1. {
142 let gray = hwba.whiteness / (hwba.whiteness + hwba.blackness);
143 Srgb {
144 red: gray,
145 green: gray,
146 blue: gray,
147 alpha: hwba.alpha,
148 }
149 } else {
150 fn hue_to_rgb(h6: f32) -> f32 {
151 if h6 < 1. {
152 h6
153 } else if h6 < 3. {
154 1.
155 } else if h6 < 4. {
156 4. - h6
157 } else {
158 0.
159 }
160 }
161 let h6 = hwba.hue * 6.;
162 let h6_red = if h6 + 2. < 6. { h6 + 2. } else { h6 - 4. };
163 let h6_blue = if h6 - 2. >= 0. { h6 - 2. } else { h6 + 4. };
164 let x = 1. - hwba.whiteness - hwba.blackness;
165 Srgb {
166 red: hue_to_rgb(h6_red) * x + hwba.whiteness,
167 green: hue_to_rgb(h6) * x + hwba.whiteness,
168 blue: hue_to_rgb(h6_blue) * x + hwba.whiteness,
169 alpha: hwba.alpha,
170 }
171 }
172 }
173}
174
175fn is_ident_start(input: &[u8]) -> bool {
176 match input.get(0) {
177 Some(b'-') => match input.get(1) {
178 Some(b'-') => true,
179 Some(c) => is_name_start(*c),
180 _ => false,
181 },
182 Some(c) => is_name_start(*c),
183 _ => false,
184 }
185}
186
187fn is_name_start(c: u8) -> bool {
188 match c {
189 b'a'..=b'z' | b'A'..=b'Z' | b'_' => true,
190 c => !c.is_ascii(),
191 }
192}
193
194fn is_name(c: u8) -> bool {
195 match c {
196 b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' | b'-' => true,
197 c => !c.is_ascii(),
198 }
199}
200
201fn is_whitespace(c: u8) -> bool {
202 c <= b' ' && (c == b' ' || c == b'\n' || c == b'\t' || c == b'\r' || c == b'\x0C')
203}
204
205fn digit(c: u8) -> Result<u8, ()> {
206 match c {
207 b'0'..=b'9' => Ok(c - b'0'),
208 _ => Err(()),
209 }
210}
211
212fn hex_digit(c: u8) -> Result<u8, ()> {
213 match c {
214 b'0'..=b'9' => Ok(c - b'0'),
215 b'A'..=b'F' => Ok(c - b'A' + 10),
216 b'a'..=b'f' => Ok(c - b'a' + 10),
217 _ => Err(()),
218 }
219}
220
221fn skip_ws(mut input: &[u8]) -> &[u8] {
222 while input.len() > 0 && is_whitespace(input[0]) {
223 input = &input[1..];
224 }
225 input
226}
227
228fn consume_byte(input: &[u8], b: u8) -> Result<&[u8], ()> {
229 match input.get(0) {
230 Some(c) if *c == b => Ok(&input[1..]),
231 _ => Err(()),
232 }
233}
234
235fn consume_function<'a>(input: &'a [u8], name: &[u8]) -> Result<&'a [u8], ()> {
239 debug_assert!(is_ident_start(name));
240
241 let n = name.len();
242 if input.len() >= n + 1 && input[..n].eq_ignore_ascii_case(name) && input[n] == b'(' {
243 Ok(skip_ws(&input[n + 1..]))
244 } else {
245 Err(())
246 }
247}
248
249#[inline]
250fn consume_name<'a>(input: &'a [u8], name: &[u8]) -> Result<&'a [u8], ()> {
251 debug_assert!(is_ident_start(name));
252
253 let n = name.len();
254 if input.len() >= n
255 && input[..n].eq_ignore_ascii_case(name)
256 && input.get(n).filter(|c| is_name(**c)).is_none()
257 {
258 Ok(&input[n..])
259 } else {
260 Err(())
261 }
262}
263
264fn consume_none(input: &[u8]) -> Result<&[u8], ()> {
265 consume_name(input, b"none")
266}
267
268fn consume_number(mut input: &[u8]) -> Result<&[u8], ()> {
269 fn skip_sign(input: &[u8]) -> &[u8] {
270 match input.get(0) {
271 Some(b'+') | Some(b'-') => &input[1..],
272 _ => input,
273 }
274 }
275 fn consume_digits(mut input: &[u8]) -> Result<&[u8], ()> {
276 match input.get(0).map(|c| digit(*c)) {
277 Some(Ok(_)) => {
278 while let Some(Ok(_)) = input.get(0).map(|c| digit(*c)) {
279 input = &input[1..];
280 }
281 Ok(input)
282 }
283 _ => Err(()),
284 }
285 }
286
287 input = skip_sign(input);
288 match input.get(0) {
289 Some(b'.') => {}
290 _ => {
291 input = consume_digits(input)?;
292 }
293 }
294 if let Some(b'.') = input.get(0) {
295 input = consume_digits(&input[1..])?;
296 }
297 match input.get(0) {
298 Some(b'E') | Some(b'e') => {
299 input = skip_sign(&input[1..]);
300 input = consume_digits(input)?;
301 }
302 _ => {}
303 }
304 Ok(input)
305}
306
307fn parse_number(input: &[u8]) -> Result<(&[u8], f32), ()> {
308 let pos = input.len() - consume_number(input)?.len();
309 Ok((
310 &input[pos..],
311 str::from_utf8(&input[..pos])
312 .unwrap()
313 .parse()
314 .map_err(|_| ())?,
315 ))
316}
317
318fn parse_percentage(input: &[u8]) -> Result<(&[u8], f32), ()> {
320 let (input, value) = parse_number(input)?;
321 let input = consume_byte(input, b'%')?;
322 Ok((input, value / 100.))
323}
324
325enum NumberOrPercentage {
326 Number,
327 Percentage,
328}
329use NumberOrPercentage::*;
330
331trait Frac {
332 fn frac(&self, denom: f32) -> f32;
333}
334
335impl Frac for (NumberOrPercentage, f32) {
336 fn frac(&self, denom: f32) -> f32 {
337 match self.0 {
338 Number => self.1 / denom,
339 Percentage => self.1,
340 }
341 }
342}
343
344fn parse_number_or_percentage(input: &[u8]) -> Result<(&[u8], (NumberOrPercentage, f32)), ()> {
345 let (input, value) = parse_number(input)?;
346
347 if let Ok(input) = consume_byte(input, b'%') {
348 Ok((input, (Percentage, value / 100.)))
349 } else {
350 Ok((input, (Number, value)))
351 }
352}
353
354fn parse_alpha_value(input: &[u8]) -> Result<(&[u8], f32), ()> {
356 let (input, (_, alpha)) = parse_number_or_percentage(input)?;
357 Ok((input, clamp_unit_f32(alpha)))
358}
359
360fn parse_hue(input: &[u8]) -> Result<(&[u8], f32), ()> {
362 let (input, value) = parse_number(input)?;
363
364 if !is_ident_start(input) {
365 Ok((input, value / 360.))
366 } else if let Ok(input) = consume_name(input, b"deg") {
367 Ok((input, value / 360.))
368 } else if let Ok(input) = consume_name(input, b"grad") {
369 Ok((input, value / 400.))
370 } else if let Ok(input) = consume_name(input, b"rad") {
371 Ok((input, value / (2. * f32::consts::PI)))
372 } else if let Ok(input) = consume_name(input, b"turn") {
373 Ok((input, value))
374 } else {
375 Err(())
376 }
377}
378
379fn parse_hex(input: &[u8]) -> Result<Srgb, ()> {
381 match input.len() {
382 8 => Ok(Srgb::from_rgba8(
383 hex_digit(input[0])? * 16 + hex_digit(input[1])?,
384 hex_digit(input[2])? * 16 + hex_digit(input[3])?,
385 hex_digit(input[4])? * 16 + hex_digit(input[5])?,
386 hex_digit(input[6])? * 16 + hex_digit(input[7])?,
387 )),
388 6 => Ok(Srgb::from_rgb8(
389 hex_digit(input[0])? * 16 + hex_digit(input[1])?,
390 hex_digit(input[2])? * 16 + hex_digit(input[3])?,
391 hex_digit(input[4])? * 16 + hex_digit(input[5])?,
392 )),
393 4 => Ok(Srgb::from_rgba8(
394 hex_digit(input[0])? * 17,
395 hex_digit(input[1])? * 17,
396 hex_digit(input[2])? * 17,
397 hex_digit(input[3])? * 17,
398 )),
399 3 => Ok(Srgb::from_rgb8(
400 hex_digit(input[0])? * 17,
401 hex_digit(input[1])? * 17,
402 hex_digit(input[2])? * 17,
403 )),
404 _ => Err(()),
405 }
406}
407
408fn parse_hsl(input: &[u8]) -> Result<Srgb, ()> {
421 let (input, hue, legacy_syntax) = if let Ok((input, hue)) = parse_hue(input) {
422 let input = skip_ws(input);
423 match input.get(0) {
424 Some(b',') => (skip_ws(&input[1..]), hue, true),
425 _ => (input, hue, false),
426 }
427 } else {
428 (skip_ws(consume_none(input)?), NONE, false)
429 };
430
431 let (input, saturation, lightness) = if legacy_syntax {
432 let (mut input, saturation) = parse_percentage(input)?;
433 input = skip_ws(input);
434 input = skip_ws(consume_byte(input, b',')?);
435 let (mut input, lightness) = parse_percentage(input)?;
436 input = skip_ws(input);
437 (input, saturation, lightness)
438 } else {
439 let (input, saturation) = if let Ok((input, saturation)) = parse_number_or_percentage(input)
440 {
441 (skip_ws(input), saturation.frac(100.))
442 } else {
443 (skip_ws(consume_none(input)?), NONE)
444 };
445 let (input, lightness) = if let Ok((input, lightness)) = parse_number_or_percentage(input) {
446 (skip_ws(input), lightness.frac(100.))
447 } else {
448 (skip_ws(consume_none(input)?), NONE)
449 };
450 (input, saturation, lightness)
451 };
452
453 let (input, alpha) = match (input.get(0), legacy_syntax) {
454 (Some(b'/'), false) | (Some(b','), true) => {
455 let input = skip_ws(&input[1..]);
456 if let Ok((input, alpha)) = parse_alpha_value(input) {
457 (skip_ws(input), alpha)
458 } else if !legacy_syntax {
459 (skip_ws(consume_none(input)?), NONE)
460 } else {
461 return Err(());
462 }
463 }
464 _ => (input, 1.),
465 };
466
467 if input != b")" {
468 return Err(());
469 }
470
471 Ok(Srgb::from(Hsla {
472 hue: normalize_hue(hue),
473 saturation: clamp_unit_f32(saturation),
474 lightness: clamp_unit_f32(lightness),
475 alpha,
476 }))
477}
478
479fn parse_hwb(input: &[u8]) -> Result<Srgb, ()> {
484 let (input, hue) = if let Ok((input, hue)) = parse_hue(input) {
485 (skip_ws(input), hue)
486 } else {
487 (skip_ws(consume_none(input)?), NONE)
488 };
489
490 let (input, whiteness) = if let Ok((input, whiteness)) = parse_number_or_percentage(input) {
491 (skip_ws(input), whiteness.frac(100.))
492 } else {
493 (skip_ws(consume_none(input)?), NONE)
494 };
495
496 let (input, blackness) = if let Ok((input, blackness)) = parse_number_or_percentage(input) {
497 (skip_ws(input), blackness.frac(100.))
498 } else {
499 (skip_ws(consume_none(input)?), NONE)
500 };
501
502 let (input, alpha) = match input.get(0) {
503 Some(b'/') => {
504 let input = skip_ws(&input[1..]);
505 if let Ok((input, alpha)) = parse_alpha_value(input) {
506 (skip_ws(input), alpha)
507 } else {
508 (skip_ws(consume_none(input)?), NONE)
509 }
510 }
511 _ => (input, 1.),
512 };
513
514 if input != b")" {
515 return Err(());
516 }
517
518 Ok(Srgb::from(Hwba {
519 hue: normalize_hue(hue),
520 whiteness: clamp_unit_f32(whiteness),
521 blackness: clamp_unit_f32(blackness),
522 alpha,
523 }))
524}
525
526fn parse_rgb(input: &[u8]) -> Result<Srgb, ()> {
535 let (input, red, legacy_syntax) = if let Ok((input, red)) = parse_number_or_percentage(input) {
536 let input = skip_ws(input);
537 match input.get(0) {
538 Some(b',') => (skip_ws(&input[1..]), Some(red), true),
539 _ => (input, Some(red), false),
540 }
541 } else {
542 (skip_ws(consume_none(input)?), None, false)
543 };
544
545 let (input, red, green, blue) = if legacy_syntax {
546 match red.unwrap() {
547 (Number, red) => {
548 let (mut input, green) = parse_number(input)?;
549 input = skip_ws(input);
550 input = skip_ws(consume_byte(input, b',')?);
551 let (mut input, blue) = parse_number(input)?;
552 input = skip_ws(input);
553 (input, red / 255., green / 255., blue / 255.)
554 }
555 (Percentage, red) => {
556 let (mut input, green) = parse_percentage(input)?;
557 input = skip_ws(input);
558 input = skip_ws(consume_byte(input, b',')?);
559 let (mut input, blue) = parse_percentage(input)?;
560 input = skip_ws(input);
561 (input, red, green, blue)
562 }
563 }
564 } else {
565 let red = red.map_or(NONE, |red| red.frac(255.));
566 let (input, green) = if let Ok((input, green)) = parse_number_or_percentage(input) {
567 (skip_ws(input), green.frac(255.))
568 } else {
569 (skip_ws(consume_none(input)?), NONE)
570 };
571 let (input, blue) = if let Ok((input, blue)) = parse_number_or_percentage(input) {
572 (skip_ws(input), blue.frac(255.))
573 } else {
574 (skip_ws(consume_none(input)?), NONE)
575 };
576 (input, red, green, blue)
577 };
578
579 let (input, alpha) = match (input.get(0), legacy_syntax) {
580 (Some(b'/'), false) | (Some(b','), true) => {
581 let input = skip_ws(&input[1..]);
582 if let Ok((input, alpha)) = parse_alpha_value(input) {
583 (skip_ws(input), alpha)
584 } else if !legacy_syntax {
585 (skip_ws(consume_none(input)?), NONE)
586 } else {
587 return Err(());
588 }
589 }
590 _ => (input, 1.),
591 };
592
593 if input != b")" {
594 return Err(());
595 }
596
597 Ok(Srgb::new(
598 clamp_unit_f32(red),
599 clamp_unit_f32(green),
600 clamp_unit_f32(blue),
601 alpha,
602 ))
603}
604
605macro_rules! rgb {
606 ($red: expr, $green: expr, $blue: expr) => {
607 Srgb::from_rgb8($red, $green, $blue)
608 };
609}
610
611fn parse_named(input: &[u8]) -> Result<Srgb, ()> {
612 const NAMED_MAX_LEN: usize = 20;
613 if input.len() > NAMED_MAX_LEN {
614 return Err(());
615 }
616 let mut name = [b'\0'; NAMED_MAX_LEN];
617 let name = &mut name[..input.len()];
618 for (i, c) in input.iter().enumerate() {
619 name[i] = c.to_ascii_lowercase();
620 }
621 Ok(match &*name {
622 b"aliceblue" => rgb!(240, 248, 255),
623 b"antiquewhite" => rgb!(250, 235, 215),
624 b"aqua" => rgb!(0, 255, 255),
625 b"aquamarine" => rgb!(127, 255, 212),
626 b"azure" => rgb!(240, 255, 255),
627 b"beige" => rgb!(245, 245, 220),
628 b"bisque" => rgb!(255, 228, 196),
629 b"black" => rgb!(0, 0, 0),
630 b"blanchedalmond" => rgb!(255, 235, 205),
631 b"blue" => rgb!(0, 0, 255),
632 b"blueviolet" => rgb!(138, 43, 226),
633 b"brown" => rgb!(165, 42, 42),
634 b"burlywood" => rgb!(222, 184, 135),
635 b"cadetblue" => rgb!(95, 158, 160),
636 b"chartreuse" => rgb!(127, 255, 0),
637 b"chocolate" => rgb!(210, 105, 30),
638 b"coral" => rgb!(255, 127, 80),
639 b"cornflowerblue" => rgb!(100, 149, 237),
640 b"cornsilk" => rgb!(255, 248, 220),
641 b"crimson" => rgb!(220, 20, 60),
642 b"cyan" => rgb!(0, 255, 255),
643 b"darkblue" => rgb!(0, 0, 139),
644 b"darkcyan" => rgb!(0, 139, 139),
645 b"darkgoldenrod" => rgb!(184, 134, 11),
646 b"darkgray" => rgb!(169, 169, 169),
647 b"darkgreen" => rgb!(0, 100, 0),
648 b"darkgrey" => rgb!(169, 169, 169),
649 b"darkkhaki" => rgb!(189, 183, 107),
650 b"darkmagenta" => rgb!(139, 0, 139),
651 b"darkolivegreen" => rgb!(85, 107, 47),
652 b"darkorange" => rgb!(255, 140, 0),
653 b"darkorchid" => rgb!(153, 50, 204),
654 b"darkred" => rgb!(139, 0, 0),
655 b"darksalmon" => rgb!(233, 150, 122),
656 b"darkseagreen" => rgb!(143, 188, 143),
657 b"darkslateblue" => rgb!(72, 61, 139),
658 b"darkslategray" => rgb!(47, 79, 79),
659 b"darkslategrey" => rgb!(47, 79, 79),
660 b"darkturquoise" => rgb!(0, 206, 209),
661 b"darkviolet" => rgb!(148, 0, 211),
662 b"deeppink" => rgb!(255, 20, 147),
663 b"deepskyblue" => rgb!(0, 191, 255),
664 b"dimgray" => rgb!(105, 105, 105),
665 b"dimgrey" => rgb!(105, 105, 105),
666 b"dodgerblue" => rgb!(30, 144, 255),
667 b"firebrick" => rgb!(178, 34, 34),
668 b"floralwhite" => rgb!(255, 250, 240),
669 b"forestgreen" => rgb!(34, 139, 34),
670 b"fuchsia" => rgb!(255, 0, 255),
671 b"gainsboro" => rgb!(220, 220, 220),
672 b"ghostwhite" => rgb!(248, 248, 255),
673 b"gold" => rgb!(255, 215, 0),
674 b"goldenrod" => rgb!(218, 165, 32),
675 b"gray" => rgb!(128, 128, 128),
676 b"green" => rgb!(0, 128, 0),
677 b"greenyellow" => rgb!(173, 255, 47),
678 b"grey" => rgb!(128, 128, 128),
679 b"honeydew" => rgb!(240, 255, 240),
680 b"hotpink" => rgb!(255, 105, 180),
681 b"indianred" => rgb!(205, 92, 92),
682 b"indigo" => rgb!(75, 0, 130),
683 b"ivory" => rgb!(255, 255, 240),
684 b"khaki" => rgb!(240, 230, 140),
685 b"lavender" => rgb!(230, 230, 250),
686 b"lavenderblush" => rgb!(255, 240, 245),
687 b"lawngreen" => rgb!(124, 252, 0),
688 b"lemonchiffon" => rgb!(255, 250, 205),
689 b"lightblue" => rgb!(173, 216, 230),
690 b"lightcoral" => rgb!(240, 128, 128),
691 b"lightcyan" => rgb!(224, 255, 255),
692 b"lightgoldenrodyellow" => rgb!(250, 250, 210),
693 b"lightgray" => rgb!(211, 211, 211),
694 b"lightgreen" => rgb!(144, 238, 144),
695 b"lightgrey" => rgb!(211, 211, 211),
696 b"lightpink" => rgb!(255, 182, 193),
697 b"lightsalmon" => rgb!(255, 160, 122),
698 b"lightseagreen" => rgb!(32, 178, 170),
699 b"lightskyblue" => rgb!(135, 206, 250),
700 b"lightslategray" => rgb!(119, 136, 153),
701 b"lightslategrey" => rgb!(119, 136, 153),
702 b"lightsteelblue" => rgb!(176, 196, 222),
703 b"lightyellow" => rgb!(255, 255, 224),
704 b"lime" => rgb!(0, 255, 0),
705 b"limegreen" => rgb!(50, 205, 50),
706 b"linen" => rgb!(250, 240, 230),
707 b"magenta" => rgb!(255, 0, 255),
708 b"maroon" => rgb!(128, 0, 0),
709 b"mediumaquamarine" => rgb!(102, 205, 170),
710 b"mediumblue" => rgb!(0, 0, 205),
711 b"mediumorchid" => rgb!(186, 85, 211),
712 b"mediumpurple" => rgb!(147, 112, 219),
713 b"mediumseagreen" => rgb!(60, 179, 113),
714 b"mediumslateblue" => rgb!(123, 104, 238),
715 b"mediumspringgreen" => rgb!(0, 250, 154),
716 b"mediumturquoise" => rgb!(72, 209, 204),
717 b"mediumvioletred" => rgb!(199, 21, 133),
718 b"midnightblue" => rgb!(25, 25, 112),
719 b"mintcream" => rgb!(245, 255, 250),
720 b"mistyrose" => rgb!(255, 228, 225),
721 b"moccasin" => rgb!(255, 228, 181),
722 b"navajowhite" => rgb!(255, 222, 173),
723 b"navy" => rgb!(0, 0, 128),
724 b"oldlace" => rgb!(253, 245, 230),
725 b"olive" => rgb!(128, 128, 0),
726 b"olivedrab" => rgb!(107, 142, 35),
727 b"orange" => rgb!(255, 165, 0),
728 b"orangered" => rgb!(255, 69, 0),
729 b"orchid" => rgb!(218, 112, 214),
730 b"palegoldenrod" => rgb!(238, 232, 170),
731 b"palegreen" => rgb!(152, 251, 152),
732 b"paleturquoise" => rgb!(175, 238, 238),
733 b"palevioletred" => rgb!(219, 112, 147),
734 b"papayawhip" => rgb!(255, 239, 213),
735 b"peachpuff" => rgb!(255, 218, 185),
736 b"peru" => rgb!(205, 133, 63),
737 b"pink" => rgb!(255, 192, 203),
738 b"plum" => rgb!(221, 160, 221),
739 b"powderblue" => rgb!(176, 224, 230),
740 b"purple" => rgb!(128, 0, 128),
741 b"rebeccapurple" => rgb!(102, 51, 153),
742 b"red" => rgb!(255, 0, 0),
743 b"rosybrown" => rgb!(188, 143, 143),
744 b"royalblue" => rgb!(65, 105, 225),
745 b"saddlebrown" => rgb!(139, 69, 19),
746 b"salmon" => rgb!(250, 128, 114),
747 b"sandybrown" => rgb!(244, 164, 96),
748 b"seagreen" => rgb!(46, 139, 87),
749 b"seashell" => rgb!(255, 245, 238),
750 b"sienna" => rgb!(160, 82, 45),
751 b"silver" => rgb!(192, 192, 192),
752 b"skyblue" => rgb!(135, 206, 235),
753 b"slateblue" => rgb!(106, 90, 205),
754 b"slategray" => rgb!(112, 128, 144),
755 b"slategrey" => rgb!(112, 128, 144),
756 b"snow" => rgb!(255, 250, 250),
757 b"springgreen" => rgb!(0, 255, 127),
758 b"steelblue" => rgb!(70, 130, 180),
759 b"tan" => rgb!(210, 180, 140),
760 b"teal" => rgb!(0, 128, 128),
761 b"thistle" => rgb!(216, 191, 216),
762 b"tomato" => rgb!(255, 99, 71),
763 b"turquoise" => rgb!(64, 224, 208),
764 b"violet" => rgb!(238, 130, 238),
765 b"wheat" => rgb!(245, 222, 179),
766 b"white" => rgb!(255, 255, 255),
767 b"whitesmoke" => rgb!(245, 245, 245),
768 b"yellow" => rgb!(255, 255, 0),
769 b"yellowgreen" => rgb!(154, 205, 50),
770 b"transparent" => Srgb::new(0., 0., 0., 0.),
771 _ => return Err(()),
772 })
773}
774
775#[cfg(test)]
776mod tests;