Skip to content

Commit a5ee51d

Browse files
committed
Complete <color> parsing
1 parent c9a4964 commit a5ee51d

File tree

1 file changed

+98
-9
lines changed

1 file changed

+98
-9
lines changed

color.rs

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ pub enum Color {
1616
// Return None on invalid/unsupported value (not a color)
1717
pub fn parse_color(component_value: &ComponentValue) -> Option<Color> {
1818
match *component_value {
19-
Hash(ref value) | IDHash(ref value) => parse_hash_color(*value),
20-
Ident(ref value) => parse_keyword_color(*value),
21-
Function(ref name, ref arguments) => parse_function_color(*name, *arguments),
19+
Hash(ref value) | IDHash(ref value) => parse_color_hash(*value),
20+
Ident(ref value) => parse_color_keyword(*value),
21+
Function(ref name, ref arguments) => parse_color_function(*name, *arguments),
2222
_ => None
2323
}
2424
}
2525

2626

2727
#[inline]
28-
fn parse_keyword_color(value: &str) -> Option<Color> {
28+
fn parse_color_keyword(value: &str) -> Option<Color> {
2929
let lower_value = to_ascii_lower(value);
3030
match COLOR_KEYWORDS.bsearch_elem(&lower_value.as_slice()) {
3131
Some(index) => Some(COLOR_VALUES[index]),
@@ -36,7 +36,7 @@ fn parse_keyword_color(value: &str) -> Option<Color> {
3636

3737

3838
#[inline]
39-
fn parse_hash_color(value: &str) -> Option<Color> {
39+
fn parse_color_hash(value: &str) -> Option<Color> {
4040
macro_rules! from_hex(
4141
($c: expr) => {{
4242
let c = $c;
@@ -72,9 +72,98 @@ fn parse_hash_color(value: &str) -> Option<Color> {
7272

7373

7474
#[inline]
75-
fn parse_function_color(name: &str, arguments: &[(ComponentValue, SourceLocation)])
75+
fn parse_color_function(name: &str, arguments: &[(ComponentValue, SourceLocation)])
7676
-> Option<Color> {
77-
let _lower_name = to_ascii_lower(name);
78-
let _iter = arguments.iter();
79-
None // TODO
77+
let lower_name = to_ascii_lower(name);
78+
79+
let (is_rgb, has_alpha) =
80+
if "rgba" == lower_name { (true, true) }
81+
else if "rgb" == lower_name { (true, false) }
82+
else if "hsl" == lower_name { (false, false) }
83+
else if "hsla" == lower_name { (false, true) }
84+
else { return None };
85+
86+
let mut iter = do arguments.iter().filter_map |&(ref c, _)| {
87+
if c != &WhiteSpace { Some(c) } else { None }
88+
};
89+
macro_rules! expect_comma(
90+
() => ( if iter.next() != Some(&Comma) { return None } );
91+
)
92+
macro_rules! expect_percentage(
93+
() => ( match iter.next() {
94+
Some(&Percentage(ref v)) => v.value,
95+
_ => return None,
96+
});
97+
)
98+
macro_rules! expect_integer(
99+
() => ( match iter.next() {
100+
Some(&Number(ref v)) if v.int_value.is_some() => v.value,
101+
_ => return None,
102+
});
103+
)
104+
macro_rules! expect_number(
105+
() => ( match iter.next() {
106+
Some(&Number(ref v)) => v.value,
107+
_ => return None,
108+
});
109+
)
110+
111+
let red: ColorFloat;
112+
let green: ColorFloat;
113+
let blue: ColorFloat;
114+
if is_rgb {
115+
// Either integers or percentages, but all the same type.
116+
match iter.next() {
117+
Some(&Number(ref v)) if v.int_value.is_some() => {
118+
red = v.value / 255.;
119+
expect_comma!();
120+
green = expect_integer!() / 255.;
121+
expect_comma!();
122+
blue = expect_integer!() / 255.;
123+
}
124+
Some(&Percentage(ref v)) => {
125+
red = v.value / 100.;
126+
expect_comma!();
127+
green = expect_percentage!() / 100.;
128+
expect_comma!();
129+
blue = expect_percentage!() / 100.;
130+
}
131+
_ => return None
132+
};
133+
} else {
134+
let hue: ColorFloat = expect_number!() / 360.;
135+
let hue = hue - hue.floor();
136+
expect_comma!();
137+
let saturation: ColorFloat = (expect_percentage!() / 100.).max(&0.).min(&1.);
138+
expect_comma!();
139+
let lightness: ColorFloat = (expect_percentage!() / 100.).max(&0.).min(&1.);
140+
141+
// http://www.w3.org/TR/css3-color/#hsl-color
142+
fn hue_to_rgb(m1: ColorFloat, m2: ColorFloat, mut h: ColorFloat) -> ColorFloat {
143+
if h < 0. { h += 1. }
144+
if h > 1. { h -= 1. }
145+
146+
if h * 6. < 1. { m1 + (m2 - m1) * h * 6. }
147+
else if h * 2. < 1. { m2 }
148+
else if h * 3. < 2. { m1 + (m2 - m1) * (2. / 3. - h) * 6. }
149+
else { m1 }
150+
}
151+
let m2 = if lightness <= 0.5 { lightness * (saturation + 1.) }
152+
else { lightness + saturation - lightness * saturation };
153+
let m1 = lightness * 2. - m2;
154+
red = hue_to_rgb(m1, m2, hue + 1. / 3.);
155+
green = hue_to_rgb(m1, m2, hue);
156+
blue = hue_to_rgb(m1, m2, hue - 1. / 3.);
157+
}
158+
159+
let alpha = if has_alpha {
160+
expect_comma!();
161+
match iter.next() {
162+
Some(&Number(ref a)) => a.value.max(&0.).min(&1.),
163+
_ => return None
164+
}
165+
} else {
166+
1.
167+
};
168+
if iter.next().is_none() { Some(RGBA(red, green, blue, alpha)) } else { None }
80169
}

0 commit comments

Comments
 (0)