diff --git a/src/servo/dom/style.rs b/src/servo/dom/style.rs index f754ab908b9..7e193fd13e5 100644 --- a/src/servo/dom/style.rs +++ b/src/servo/dom/style.rs @@ -1,5 +1,5 @@ -import io::println; - +import util::color::{Color, methods}; +import util::color::css_colors::black; enum display_type{ di_block, @@ -10,8 +10,8 @@ enum display_type{ enum style_decl{ font_size(uint), // Currently assumes format '# pt' display(display_type), - text_color(uint), - background_color(uint) + text_color(Color), + background_color(Color) } enum attr{ @@ -75,32 +75,32 @@ fn print_display(dis_ty : display_type) -> str { fn print_style(decl : style_decl) -> str{ alt decl { - font_size(s) { #fmt("Font size = %u pt", s) } - display(dis_ty) { #fmt("Display style = %s", print_display(dis_ty)) } - text_color(c) { #fmt("Text color = 0x%06x", c) } - background_color(c) { #fmt("Background color = 0x%06x", c) } + font_size(s) { #fmt["Font size = %u pt", s] } + display(dis_ty) { #fmt["Display style = %s", print_display(dis_ty)] } + text_color(c) { #fmt["Text color = %s", c.print()] } + background_color(c) { #fmt["Background color = %s", c.print()] } } } fn print_attr(attribute : attr) -> str { alt attribute { - exists(att) { #fmt("[%s]", att) } - exact(att, val) { #fmt("[%s = %s]", att, val) } - includes(att, val) { #fmt("[%s ~= %s]", att, val) } - starts_with(att, val) { #fmt("[%s |= %s]", att, val) } + exists(att) { #fmt["[%s]", att] } + exact(att, val) { #fmt["[%s = %s]", att, val] } + includes(att, val) { #fmt["[%s ~= %s]", att, val] } + starts_with(att, val) { #fmt["[%s |= %s]", att, val] } } } fn print_selector(&&select : ~selector) -> str { alt *select { - element(s, attrs) { #fmt("Element %s with attributes: %s", s, - print_list(attrs, print_attr)) } - child(sel1, sel2) { #fmt("(%s) > (%s)", print_selector(sel1), - print_selector(sel2)) } - descendant(sel1, sel2) { #fmt("(%s) (%s)", print_selector(sel1), - print_selector(sel2)) } - sibling(sel1, sel2) { #fmt("(%s) + (%s)", print_selector(sel1), - print_selector(sel2)) } + element(s, attrs) { #fmt["Element %s with attributes: %s", s, + print_list(attrs, print_attr)] } + child(sel1, sel2) { #fmt["(%s) > (%s)", print_selector(sel1), + print_selector(sel2)] } + descendant(sel1, sel2) { #fmt["(%s) (%s)", print_selector(sel1), + print_selector(sel2)] } + sibling(sel1, sel2) { #fmt["(%s) + (%s)", print_selector(sel1), + print_selector(sel2)] } } } @@ -110,13 +110,13 @@ fn print_rule(&&rule : ~rule) -> str { let sel_str = print_list(sels, print_selector); let sty_str = print_list(styles, print_style); - #fmt("Selectors: %s; Style: {%s}", sel_str, sty_str) + #fmt["Selectors: %s; Style: {%s}", sel_str, sty_str] } } } fn print_sheet(sheet : stylesheet) -> str { - #fmt("CSS Rules:\n%s", print_list_vert(sheet, print_rule)) + #fmt["CSS Rules:\n%s", print_list_vert(sheet, print_rule)] } #[test] @@ -132,12 +132,12 @@ fn test_pretty_print() { let elmt2 = ~element("body", [exact("class", "2")]); let test2 = [~([~descendant(elmt1, elmt2)], - [display(di_block), text_color(0u)])]; + [display(di_block), text_color(black())])]; let actual2 = print_sheet(test2); let expected2 = "CSS Rules:\n-Selectors: (Element * with attributes: ) " + "(Element body with attributes: [class = 2]); " + - "Style: {Display style = block, Text color = 0x000000}"; + "Style: {Display style = block, Text color = rgba(0,0,0,1)}"; assert(actual2 == expected2); } diff --git a/src/servo/layout/base.rs b/src/servo/layout/base.rs index c2ba9b5db5b..d9d74c149bd 100644 --- a/src/servo/layout/base.rs +++ b/src/servo/layout/base.rs @@ -14,6 +14,7 @@ import layout::inline::inline_layout_methods; import layout::style::style::*; import layout::text::*; import util::tree; +import util::color::Color; enum box_kind { bk_block, @@ -24,7 +25,7 @@ enum box_kind { class appearance { let mut background_image: option<@image>; - let mut background_color: option; + let mut background_color: option; new() { self.background_image = none; diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index fdb730d9b82..064e2af0837 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -19,6 +19,7 @@ import layout::style::apply::apply_style_methods; import layout::style::style::style_methods; import box_builder::box_builder_methods; import dl = display_list; +import util::color::methods; enum msg { build(node, stylesheet), @@ -109,16 +110,10 @@ fn box_to_display_item(box: @base::box, origin: Point2D) }); } (none, some(col)) { - let red_col = (col >> 16u) & 255u; - let green_col = (col >> 8u) & 255u; - let blue_col = col & 255u; - - #debug("Assigning colors (%d, %d, %d) to box with bounds %?", red_col as int, green_col as int, blue_col as int, bounds); - + #debug("Assigning color %? to box with bounds %?", col, bounds); item = dl::display_item({ - item_type: dl::display_item_solid_color(red_col as u8, - green_col as u8, - blue_col as u8), + item_type: dl::display_item_solid_color(col.red, col.green, + col.blue), bounds: bounds }); } diff --git a/src/servo/layout/style/style.rs b/src/servo/layout/style/style.rs index f0b34ba9a1a..498ee7685d0 100644 --- a/src/servo/layout/style/style.rs +++ b/src/servo/layout/style/style.rs @@ -8,22 +8,22 @@ import dom::base::node_kind; import dom::rcu::reader_methods; import layout::base::*; // FIXME: resolve bug requires * import matching::matching_methods; +import util::color::{Color, rgb}; +import util::color::css_colors::{white, black}; type computed_style = {mut display : display_type, - mut back_color : uint}; + mut back_color : Color}; #[doc="Returns the default style for the given node kind."] fn default_style_for_node_kind(kind: node_kind) -> computed_style { alt kind { nk_text(*) { {mut display: di_inline, - mut back_color : 256u*256u*256u-1u} + mut back_color : white()} } nk_element(element) { let r = rand::rng(); - let rand_color = 256u*256u*((r.next() & (255 as u32)) as uint) - + 256u*((r.next() & (255 as u32)) as uint) - + ((r.next() & (255 as u32)) as uint); + let rand_color = rgb(r.next() as u8, r.next() as u8, r.next() as u8); alt *element.subclass { es_div { {mut display : di_block, diff --git a/src/servo/parser/css_builder.rs b/src/servo/parser/css_builder.rs index 30133f3b577..6020b3fb76a 100644 --- a/src/servo/parser/css_builder.rs +++ b/src/servo/parser/css_builder.rs @@ -10,6 +10,7 @@ import parser::lexer::css::{token, to_start_desc, to_end_desc, to_eof}; import comm::recv; import option::is_none; +import util::color::parsing::parse_color; type token_reader = {stream : port, mut lookahead : option}; @@ -58,77 +59,6 @@ fn parse_element(reader : token_reader) -> option<~selector> { ret some(~element(elmt_name, attr_list)); } -// Currently colors are supported in rgb(a,b,c) form and also by -// keywords for several common colors. -// TODO: extend this -fn parse_color(color : str) -> uint { - let blue_unit = 1u; - let green_unit = 256u; - let red_unit = 256u * 256u; - - let result_color = if color.starts_with("rgb(") { - let color_vec = str::bytes(color); - let mut i = 4u; - let mut red_vec = []; - let mut green_vec = []; - let mut blue_vec = []; - - while i < color_vec.len() && color_vec[i] != ',' as u8 { - red_vec += [color_vec[i]]; - i += 1u; - } - - i += 1u; - - while i < color_vec.len() && color_vec[i] != ',' as u8 { - green_vec += [color_vec[i]]; - i += 1u; - } - - i += 1u; - - while i < color_vec.len() && color_vec[i] != ')' as u8 { - blue_vec += [color_vec[i]]; - i += 1u; - } - - // TODO, fail by ignoring the rule instead of setting the - // color to black - - let blue_intense = alt uint::from_str(str::from_bytes(blue_vec)) { - some(c) { c } - none { 0u } - }; - - let green_intense = alt uint::from_str(str::from_bytes(green_vec)) { - some(c) { c } - none { 0u } - }; - - let red_intense = alt uint::from_str(str::from_bytes(red_vec)) { - some(c) { c } - none { 0u } - }; - - - blue_unit * blue_intense + green_intense * green_unit - + red_intense * red_unit - } else { - alt color { - "red" { red_unit * 255u } - "blue" { blue_unit * 255u } - "green" { green_unit * 255u} - "white" { red_unit * 256u - 1u } - "black" { 0u } - // TODO, fail by ignoring the rule instead of setting the - // color to black - _ { #debug["Unrecognized color %s", color]; 0u } - } - }; - - ret result_color; -} - fn parse_rule(reader : token_reader) -> option<~rule> { let mut sel_list = []; let mut desc_list = []; diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 2bee3b90721..7199eb02f35 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -69,6 +69,7 @@ mod text { mod util { mod tree; + mod color; mod unsafe; } diff --git a/src/servo/util/color.rs b/src/servo/util/color.rs new file mode 100644 index 00000000000..d568db4cf57 --- /dev/null +++ b/src/servo/util/color.rs @@ -0,0 +1,282 @@ +#[doc = "A library for handling colors and parsing css color declarations."] + +// TODO: handle #rrggbb color declarations, handle rgb(r%,g%,b%), +// sanitize input / crop it to correct ranges, predefine other 130 +// css-defined colors + +import float::round; +import libc::types::os::arch::c95::c_double; +import css_colors::*; + +enum Color = {red : u8, green : u8, blue : u8, alpha : float}; + +fn rgba(r : u8, g : u8, b : u8, a : float) -> Color { + Color({red : r, green : g, blue : b, alpha : a}) +} + +fn rgb(r : u8, g : u8, b : u8) -> Color { + ret rgba(r, g, b, 1.0); +} + +fn hsla(h : float, s : float, l : float, a : float) -> Color { + // Algorithm for converting hsl to rbg taken from + // http://www.w3.org/TR/2003/CR-css3-color-20030514/#hsl-color + let m2 = if l <= 0.5 { l*(s + 1.0) } else { l + s - l*s }; + let m1 = l*2.0 - m2; + let h = h / 360.0; + + fn hue_to_rgb(m1 : float, m2 : float, h : float) -> float { + let h = if h < 0.0 { h + 1.0 } else if h > 1.0 { h - 1.0 } else { h }; + + alt h { + 0.0 to 1.0/6.0 { ret m1 + (m2 - m1)*h*6.0; } + 1.0/6.0 to 1.0/2.0 { ret m2; } + 1.0/2.0 to 2.0/3.0 { ret m1 + (m2 - m1)*(4.0 - 6.0*h); } + 2.0/3.0 to 1.0 { ret m1; } + _ { fail "unexpected hue value"; } + } + } + + let r = round(255.0*hue_to_rgb(m1, m2, h + 1.0/3.0) as c_double);; + let g = round(255.0*hue_to_rgb(m1, m2, h) as c_double); + let b = round(255.0*hue_to_rgb(m1, m2, h - 1.0/3.0) as c_double); + + ret rgba(r as u8, g as u8, b as u8, a); +} + +fn hsl(h : float, s : float, l : float) -> Color { + ret hsla(h, s, l, 1.0); +} + +impl methods for Color { + fn print() -> str { + #fmt["rgba(%u,%u,%u,%f)", self.red as uint, self.green as uint, + self.blue as uint, self.alpha] + } +} + +mod parsing { + export parse_color; + + // TODO, fail by ignoring the rule instead of setting the + // color to black + fn fail_unrecognized(col : str) -> Color { + #warn["Unrecognized color %s", col]; + ret black(); + } + + #[doc="Match an exact color keyword."] + fn parse_by_name(color : str) -> Color { + alt color.to_lower() { + "black" { black() } + "silver" { silver() } + "gray" { gray() } + "grey" { gray() } + "white" { white() } + "maroon" { maroon() } + "red" { red() } + "purple" { purple() } + "fuschia" { fuschia() } + "green" { green() } + "lime" { lime() } + "olive" { olive() } + "yellow" { yellow() } + "navy" { navy() } + "blue" { blue() } + "teal" { teal() } + "aqua" { aqua() } + _ { fail_unrecognized(color) } + } + } + + #[doc="Parses a color specification in the form rgb(foo,bar,baz)"] + fn parse_rgb(color : str) -> Color { + // Shave off the rgb( and the ) + let only_colors = color.substr(4u, color.len() - 5u); + + // split up r, g, and b + let cols = only_colors.split_char(','); + if cols.len() != 3u { ret fail_unrecognized(color); } + + alt (u8::from_str(cols[0]), u8::from_str(cols[1]), + u8::from_str(cols[2])) { + (some(r), some(g), some(b)) { rgb(r, g, b) } + _ { fail_unrecognized(color) } + } + } + + #[doc="Parses a color specification in the form rgba(foo,bar,baz,qux)"] + fn parse_rgba(color : str) -> Color { + // Shave off the rgba( and the ) + let only_vals = color.substr(5u, color.len() - 6u); + + // split up r, g, and b + let cols = only_vals.split_char(','); + if cols.len() != 4u { ret fail_unrecognized(color); } + + alt (u8::from_str(cols[0]), u8::from_str(cols[1]), + u8::from_str(cols[2]), float::from_str(cols[3])) { + (some(r), some(g), some(b), some(a)) { rgba(r, g, b, a) } + _ { fail_unrecognized(color) } + } + } + + #[doc="Parses a color specification in the form hsl(foo,bar,baz)"] + fn parse_hsl(color : str) -> Color { + // Shave off the hsl( and the ) + let only_vals = color.substr(4u, color.len() - 5u); + + // split up h, s, and l + let vals = only_vals.split_char(','); + if vals.len() != 3u { ret fail_unrecognized(color); } + + alt (float::from_str(vals[0]), float::from_str(vals[1]), + float::from_str(vals[2])) { + (some(h), some(s), some(l)) { hsl(h, s, l) } + _ { fail_unrecognized(color) } + } + } + + #[doc="Parses a color specification in the form hsla(foo,bar,baz,qux)"] + fn parse_hsla(color : str) -> Color { + // Shave off the hsla( and the ) + let only_vals = color.substr(5u, color.len() - 6u); + + let vals = only_vals.split_char(','); + if vals.len() != 4u { ret fail_unrecognized(color); } + + alt (float::from_str(vals[0]), float::from_str(vals[1]), + float::from_str(vals[2]), float::from_str(vals[3])) { + (some(h), some(s), some(l), some(a)) { hsla(h, s, l, a) } + _ { fail_unrecognized(color) } + } + } + + // Currently colors are supported in rgb(a,b,c) form and also by + // keywords for several common colors. + // TODO: extend this + fn parse_color(color : str) -> Color { + alt color { + c if c.starts_with("rgb(") { parse_rgb(c) } + c if c.starts_with("rgba(") { parse_rgba(c) } + c if c.starts_with("hsl(") { parse_hsl(c) } + c if c.starts_with("hsla(") { parse_hsla(c) } + c { parse_by_name(c) } + } + } +} + +mod test { + import css_colors::*; + import parsing::parse_color; + + #[test] + fn test_parse_by_name() { + assert red() == parse_color("red"); + assert lime() == parse_color("Lime"); + assert blue() == parse_color("BLUE"); + assert green() == parse_color("GreEN"); + assert white() == parse_color("white"); + assert black() == parse_color("Black"); + assert gray() == parse_color("Gray"); + assert silver() == parse_color("SiLvEr"); + assert maroon() == parse_color("maroon"); + assert purple() == parse_color("PURPLE"); + assert fuschia() == parse_color("FUSCHIA"); + assert olive() == parse_color("oLiVe"); + assert yellow() == parse_color("yellow"); + assert navy() == parse_color("NAVY"); + assert teal() == parse_color("Teal"); + assert aqua() == parse_color("Aqua"); + } + + #[test] + fn test_parsing_rgb() { + assert red() == parse_color("rgb(255,0,0)"); + assert red() == parse_color("rgba(255,0,0,1.0)"); + assert red() == parse_color("rgba(255,0,0,1)"); + assert lime() == parse_color("rgba(0,255,0,1.00)"); + assert rgb(1u8,2u8,3u8) == parse_color("rgb(1,2,03)"); + assert rgba(15u8,250u8,3u8,0.5) == parse_color("rgba(15,250,3,.5)"); + assert rgba(15u8,250u8,3u8,0.5) == parse_color("rgba(15,250,3,0.5)"); + } + + #[test] + fn test_parsing_hsl() { + assert red() == parse_color("hsl(0,1,.5)"); + assert lime() == parse_color("hsl(120.0,1.0,.5)"); + assert blue() == parse_color("hsl(240.0,1.0,.5)"); + assert green() == parse_color("hsl(120.0,1.0,.25)"); + assert white() == parse_color("hsl(1.0,1.,1.0)"); + assert white() == parse_color("hsl(129.0,0.3,1.0)"); + assert black() == parse_color("hsl(231.2,0.75,0.0)"); + assert black() == parse_color("hsl(11.2,0.0,0.0)"); + assert gray() == parse_color("hsl(0.0,0.0,0.5)"); + assert maroon() == parse_color("hsl(0.0,1.0,0.25)"); + assert purple() == parse_color("hsl(300.0,1.0,0.25)"); + assert fuschia() == parse_color("hsl(300,1.0,0.5)"); + assert olive() == parse_color("hsl(60.,1.0,0.25)"); + assert yellow() == parse_color("hsl(60.,1.0,0.5)"); + assert navy() == parse_color("hsl(240.0,1.0,.25)"); + assert teal() == parse_color("hsl(180.0,1.0,.25)"); + assert aqua() == parse_color("hsl(180.0,1.0,.5)"); + } +} + + +#[doc="Define the colors specified by css"] +mod css_colors { + // The 16 basic css colors + fn black() -> Color { + Color({red : 0u8, green : 0u8, blue : 0u8, alpha : 1.0}) + } + fn silver() -> Color { + Color({red : 192u8, green : 192u8, blue : 192u8, alpha : 1.0}) + } + fn gray() -> Color { + Color({red : 128u8, green : 128u8, blue : 128u8, alpha : 1.0}) + } + fn white() -> Color { + Color({red : 255u8, green : 255u8, blue : 255u8, alpha : 1.0}) + } + fn maroon() -> Color { + Color({red : 128u8, green : 0u8, blue : 0u8, alpha : 1.0}) + } + fn red() -> Color { + Color({red : 255u8, green : 0u8, blue : 0u8, alpha : 1.0}) + } + fn purple() -> Color { + Color({red : 128u8, green : 0u8, blue : 128u8, alpha : 1.0}) + } + fn fuschia() -> Color { + Color({red : 255u8, green : 0u8, blue : 255u8, alpha : 1.0}) + } + fn green() -> Color { + Color({red : 0u8, green : 128u8, blue : 0u8, alpha : 1.0}) + } + fn lime() -> Color { + Color({red : 0u8, green : 255u8, blue : 0u8, alpha : 1.0}) + } + fn olive() -> Color { + Color({red : 128u8, green : 128u8, blue : 0u8, alpha : 1.0}) + } + fn yellow() -> Color { + Color({red : 255u8, green : 255u8, blue : 0u8, alpha : 1.0}) + } + fn navy() -> Color { + Color({red : 0u8, green : 0u8, blue : 128u8, alpha : 1.0}) + } + fn blue() -> Color { + Color({red : 0u8, green : 0u8, blue : 255u8, alpha : 1.0}) + } + fn teal() -> Color { + Color({red : 0u8, green : 128u8, blue : 128u8, alpha : 1.0}) + } + fn aqua() -> Color { + Color({red : 0u8, green : 255u8, blue : 255u8, alpha : 1.0}) + } + + + // The other 130 css colors + // TODO +}