1#![allow(rustdoc::invalid_html_tags)]
13
14pub const OPAQUE: f32 = 1.0;
16
17use crate::{BasicParseError, Parser, ToCss, Token};
18use std::fmt;
19
20#[inline]
37pub fn clamp_unit_f32(val: f32) -> u8 {
38 clamp_floor_256_f32(val * 255.)
39}
40
41#[inline]
43pub fn clamp_floor_256_f32(val: f32) -> u8 {
44 val.round().clamp(0., 255.) as u8
45}
46
47#[inline]
50pub fn serialize_color_alpha(
51 dest: &mut impl fmt::Write,
52 alpha: Option<f32>,
53 legacy_syntax: bool,
54) -> fmt::Result {
55 let alpha = match alpha {
56 None => return dest.write_str(" / none"),
57 Some(a) => a,
58 };
59
60 if alpha == OPAQUE {
62 return Ok(());
63 }
64
65 dest.write_str(if legacy_syntax { ", " } else { " / " })?;
66
67 let mut rounded_alpha = (alpha * 100.).round() / 100.;
69 if clamp_unit_f32(rounded_alpha) != clamp_unit_f32(alpha) {
70 rounded_alpha = (alpha * 1000.).round() / 1000.;
71 }
72
73 rounded_alpha.to_css(dest)
74}
75
76#[derive(Clone, Copy, Eq, PartialEq, Debug)]
79#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
80#[cfg_attr(feature = "serde", serde(tag = "type"))]
81pub enum PredefinedColorSpace {
82 Srgb,
84 SrgbLinear,
86 DisplayP3,
88 DisplayP3Linear,
90 A98Rgb,
92 ProphotoRgb,
94 Rec2020,
96 XyzD50,
98 XyzD65,
100}
101
102impl PredefinedColorSpace {
103 pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, BasicParseError<'i>> {
105 let location = input.current_source_location();
106
107 let ident = input.expect_ident()?;
108 Ok(match_ignore_ascii_case! { ident,
109 "srgb" => Self::Srgb,
110 "srgb-linear" => Self::SrgbLinear,
111 "display-p3" => Self::DisplayP3,
112 "display-p3-linear" => Self::DisplayP3Linear,
113 "a98-rgb" => Self::A98Rgb,
114 "prophoto-rgb" => Self::ProphotoRgb,
115 "rec2020" => Self::Rec2020,
116 "xyz-d50" => Self::XyzD50,
117 "xyz" | "xyz-d65" => Self::XyzD65,
118 _ => return Err(location.new_basic_unexpected_token_error(Token::Ident(ident.clone()))),
119 })
120 }
121}
122
123impl ToCss for PredefinedColorSpace {
124 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
125 where
126 W: fmt::Write,
127 {
128 dest.write_str(match self {
129 Self::Srgb => "srgb",
130 Self::SrgbLinear => "srgb-linear",
131 Self::DisplayP3 => "display-p3",
132 Self::DisplayP3Linear => "display-p3-linear",
133 Self::A98Rgb => "a98-rgb",
134 Self::ProphotoRgb => "prophoto-rgb",
135 Self::Rec2020 => "rec2020",
136 Self::XyzD50 => "xyz-d50",
137 Self::XyzD65 => "xyz-d65",
138 })
139 }
140}
141
142#[allow(clippy::result_unit_err)]
144#[inline]
145pub fn parse_hash_color(value: &[u8]) -> Result<(u8, u8, u8, f32), ()> {
146 Ok(match value.len() {
147 8 => (
148 from_hex(value[0])? * 16 + from_hex(value[1])?,
149 from_hex(value[2])? * 16 + from_hex(value[3])?,
150 from_hex(value[4])? * 16 + from_hex(value[5])?,
151 (from_hex(value[6])? * 16 + from_hex(value[7])?) as f32 / 255.0,
152 ),
153 6 => (
154 from_hex(value[0])? * 16 + from_hex(value[1])?,
155 from_hex(value[2])? * 16 + from_hex(value[3])?,
156 from_hex(value[4])? * 16 + from_hex(value[5])?,
157 OPAQUE,
158 ),
159 4 => (
160 from_hex(value[0])? * 17,
161 from_hex(value[1])? * 17,
162 from_hex(value[2])? * 17,
163 (from_hex(value[3])? * 17) as f32 / 255.0,
164 ),
165 3 => (
166 from_hex(value[0])? * 17,
167 from_hex(value[1])? * 17,
168 from_hex(value[2])? * 17,
169 OPAQUE,
170 ),
171 _ => return Err(()),
172 })
173}
174
175ascii_case_insensitive_phf_map! {
176 named_colors -> (u8, u8, u8) = {
177 "black" => (0, 0, 0),
178 "silver" => (192, 192, 192),
179 "gray" => (128, 128, 128),
180 "white" => (255, 255, 255),
181 "maroon" => (128, 0, 0),
182 "red" => (255, 0, 0),
183 "purple" => (128, 0, 128),
184 "fuchsia" => (255, 0, 255),
185 "green" => (0, 128, 0),
186 "lime" => (0, 255, 0),
187 "olive" => (128, 128, 0),
188 "yellow" => (255, 255, 0),
189 "navy" => (0, 0, 128),
190 "blue" => (0, 0, 255),
191 "teal" => (0, 128, 128),
192 "aqua" => (0, 255, 255),
193
194 "aliceblue" => (240, 248, 255),
195 "antiquewhite" => (250, 235, 215),
196 "aquamarine" => (127, 255, 212),
197 "azure" => (240, 255, 255),
198 "beige" => (245, 245, 220),
199 "bisque" => (255, 228, 196),
200 "blanchedalmond" => (255, 235, 205),
201 "blueviolet" => (138, 43, 226),
202 "brown" => (165, 42, 42),
203 "burlywood" => (222, 184, 135),
204 "cadetblue" => (95, 158, 160),
205 "chartreuse" => (127, 255, 0),
206 "chocolate" => (210, 105, 30),
207 "coral" => (255, 127, 80),
208 "cornflowerblue" => (100, 149, 237),
209 "cornsilk" => (255, 248, 220),
210 "crimson" => (220, 20, 60),
211 "cyan" => (0, 255, 255),
212 "darkblue" => (0, 0, 139),
213 "darkcyan" => (0, 139, 139),
214 "darkgoldenrod" => (184, 134, 11),
215 "darkgray" => (169, 169, 169),
216 "darkgreen" => (0, 100, 0),
217 "darkgrey" => (169, 169, 169),
218 "darkkhaki" => (189, 183, 107),
219 "darkmagenta" => (139, 0, 139),
220 "darkolivegreen" => (85, 107, 47),
221 "darkorange" => (255, 140, 0),
222 "darkorchid" => (153, 50, 204),
223 "darkred" => (139, 0, 0),
224 "darksalmon" => (233, 150, 122),
225 "darkseagreen" => (143, 188, 143),
226 "darkslateblue" => (72, 61, 139),
227 "darkslategray" => (47, 79, 79),
228 "darkslategrey" => (47, 79, 79),
229 "darkturquoise" => (0, 206, 209),
230 "darkviolet" => (148, 0, 211),
231 "deeppink" => (255, 20, 147),
232 "deepskyblue" => (0, 191, 255),
233 "dimgray" => (105, 105, 105),
234 "dimgrey" => (105, 105, 105),
235 "dodgerblue" => (30, 144, 255),
236 "firebrick" => (178, 34, 34),
237 "floralwhite" => (255, 250, 240),
238 "forestgreen" => (34, 139, 34),
239 "gainsboro" => (220, 220, 220),
240 "ghostwhite" => (248, 248, 255),
241 "gold" => (255, 215, 0),
242 "goldenrod" => (218, 165, 32),
243 "greenyellow" => (173, 255, 47),
244 "grey" => (128, 128, 128),
245 "honeydew" => (240, 255, 240),
246 "hotpink" => (255, 105, 180),
247 "indianred" => (205, 92, 92),
248 "indigo" => (75, 0, 130),
249 "ivory" => (255, 255, 240),
250 "khaki" => (240, 230, 140),
251 "lavender" => (230, 230, 250),
252 "lavenderblush" => (255, 240, 245),
253 "lawngreen" => (124, 252, 0),
254 "lemonchiffon" => (255, 250, 205),
255 "lightblue" => (173, 216, 230),
256 "lightcoral" => (240, 128, 128),
257 "lightcyan" => (224, 255, 255),
258 "lightgoldenrodyellow" => (250, 250, 210),
259 "lightgray" => (211, 211, 211),
260 "lightgreen" => (144, 238, 144),
261 "lightgrey" => (211, 211, 211),
262 "lightpink" => (255, 182, 193),
263 "lightsalmon" => (255, 160, 122),
264 "lightseagreen" => (32, 178, 170),
265 "lightskyblue" => (135, 206, 250),
266 "lightslategray" => (119, 136, 153),
267 "lightslategrey" => (119, 136, 153),
268 "lightsteelblue" => (176, 196, 222),
269 "lightyellow" => (255, 255, 224),
270 "limegreen" => (50, 205, 50),
271 "linen" => (250, 240, 230),
272 "magenta" => (255, 0, 255),
273 "mediumaquamarine" => (102, 205, 170),
274 "mediumblue" => (0, 0, 205),
275 "mediumorchid" => (186, 85, 211),
276 "mediumpurple" => (147, 112, 219),
277 "mediumseagreen" => (60, 179, 113),
278 "mediumslateblue" => (123, 104, 238),
279 "mediumspringgreen" => (0, 250, 154),
280 "mediumturquoise" => (72, 209, 204),
281 "mediumvioletred" => (199, 21, 133),
282 "midnightblue" => (25, 25, 112),
283 "mintcream" => (245, 255, 250),
284 "mistyrose" => (255, 228, 225),
285 "moccasin" => (255, 228, 181),
286 "navajowhite" => (255, 222, 173),
287 "oldlace" => (253, 245, 230),
288 "olivedrab" => (107, 142, 35),
289 "orange" => (255, 165, 0),
290 "orangered" => (255, 69, 0),
291 "orchid" => (218, 112, 214),
292 "palegoldenrod" => (238, 232, 170),
293 "palegreen" => (152, 251, 152),
294 "paleturquoise" => (175, 238, 238),
295 "palevioletred" => (219, 112, 147),
296 "papayawhip" => (255, 239, 213),
297 "peachpuff" => (255, 218, 185),
298 "peru" => (205, 133, 63),
299 "pink" => (255, 192, 203),
300 "plum" => (221, 160, 221),
301 "powderblue" => (176, 224, 230),
302 "rebeccapurple" => (102, 51, 153),
303 "rosybrown" => (188, 143, 143),
304 "royalblue" => (65, 105, 225),
305 "saddlebrown" => (139, 69, 19),
306 "salmon" => (250, 128, 114),
307 "sandybrown" => (244, 164, 96),
308 "seagreen" => (46, 139, 87),
309 "seashell" => (255, 245, 238),
310 "sienna" => (160, 82, 45),
311 "skyblue" => (135, 206, 235),
312 "slateblue" => (106, 90, 205),
313 "slategray" => (112, 128, 144),
314 "slategrey" => (112, 128, 144),
315 "snow" => (255, 250, 250),
316 "springgreen" => (0, 255, 127),
317 "steelblue" => (70, 130, 180),
318 "tan" => (210, 180, 140),
319 "thistle" => (216, 191, 216),
320 "tomato" => (255, 99, 71),
321 "turquoise" => (64, 224, 208),
322 "violet" => (238, 130, 238),
323 "wheat" => (245, 222, 179),
324 "whitesmoke" => (245, 245, 245),
325 "yellowgreen" => (154, 205, 50),
326 }
327}
328
329#[allow(clippy::result_unit_err)]
332#[inline]
333pub fn parse_named_color(ident: &str) -> Result<(u8, u8, u8), ()> {
334 named_colors::get(ident).copied().ok_or(())
335}
336
337#[inline]
340pub fn all_named_colors() -> impl Iterator<Item = (&'static str, (u8, u8, u8))> {
341 named_colors::entries().map(|(k, v)| (*k, *v))
342}
343
344#[inline]
345fn from_hex(c: u8) -> Result<u8, ()> {
346 match c {
347 b'0'..=b'9' => Ok(c - b'0'),
348 b'a'..=b'f' => Ok(c - b'a' + 10),
349 b'A'..=b'F' => Ok(c - b'A' + 10),
350 _ => Err(()),
351 }
352}