diff --git a/src/builtin/color/other.rs b/src/builtin/color/other.rs index f18eee9..4da882a 100644 --- a/src/builtin/color/other.rs +++ b/src/builtin/color/other.rs @@ -118,4 +118,65 @@ pub(crate) fn register(f: &mut BTreeMap) { color })) }); + decl!(f "scale-color", |args, _| { + let color = match arg!(args, 0, "color").eval() { + Value::Color(c) => c.clone(), + _ => todo!("non-color given to builtin function `scale-color()`") + }; + + opt_arg!(args, alpha, "alpha"); + opt_arg!(args, red, "red"); + opt_arg!(args, green, "green"); + opt_arg!(args, blue, "blue"); + + if red.is_some() || green.is_some() || blue.is_some() { + return + Some(Value::Color( + Color::from_rgba( + scale(color.red(), red.unwrap_or(Number::from(0)), Number::from(255)), + scale(color.green(), green.unwrap_or(Number::from(0)), Number::from(255)), + scale(color.blue(), blue.unwrap_or(Number::from(0)), Number::from(255)), + scale(color.alpha(), alpha.unwrap_or(Number::from(0)), Number::from(1)), + ) + )) + } + + let hue = match arg!(args, -1, "hue"=Value::Null).eval() { + Value::Dimension(n, Unit::None) + | Value::Dimension(n, Unit::Percent) + | Value::Dimension(n, Unit::Deg) => Some(n), + Value::Null => None, + _ => todo!("expected either unitless or % number for hue"), + }; + + opt_arg!(hsl: args, saturation, "saturation"); + opt_arg!(hsl: args, luminance, "lightness"); + + if hue.is_some() || saturation.is_some() || luminance.is_some() { + // Color::as_hsla() returns more exact values than Color::hue(), etc. + let (this_hue, this_saturation, this_luminance, this_alpha) = color.as_hsla(); + return Some(Value::Color( + Color::from_hsla( + scale(this_hue, hue.unwrap_or(Number::from(0)), Number::from(360)), + scale(this_saturation, saturation.unwrap_or(Number::from(0)), Number::from(1)), + scale(this_luminance, luminance.unwrap_or(Number::from(0)), Number::from(1)), + scale(this_alpha, alpha.unwrap_or(Number::from(0)), Number::from(1)), + ) + )) + } + + Some(Value::Color(if let Some(a) = alpha { + let temp_alpha = color.alpha(); + color.with_alpha(scale(temp_alpha, a, Number::from(1))) + } else { + color + })) + }); +} + +fn scale(val: Number, by: Number, max: Number) -> Number { + if by == Number::from(0) { + return val; + } + val.clone() + (if by > Number::from(0) { max - val } else { val }) * by } diff --git a/tests/color.rs b/tests/color.rs index 6ba79fd..71bfca3 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -421,3 +421,18 @@ test!( "a {\n color: adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4);\n}\n", "a {\n color: rgba(255, 106, 0, 0.6);\n}\n" ); +test!( + scale_color_lightness, + "a {\n color: scale-color(hsl(120, 70%, 80%), $lightness: 50%);\n}\n", + "a {\n color: #d4f7d4;\n}\n" +); +test!( + scale_color_negative, + "a {\n color: scale-color(rgb(200, 150%, 170%), $green: -40%, $blue: 70%);\n}\n", + "a {\n color: #c899ff;\n}\n" +); +test!( + scale_color_alpha, + "a {\n color: scale-color(hsl(200, 70%, 80%), $saturation: -90%, $alpha: -30%);\n}\n", + "a {\n color: rgba(200, 205, 208, 0.7);\n}\n" +);