servo: Merge #19 - Refactored color into a separate module and added support for input types (from mmeyerho:color)

Source-Repo: https://github.com/servo/servo
Source-Revision: c5e2f391b0454ffb941de6326f4ef473d6262cb3
This commit is contained in:
Margaret Meyerhofer 2012-06-14 15:20:50 -07:00
parent 2ab42caeae
commit c8b78fac7a
7 changed files with 319 additions and 110 deletions

View File

@ -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);
}

View File

@ -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<uint>;
let mut background_color: option<Color>;
new() {
self.background_image = none;

View File

@ -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<au>)
});
}
(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
});
}

View File

@ -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,

View File

@ -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<token>, mut lookahead : option<token>};
@ -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 = [];

View File

@ -69,6 +69,7 @@ mod text {
mod util {
mod tree;
mod color;
mod unsafe;
}

View File

@ -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
}