Commit 871c01c2 by Aaron Leung

Implemented the 'scale-color' built-in. Slight rounding discrepancies compared to Ruby Sass.

parent da5a9c59
...@@ -131,6 +131,7 @@ namespace Sass { ...@@ -131,6 +131,7 @@ namespace Sass {
register_function(fade_out_sig, fade_out); register_function(fade_out_sig, fade_out);
// Other Color Functions // Other Color Functions
register_function(adjust_color_sig, adjust_color); register_function(adjust_color_sig, adjust_color);
register_function(scale_color_sig, scale_color);
register_function(change_color_sig, change_color); register_function(change_color_sig, change_color);
// String Functions // String Functions
register_function(unquote_sig, unquote); register_function(unquote_sig, unquote);
......
...@@ -155,8 +155,13 @@ namespace Sass { ...@@ -155,8 +155,13 @@ namespace Sass {
return Node(); return Node();
} }
static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, double low, double high) { static Node arg(Signature sig, string& path, size_t line, const Node parameter_names, Environment& bindings, size_t param_num, Node::Type t, double low, double high) {
Node the_arg(arg(sig, path, line, parameter_names, bindings, param_num, Node::numeric)); Node the_arg(arg(sig, path, line, parameter_names, bindings, param_num, t));
if (!the_arg.is_numeric()) {
stringstream msg;
msg << "numeric value required for argument " << param_num+1 << " in call to '" << sig << "'";
throw_eval_error(msg.str(), path, line);
}
double val = the_arg.numeric_value(); double val = the_arg.numeric_value();
if (val < low || high < val) { if (val < low || high < val) {
stringstream msg; stringstream msg;
...@@ -172,25 +177,25 @@ namespace Sass { ...@@ -172,25 +177,25 @@ namespace Sass {
extern Signature rgb_sig = "rgb($red, $green, $blue)"; extern Signature rgb_sig = "rgb($red, $green, $blue)";
Node rgb(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node rgb(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double r = arg(rgb_sig, path, line, parameter_names, bindings, 0, 0, 255).numeric_value(); double r = arg(rgb_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255).numeric_value();
double g = arg(rgb_sig, path, line, parameter_names, bindings, 1, 0, 255).numeric_value(); double g = arg(rgb_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255).numeric_value();
double b = arg(rgb_sig, path, line, parameter_names, bindings, 2, 0, 255).numeric_value(); double b = arg(rgb_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255).numeric_value();
return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), 1.0); return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), 1.0);
} }
extern Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; extern Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
Node rgba_4(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node rgba_4(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double r = arg(rgba_4_sig, path, line, parameter_names, bindings, 0, 0, 255).numeric_value(); double r = arg(rgba_4_sig, path, line, parameter_names, bindings, 0, Node::numeric, 0, 255).numeric_value();
double g = arg(rgba_4_sig, path, line, parameter_names, bindings, 1, 0, 255).numeric_value(); double g = arg(rgba_4_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 255).numeric_value();
double b = arg(rgba_4_sig, path, line, parameter_names, bindings, 2, 0, 255).numeric_value(); double b = arg(rgba_4_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 255).numeric_value();
double a = arg(rgba_4_sig, path, line, parameter_names, bindings, 3, 0, 1).numeric_value(); double a = arg(rgba_4_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1).numeric_value();
return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), a); return new_Node(path, line, std::floor(r), std::floor(g), std::floor(b), a);
} }
extern Signature rgba_2_sig = "rgba($color, $alpha)"; extern Signature rgba_2_sig = "rgba($color, $alpha)";
Node rgba_2(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node rgba_2(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node alpha_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 1, 0, 1)); Node alpha_arg(arg(rgba_2_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1));
double r = color_arg[0].numeric_value(); double r = color_arg[0].numeric_value();
double g = color_arg[1].numeric_value(); double g = color_arg[1].numeric_value();
double b = color_arg[2].numeric_value(); double b = color_arg[2].numeric_value();
...@@ -220,7 +225,7 @@ namespace Sass { ...@@ -220,7 +225,7 @@ namespace Sass {
Node mix(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node mix(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color1(arg(mix_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color1(arg(mix_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node color2(arg(mix_sig, path, line, parameter_names, bindings, 1, Node::numeric_color)); Node color2(arg(mix_sig, path, line, parameter_names, bindings, 1, Node::numeric_color));
Node weight(arg(mix_sig, path, line, parameter_names, bindings, 2, 0, 100)); Node weight(arg(mix_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100));
double p = weight.numeric_value()/100; double p = weight.numeric_value()/100;
double w = 2*p - 1; double w = 2*p - 1;
...@@ -310,17 +315,17 @@ namespace Sass { ...@@ -310,17 +315,17 @@ namespace Sass {
extern Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; extern Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
Node hsl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node hsl(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double h = arg(hsl_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value(); double h = arg(hsl_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value();
double s = arg(hsl_sig, path, line, parameter_names, bindings, 1, 0, 100).numeric_value(); double s = arg(hsl_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100).numeric_value();
double l = arg(hsl_sig, path, line, parameter_names, bindings, 2, 0, 100).numeric_value(); double l = arg(hsl_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100).numeric_value();
return hsla_impl(h, s, l, 1.0, new_Node, path, line); return hsla_impl(h, s, l, 1.0, new_Node, path, line);
} }
extern Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; extern Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
Node hsla(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node hsla(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
double h = arg(hsla_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value(); double h = arg(hsla_sig, path, line, parameter_names, bindings, 0, Node::numeric).numeric_value();
double s = arg(hsla_sig, path, line, parameter_names, bindings, 1, 0, 100).numeric_value(); double s = arg(hsla_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100).numeric_value();
double l = arg(hsla_sig, path, line, parameter_names, bindings, 2, 0, 100).numeric_value(); double l = arg(hsla_sig, path, line, parameter_names, bindings, 2, Node::numeric, 0, 100).numeric_value();
double a = arg(hsla_sig, path, line, parameter_names, bindings, 3, 0, 1).numeric_value(); double a = arg(hsla_sig, path, line, parameter_names, bindings, 3, Node::numeric, 0, 1).numeric_value();
return hsla_impl(h, s, l, a, new_Node, path, line); return hsla_impl(h, s, l, a, new_Node, path, line);
} }
...@@ -372,7 +377,7 @@ namespace Sass { ...@@ -372,7 +377,7 @@ namespace Sass {
extern Signature lighten_sig = "lighten($color, $amount)"; extern Signature lighten_sig = "lighten($color, $amount)";
Node lighten(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node lighten(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(lighten_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node rgb_col(arg(lighten_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(lighten_sig, path, line, parameter_names, bindings, 1, 0, 100)); Node amount(arg(lighten_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(), Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(), rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(), rgb_col[2].numeric_value(),
...@@ -387,7 +392,7 @@ namespace Sass { ...@@ -387,7 +392,7 @@ namespace Sass {
extern Signature darken_sig = "darken($color, $amount)"; extern Signature darken_sig = "darken($color, $amount)";
Node darken(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node darken(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(darken_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node rgb_col(arg(darken_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(darken_sig, path, line, parameter_names, bindings, 1, 0, 100)); Node amount(arg(darken_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(), Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(), rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(), rgb_col[2].numeric_value(),
...@@ -402,7 +407,7 @@ namespace Sass { ...@@ -402,7 +407,7 @@ namespace Sass {
extern Signature saturate_sig = "saturate($color, $amount)"; extern Signature saturate_sig = "saturate($color, $amount)";
Node saturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node saturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(saturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node rgb_col(arg(saturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(saturate_sig, path, line, parameter_names, bindings, 1, 0, 100)); Node amount(arg(saturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(), Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(), rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(), rgb_col[2].numeric_value(),
...@@ -417,7 +422,7 @@ namespace Sass { ...@@ -417,7 +422,7 @@ namespace Sass {
extern Signature desaturate_sig = "desaturate($color, $amount)"; extern Signature desaturate_sig = "desaturate($color, $amount)";
Node desaturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node desaturate(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node rgb_col(arg(desaturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node rgb_col(arg(desaturate_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
Node amount(arg(desaturate_sig, path, line, parameter_names, bindings, 1, 0, 100)); Node amount(arg(desaturate_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 100));
Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(), Node hsl_col(rgb_to_hsl(rgb_col[0].numeric_value(),
rgb_col[1].numeric_value(), rgb_col[1].numeric_value(),
rgb_col[2].numeric_value(), rgb_col[2].numeric_value(),
...@@ -486,7 +491,7 @@ namespace Sass { ...@@ -486,7 +491,7 @@ namespace Sass {
extern Signature opacify_sig = "opacify($color, $amount)"; extern Signature opacify_sig = "opacify($color, $amount)";
Node opacify(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node opacify(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(opacify_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color(arg(opacify_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(opacify_sig, path, line, parameter_names, bindings, 1, 0, 1).numeric_value(); double delta = arg(opacify_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
delta += color[3].numeric_value(); delta += color[3].numeric_value();
if (delta > 1) delta = 1; if (delta > 1) delta = 1;
return new_Node(path, line, return new_Node(path, line,
...@@ -499,7 +504,7 @@ namespace Sass { ...@@ -499,7 +504,7 @@ namespace Sass {
extern Signature fade_in_sig = "fade-in($color, $amount)"; extern Signature fade_in_sig = "fade-in($color, $amount)";
Node fade_in(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node fade_in(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(fade_in_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color(arg(fade_in_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(fade_in_sig, path, line, parameter_names, bindings, 1, 0, 1).numeric_value(); double delta = arg(fade_in_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
delta += color[3].numeric_value(); delta += color[3].numeric_value();
if (delta > 1) delta = 1; if (delta > 1) delta = 1;
return new_Node(path, line, return new_Node(path, line,
...@@ -512,7 +517,7 @@ namespace Sass { ...@@ -512,7 +517,7 @@ namespace Sass {
extern Signature transparentize_sig = "transparentize($color, $amount)"; extern Signature transparentize_sig = "transparentize($color, $amount)";
Node transparentize(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node transparentize(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(transparentize_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color(arg(transparentize_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(transparentize_sig, path, line, parameter_names, bindings, 1, 0, 1).numeric_value(); double delta = arg(transparentize_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
double alpha = color[3].numeric_value() - delta; double alpha = color[3].numeric_value() - delta;
if (alpha < 0) alpha = 0; if (alpha < 0) alpha = 0;
return new_Node(path, line, return new_Node(path, line,
...@@ -525,7 +530,7 @@ namespace Sass { ...@@ -525,7 +530,7 @@ namespace Sass {
extern Signature fade_out_sig = "fade-out($color, $amount)"; extern Signature fade_out_sig = "fade-out($color, $amount)";
Node fade_out(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node fade_out(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(arg(fade_out_sig, path, line, parameter_names, bindings, 0, Node::numeric_color)); Node color(arg(fade_out_sig, path, line, parameter_names, bindings, 0, Node::numeric_color));
double delta = arg(fade_out_sig, path, line, parameter_names, bindings, 1, 0, 1).numeric_value(); double delta = arg(fade_out_sig, path, line, parameter_names, bindings, 1, Node::numeric, 0, 1).numeric_value();
double alpha = color[3].numeric_value() - delta; double alpha = color[3].numeric_value() - delta;
if (alpha < 0) alpha = 0; if (alpha < 0) alpha = 0;
return new_Node(path, line, return new_Node(path, line,
...@@ -601,6 +606,83 @@ namespace Sass { ...@@ -601,6 +606,83 @@ namespace Sass {
return Node(); return Node();
} }
extern Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
Node scale_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(bindings[parameter_names[0].token()]);
Node r(bindings[parameter_names[1].token()]);
Node g(bindings[parameter_names[2].token()]);
Node b(bindings[parameter_names[3].token()]);
Node h(bindings[parameter_names[4].token()]);
Node s(bindings[parameter_names[5].token()]);
Node l(bindings[parameter_names[6].token()]);
Node a(bindings[parameter_names[7].token()]);
bool no_rgb = r.is_false() && g.is_false() && b.is_false();
bool no_hsl = h.is_false() && s.is_false() && l.is_false();
size_t low, high;
bool is_hsl = false;
if (color.type() != Node::numeric_color) {
throw_eval_error("first argument to 'scale-color' must be a color", path, line);
}
if (!no_rgb && !no_hsl) {
throw_eval_error("cannot specify RGB and HSL values for a color at the same time for 'scale-color'", path, line);
}
else if (!no_rgb) {
low = 1; high = 4;
}
else if (!no_hsl) {
is_hsl = true;
low = 4; high = 7;
Node alpha(color[3]);
color = rgb_to_hsl(color[0].numeric_value(), color[1].numeric_value(), color[2].numeric_value(), new_Node, path, line);
color << alpha;
}
else if (!a.is_false()) {
Node result(new_Node(Node::numeric_color, path, line, 4));
for (size_t i = 0; i < 3; ++i) {
result << new_Node(path, line, color[i].numeric_value());
}
double current = color[3].numeric_value();
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double diff = scale > 0 ? 1 - current : current;
result << new_Node(path, line, current + diff*scale);
return result;
}
else {
throw_eval_error("not enough arguments for 'scale-color'", color.path(), color.line());
}
Node result(new_Node(Node::numeric_color, path, line, 4));
for (size_t i = low, j = 0; i < high; ++i, ++j) {
double current = color[j].numeric_value();
if (!bindings[parameter_names[i].token()].is_false()) {
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, i, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double diff = scale > 0 ? (is_hsl ? 100 : 255) - current : current;
result << new_Node(path, line, current + diff*scale);
}
else {
result << new_Node(path, line, current);
}
}
if (!a.is_false()) {
double current = color[3].numeric_value();
double scale = arg(scale_color_sig, path, line, parameter_names, bindings, 7, Node::numeric_percentage, -100, 100).numeric_value() / 100;
double diff = scale > 0 ? 1 - current : current;
result << new_Node(path, line, current + diff*scale);
}
else {
result << new_Node(path, line, color[3].numeric_value());
}
if (is_hsl) {
result = hsla_impl(result[0].numeric_value(),
result[1].numeric_value(),
result[2].numeric_value(),
result[3].numeric_value(),
new_Node, path, line);
}
return result;
}
extern Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; extern Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";
Node change_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) { Node change_color(const Node parameter_names, Environment& bindings, Node_Factory& new_Node, string& path, size_t line) {
Node color(bindings[parameter_names[0].token()]); Node color(bindings[parameter_names[0].token()]);
...@@ -855,7 +937,7 @@ namespace Sass { ...@@ -855,7 +937,7 @@ namespace Sass {
l = new_Node(Node::space_list, path, line, 1) << l; l = new_Node(Node::space_list, path, line, 1) << l;
} }
// just truncate the index if it's not an integer ... more permissive than Ruby Sass // just truncate the index if it's not an integer ... more permissive than Ruby Sass
size_t n = std::floor(arg(nth_sig, path, line, parameter_names, bindings, 1, 1, l.size()).numeric_value()); size_t n = std::floor(arg(nth_sig, path, line, parameter_names, bindings, 1, Node::numeric, 1, l.size()).numeric_value());
return l[n - 1]; return l[n - 1];
} }
......
...@@ -147,6 +147,9 @@ namespace Sass { ...@@ -147,6 +147,9 @@ namespace Sass {
extern Signature adjust_color_sig; extern Signature adjust_color_sig;
Node adjust_color(const Node, Environment&, Node_Factory&, string& path, size_t line); Node adjust_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
extern Signature scale_color_sig;
Node scale_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
extern Signature change_color_sig; extern Signature change_color_sig;
Node change_color(const Node, Environment&, Node_Factory&, string& path, size_t line); Node change_color(const Node, Environment&, Node_Factory&, string& path, size_t line);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment