Skip to content

Commit 372b2e3

Browse files
color: Support CSS Color Level 4 rgb & hsl syntax (#113)
1 parent be2503e commit 372b2e3

File tree

4 files changed

+11873
-34
lines changed

4 files changed

+11873
-34
lines changed

src/color.rs

+83-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::cmp;
66
use std::fmt;
7+
use std::f64::consts::PI;
78

89
use super::{Token, Parser, ToCss};
910
use tokenizer::NumericValue;
@@ -386,47 +387,84 @@ fn clamp_f32(val: f32) -> u8 {
386387

387388
#[inline]
388389
fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
389-
let (is_rgb, has_alpha) = match_ignore_ascii_case! { name,
390-
"rgba" => (true, true),
391-
"rgb" => (true, false),
392-
"hsl" => (false, false),
393-
"hsla" => (false, true),
390+
let is_rgb = match_ignore_ascii_case! { name,
391+
"rgba" => true,
392+
"rgb" => true,
393+
"hsl" => false,
394+
"hsla" => false,
394395
_ => return Err(())
395396
};
396397

397398
let red: u8;
398399
let green: u8;
399400
let blue: u8;
401+
let mut uses_commas = false;
400402
if is_rgb {
401403
// Either integers or percentages, but all the same type.
402404
// https://drafts.csswg.org/css-color/#rgb-functions
403405
match try!(arguments.next()) {
404-
Token::Number(NumericValue { int_value: Some(v), .. }) => {
405-
red = clamp_i32(v);
406-
try!(arguments.expect_comma());
407-
green = clamp_i32(try!(arguments.expect_integer()));
408-
try!(arguments.expect_comma());
409-
blue = clamp_i32(try!(arguments.expect_integer()));
406+
Token::Number(NumericValue { value: v, .. }) => {
407+
red = clamp_i32(v as i32);
408+
green = clamp_i32(match try!(arguments.next()) {
409+
Token::Number(NumericValue { value: v, .. }) => v,
410+
Token::Comma => {
411+
uses_commas = true;
412+
try!(arguments.expect_number())
413+
}
414+
_ => return Err(())
415+
} as i32);
416+
if uses_commas {
417+
try!(arguments.expect_comma());
418+
}
419+
blue = clamp_i32(try!(arguments.expect_number()) as i32);
410420
}
411421
Token::Percentage(ref v) => {
412422
red = clamp_f32(v.unit_value);
413-
try!(arguments.expect_comma());
414-
green = clamp_f32(try!(arguments.expect_percentage()));
415-
try!(arguments.expect_comma());
423+
green = clamp_f32(match try!(arguments.next()) {
424+
Token::Percentage(ref v) => v.unit_value,
425+
Token::Comma => {
426+
uses_commas = true;
427+
try!(arguments.expect_percentage())
428+
}
429+
_ => return Err(())
430+
});
431+
if uses_commas {
432+
try!(arguments.expect_comma());
433+
}
416434
blue = clamp_f32(try!(arguments.expect_percentage()));
417435
}
418436
_ => return Err(())
419437
};
420438
} else {
421-
let hue_degrees = try!(arguments.expect_number());
439+
let hue_degrees = match try!(arguments.next()) {
440+
Token::Number(NumericValue { value: v, .. }) => v,
441+
Token::Dimension(NumericValue { value: v, .. }, unit) => {
442+
match &*unit {
443+
"deg" => v,
444+
"grad" => v * 360. / 400.,
445+
"rad" => v * 360. / (2. * PI as f32),
446+
"turn" => v * 360.,
447+
_ => return Err(())
448+
}
449+
}
450+
_ => return Err(())
451+
};
422452
// Subtract an integer before rounding, to avoid some rounding errors:
423453
let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor();
424454
let hue = hue_normalized_degrees / 360.;
425455
// Saturation and lightness are clamped to 0% ... 100%
426456
// https://drafts.csswg.org/css-color/#the-hsl-notation
427-
try!(arguments.expect_comma());
428-
let saturation = try!(arguments.expect_percentage()).max(0.).min(1.);
429-
try!(arguments.expect_comma());
457+
let saturation = match try!(arguments.next()) {
458+
Token::Percentage(ref v) => v.unit_value,
459+
Token::Comma => {
460+
uses_commas = true;
461+
try!(arguments.expect_percentage())
462+
}
463+
_ => return Err(())
464+
}.max(0.).min(1.);
465+
if uses_commas {
466+
try!(arguments.expect_comma());
467+
}
430468
let lightness = try!(arguments.expect_percentage()).max(0.).min(1.);
431469

432470
// https://drafts.csswg.org/css-color/#hsl-color
@@ -449,12 +487,38 @@ fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()>
449487
blue = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 - 1.));
450488
}
451489

490+
let alpha = if !arguments.is_exhausted() {
491+
if uses_commas {
492+
try!(arguments.expect_comma());
493+
} else {
494+
match try!(arguments.next()) {
495+
Token::Delim('/') => {},
496+
_ => return Err(())
497+
};
498+
};
499+
let token = try!(arguments.next());
500+
match token {
501+
Token::Number(NumericValue { value: v, .. }) => {
502+
clamp_f32(v)
503+
}
504+
Token::Percentage(ref v) => {
505+
clamp_f32(v.unit_value)
506+
}
507+
_ => {
508+
return Err(())
509+
}
510+
}
511+
} else {
512+
255
513+
};
514+
515+
/*
452516
let alpha = if has_alpha {
453517
try!(arguments.expect_comma());
454518
clamp_f32(try!(arguments.expect_number()))
455519
} else {
456520
255
457-
};
521+
};*/
458522
try!(arguments.expect_exhausted());
459523
rgba(red, green, blue, alpha)
460524
}

src/css-parsing-tests/color3.json

+120-11
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@
6565
"rgb()", null,
6666
"rgb(0)", null,
6767
"rgb(0, 0)", null,
68-
"rgb(0, 0, 0, 0)", null,
68+
"rgb(0, 0, 0, 0)", [0, 0, 0, 0],
6969
"rgb(0%)", null,
7070
"rgb(0%, 0%)", null,
71-
"rgb(0%, 0%, 0%, 0%)", null,
72-
"rgb(0%, 0%, 0%, 0)", null,
71+
"rgb(0%, 0%, 0%, 0%)", [0, 0, 0, 0],
72+
"rgb(0%, 0%, 0%, 0)", [0, 0, 0, 0],
7373

7474
"rgba(0, 0, 0, 0)", [0, 0, 0, 0],
75+
"rgba(.3, -1.4, -0.001e2, 0)", [0, 0, 0, 0],
7576
"rgba(204, 0, 102, 0.25)", [204, 0, 102, 64],
7677
"RGBA(255,255,255, 0)", [255, 255, 255, 0],
7778
"rgBA(0, 51,255, 1)", [0, 51, 255, 255],
@@ -92,20 +93,20 @@
9293
"rgba(0%, 20%, 100%, -0.1)", [0, 51, 255, 0],
9394
"rgba(0%, 20%, 100%, -139)", [0, 51, 255, 0],
9495

95-
"rgba(255,255,255, 0%)", null,
96+
"rgba(255,255,255, 0%)", [255, 255, 255, 0],
9697
"rgba(10%, 50%, 0, 1)", null,
9798
"rgba(255, 50%, 0%, 1)", null,
9899
"rgba(0, 0, 0 0)", null,
99100
"rgba(0, 0, 0, 0deg)", null,
100101
"rgba(0, 0, 0, light)", null,
101102
"rgba()", null,
102103
"rgba(0)", null,
103-
"rgba(0, 0, 0)", null,
104+
"rgba(0, 0, 0)", [0, 0, 0, 255],
104105
"rgba(0, 0, 0, 0, 0)", null,
105106
"rgba(0%)", null,
106107
"rgba(0%, 0%)", null,
107-
"rgba(0%, 0%, 0%)", null,
108-
"rgba(0%, 0%, 0%, 0%)", null,
108+
"rgba(0%, 0%, 0%)", [0, 0, 0, 255],
109+
"rgba(0%, 0%, 0%, 0%)", [0, 0, 0, 0],
109110
"rgba(0%, 0%, 0%, 0%, 0%)", null,
110111

111112
"HSL(0, 0%, 0%)", [0, 0, 0, 255],
@@ -118,12 +119,12 @@
118119
"hsl(10, 50%, 0)", null,
119120
"hsl(50%, 50%, 0%)", null,
120121
"hsl(0, 0% 0%)", null,
121-
"hsl(30deg, 100%, 100%)", null,
122+
"hsl(30deg, 100%, 100%)", [255, 255, 255, 255],
122123
"hsl(0, 0%, light)", null,
123124
"hsl()", null,
124125
"hsl(0)", null,
125126
"hsl(0, 0%)", null,
126-
"hsl(0, 0%, 0%, 0%)", null,
127+
"hsl(0, 0%, 0%, 0%)", [0, 0, 0, 0],
127128

128129
"HSLA(-300, 100%, 37.5%, 1)", [192, 192, 0, 255],
129130
"hsLA(-300, 100%, 37.5%, 12)", [192, 192, 0, 255],
@@ -134,13 +135,121 @@
134135
"hsla(10, 50%, 0, 1)", null,
135136
"hsla(50%, 50%, 0%, 1)", null,
136137
"hsla(0, 0% 0%, 1)", null,
137-
"hsla(30deg, 100%, 100%, 1)", null,
138+
"hsla(30deg, 100%, 100%, 1)", [255, 255, 255, 255],
138139
"hsla(0, 0%, light, 1)", null,
139140
"hsla()", null,
140141
"hsla(0)", null,
141142
"hsla(0, 0%)", null,
142-
"hsla(0, 0%, 0%, 50%)", null,
143+
"hsla(0, 0%, 0%, 50%)", [0, 0, 0, 128],
143144
"hsla(0, 0%, 0%, 255, 0%)", null,
144145

146+
"rgb(0 0 0 0)", null,
147+
"rgb(0 0 0 / 0)", [0, 0, 0, 0],
148+
"rgb(0%)", null,
149+
"rgb(0% 0%)", null,
150+
"rgb(0% 0% 0% / 0%)", [0, 0, 0, 0],
151+
"rgb(0% 0% 0% / 0)", [0, 0, 0, 0],
152+
153+
"rgba(0%)", null,
154+
"rgba(0% 0%)", null,
155+
"rgba(0% 0% 0%)", [0, 0, 0, 255],
156+
"rgba(0% 0% 0% / 0%)", [0, 0, 0, 0],
157+
"rgba(0% 0% 0% / 0% 0%)", null,
158+
159+
"rgb(0, 0 0 0)", null,
160+
"rgb(0 0, 0 0)", null,
161+
"rgb(0 0 0, 0)", null,
162+
"rgb(0, 0, 0 0)", null,
163+
164+
"rgba(0%, 0% 0%)", null,
165+
"rgba(0% 0% 0%, 0%)", null,
166+
167+
"HSL(0 0% 0%)", [0, 0, 0, 255],
168+
"hsL(0 100% 50%)", [255, 0, 0, 255],
169+
"hsl(60 100% 37.5%)", [192, 192, 0, 255],
170+
171+
"HSLA(-300 100% 37.5% /1)", [192, 192, 0, 255],
172+
"hsLA(-300 100% 37.5% /12)", [192, 192, 0, 255],
173+
174+
"hsl(0, 0 0 0)", null,
175+
"hsl(0 0, 0 0)", null,
176+
"hsl(0 0 0, 0)", null,
177+
"hsl(0, 0, 0 0)", null,
178+
179+
"hsla(0%, 0% 0%)", null,
180+
"hsla(0% 0% 0%, 0%)", null,
181+
182+
"hsla(120.0, 75%, 50%, 20%)", [32, 224, 32, 51],
183+
"hsla(120, 75%, 50%, 0.4)", [32, 224, 32, 102],
184+
"hsla(120 75% 50% / 60%)", [32, 224, 32, 153],
185+
"hsla(120.0 75% 50% / 1.0)", [32, 224, 32, 255],
186+
"hsla(120/* comment */75%/* comment */50%/1.0)", [32, 224, 32, 255],
187+
"hsla(120,/* comment */75%,/* comment */50%,100%)", [32, 224, 32, 255],
188+
"hsla(120.0, 75%, 50%)", [32, 224, 32, 255],
189+
"hsla(120 75% 50%)", [32, 224, 32, 255],
190+
"hsla(120/* comment */75%/* comment */50%)", [32, 224, 32, 255],
191+
"hsla(120/* comment */,75%,/* comment */50%)", [32, 224, 32, 255],
192+
"hsl(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
193+
"hsl(120, 75%, 50%, 40%)", [32, 224, 32, 102],
194+
"hsl(120 75% 50% / 0.6)", [32, 224, 32, 153],
195+
"hsl(120 75% 50% / 80%)", [32, 224, 32, 204],
196+
"hsl(120/* comment */75%/* comment */50%/1.0)", [32, 224, 32, 255],
197+
"hsl(120/* comment */75%/* comment */50%/100%)", [32, 224, 32, 255],
198+
"hsl(120,/* comment */75%,/* comment */50%,1.0)", [32, 224, 32, 255],
199+
"hsl(120,/* comment */75%,/* comment */50%,100%)", [32, 224, 32, 255],
200+
"hsl(120/* comment */75%/* comment */50%)", [32, 224, 32, 255],
201+
"hsl(120/* comment */,75%,/* comment */50%)", [32, 224, 32, 255],
202+
"hsla(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
203+
"hsl(240, 75%, 50%)", [32, 32, 224, 255],
204+
"hsla(120, 75%, 50%)", [32, 224, 32, 255],
205+
"hsla(120.0, 75%, 50%)", [32, 224, 32, 255],
206+
"hsla(1.2e2, 75%, 50%)", [32, 224, 32, 255],
207+
"hsla(1.2E2, 75%, 50%)", [32, 224, 32, 255],
208+
"hsla(60, 75%, 50%)", [224, 224, 32, 255],
209+
"hsl(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
210+
"hsl(120.0, 75%, 50%, 0.4)", [32, 224, 32, 102],
211+
"hsl(1.2e2, 75%, 50%, 0.6)", [32, 224, 32, 153],
212+
"hsl(1.2E2, 75%, 50%, 0.8)", [32, 224, 32, 204],
213+
"hsl(60.0, 75%, 50%, 1.0)", [224, 224, 32, 255],
214+
"rgb(10%, 60%, 10%, 20%)", [25, 153, 25, 51],
215+
"rgb(10, 175, 10, 0.4)", [10, 175, 10, 102],
216+
"rgb(10 175 10 / 60%)", [10, 175, 10, 153],
217+
"rgb(10.0 175.0 10.0 / 0.8)", [10, 175, 10, 204],
218+
"rgb(10/* comment */175/* comment */10/100%)", [10, 175, 10, 255],
219+
"rgb(10,/* comment */150,/* comment */50)", [10, 150, 50, 255],
220+
"rgb(10%, 60%, 10%)", [25, 153, 25, 255],
221+
"rgb(10.0 100.0 100.0)", [10, 100, 100, 255],
222+
"rgb(10/* comment */75/* comment */125)", [10, 75, 125, 255],
223+
"rgb(10.0, 50.0, 150.0)", [10, 50, 150, 255],
224+
"rgba(10.0, 175.0, 10.0, 0.2)", [10, 175, 10, 51],
225+
"rgba(10, 175, 10, 40%)", [10, 175, 10, 102],
226+
"rgba(10% 75% 10% / 0.6)", [25, 192, 25, 153],
227+
"rgba(10 175 10 / 80%)", [10, 175, 10, 204],
228+
"rgba(10/* comment */175/* comment */10/100%)", [10, 175, 10, 255],
229+
"rgba(10,/* comment */150,/* comment */50)", [10, 150, 50, 255],
230+
"rgba(10.0, 125.0, 75.0)", [10, 125, 75, 255],
231+
"rgba(10%, 45%, 45%)", [25, 115, 115, 255],
232+
"rgba(10/* comment */75/* comment */125)", [10, 75, 125, 255],
233+
"rgba(10.0, 50.0, 150.0)", [10, 50, 150, 255],
234+
"rgb(10, 175, 10, 0.2)", [10, 175, 10, 51],
235+
"rgb(10, 175, 10, 0.4)", [10, 175, 10, 102],
236+
"rgb(10, 175, 10, 0.6)", [10, 175, 10, 153],
237+
"rgb(10%, 70%, 10%, 0.8)", [25, 179, 25, 204],
238+
"rgb(10%, 70%, 10%, 1.0)", [25, 179, 25, 255],
239+
"rgba(10, 150, 50)", [10, 150, 50, 255],
240+
"rgba(10, 125, 75)", [10, 125, 75, 255],
241+
"rgba(10%,40%, 40%)", [25, 102, 102, 255],
242+
"rgba(10%, 45%, 50%)", [25, 115, 128, 255],
243+
"rgba(10%, 50%, 60%)", [25, 128, 153, 255],
244+
245+
"hsla(120deg, 75%, 50%, 0.4)", [32, 224, 32, 102],
246+
"hsla(133.33333333grad, 75%, 50%, 0.6)", [32, 224, 32, 153],
247+
"hsla(2.0943951024rad, 75%, 50%, 0.8)", [32, 224, 32, 204],
248+
"hsla(0.3333333333turn, 75%, 50%, 1.0)", [32, 224, 32, 255],
249+
"hsl(600deg, 75%, 50%)", [32, 32, 224, 255],
250+
"hsl(1066.66666666grad, 75%, 50%)", [32, 32, 224, 255],
251+
"hsl(10.4719755118rad, 75%, 50%)", [32, 32, 224, 255],
252+
"hsl(2.6666666666turn, 75%, 50%)", [32, 32, 224, 255],
253+
145254
"cmyk(0, 0, 0, 0)", null
146255
]

0 commit comments

Comments
 (0)