Implement hsl()
and hsla()
functions
This commit is contained in:
parent
86173a3ca7
commit
5cab99cd6e
@ -34,9 +34,50 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
};
|
||||
Some(Value::Color(Color::from_values(red, green, blue, alpha)))
|
||||
} else {
|
||||
todo!("channels variable in `rgb`")
|
||||
todo!("channels variable in `rgba`")
|
||||
}
|
||||
});
|
||||
decl!(f "hsl", |args, _| {
|
||||
let hue = match arg!(args, 0, "hue").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n,
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
let saturation = match arg!(args, 1, "saturation").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
let luminance = match arg!(args, 2, "luminance").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
Some(Value::Color(Color::from_hsla(hue, saturation, luminance, Number::from(1))))
|
||||
});
|
||||
decl!(f "hsla", |args, _| {
|
||||
let hue = match arg!(args, 0, "hue").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n,
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
let saturation = match arg!(args, 1, "saturation").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
let luminance = match arg!(args, 2, "luminance").clone().eval() {
|
||||
Value::Dimension(n, Unit::None)
|
||||
| Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
let alpha = match arg!(args, 3, "alpha").clone().eval() {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for alpha"),
|
||||
};
|
||||
Some(Value::Color(Color::from_hsla(hue, saturation, luminance, alpha)))
|
||||
});
|
||||
decl!(f "red", |args, _| {
|
||||
match arg!(args, 0, "red") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.red())), Unit::None)),
|
||||
|
100
src/color/mod.rs
100
src/color/mod.rs
@ -3,6 +3,8 @@ use std::fmt::{self, Display};
|
||||
use crate::value::Number;
|
||||
pub(crate) use name::ColorName;
|
||||
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
mod name;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -41,6 +43,104 @@ impl Color {
|
||||
self.alpha.clone()
|
||||
}
|
||||
|
||||
/// Create RGBA representation from HSLA values
|
||||
/// Algorithm adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
|
||||
pub fn from_hsla(
|
||||
mut hue: Number,
|
||||
saturation: Number,
|
||||
luminance: Number,
|
||||
alpha: Number,
|
||||
) -> Self {
|
||||
println!("{}-{}-{}", &hue, &saturation, &luminance);
|
||||
if saturation.clone() == Number::from(0) {
|
||||
let luminance = if luminance > Number::from(100) {
|
||||
Number::from(100)
|
||||
} else {
|
||||
luminance
|
||||
};
|
||||
let val = (luminance.clone() * Number::from(255))
|
||||
.to_integer()
|
||||
.to_u16()
|
||||
.unwrap();
|
||||
let repr = if alpha >= Number::from(1) {
|
||||
format!("#{:0>2x}{:0>2x}{:0>2x}", val, val, val)
|
||||
} else {
|
||||
format!("rgba({}, {}, {}, {})", val, val, val, alpha)
|
||||
};
|
||||
return Color {
|
||||
red: val,
|
||||
green: val,
|
||||
blue: val,
|
||||
alpha: Number::from(alpha),
|
||||
repr,
|
||||
};
|
||||
}
|
||||
let temporary_1 = if luminance.clone() < Number::ratio(1, 2) {
|
||||
luminance.clone() * (Number::from(1) + saturation)
|
||||
} else {
|
||||
luminance.clone() + saturation.clone() - luminance.clone() * saturation.clone()
|
||||
};
|
||||
let temporary_2 = Number::from(2) * luminance.clone() - temporary_1.clone();
|
||||
hue = hue / Number::from(360);
|
||||
let mut temporary_r = hue.clone() + Number::ratio(1, 3);
|
||||
let mut temporary_g = hue.clone();
|
||||
let mut temporary_b = hue.clone() - Number::ratio(1, 3);
|
||||
|
||||
macro_rules! clamp {
|
||||
($temp:ident) => {
|
||||
if $temp > Number::from(1) {
|
||||
$temp -= Number::from(1);
|
||||
} else if $temp < Number::from(0) {
|
||||
$temp += Number::from(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
clamp!(temporary_r);
|
||||
clamp!(temporary_g);
|
||||
clamp!(temporary_b);
|
||||
|
||||
macro_rules! channel {
|
||||
($name:ident, $temp:ident, $temp1:ident, $temp2:ident) => {
|
||||
let $name = (if Number::from(6) * $temp.clone() < Number::from(1) {
|
||||
$temp2.clone()
|
||||
+ ($temp1.clone() - $temp2.clone()) * Number::from(6) * $temp.clone()
|
||||
} else if Number::from(2) * $temp.clone() < Number::from(1) {
|
||||
$temp1.clone()
|
||||
} else if Number::from(3) * $temp.clone() < Number::from(2) {
|
||||
$temp2.clone()
|
||||
+ ($temp1.clone() - $temp2.clone())
|
||||
* (Number::ratio(2, 3) - $temp)
|
||||
* Number::from(6)
|
||||
} else {
|
||||
$temp2.clone()
|
||||
} * Number::from(255))
|
||||
.round()
|
||||
.to_integer()
|
||||
.to_u16()
|
||||
.expect("expected channel to fit inside u16");
|
||||
};
|
||||
}
|
||||
|
||||
channel!(red, temporary_r, temporary_1, temporary_2);
|
||||
channel!(green, temporary_g, temporary_1, temporary_2);
|
||||
channel!(blue, temporary_b, temporary_1, temporary_2);
|
||||
|
||||
let repr = if alpha >= Number::from(1) {
|
||||
format!("#{:0>2x}{:0>2x}{:0>2x}", red, green, blue)
|
||||
} else {
|
||||
dbg!("hi");
|
||||
format!("rgba({}, {}, {}, {})", red, green, blue, alpha)
|
||||
};
|
||||
Color {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha,
|
||||
repr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_values(red: u16, green: u16, blue: u16, alpha: Number) -> Self {
|
||||
let repr = if alpha >= Number::from(1) {
|
||||
format!("#{:0>2x}{:0>2x}{:0>2x}", red, green, blue)
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::convert::From;
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
@ -20,6 +20,22 @@ impl Number {
|
||||
pub fn to_integer(&self) -> BigInt {
|
||||
self.val.to_integer()
|
||||
}
|
||||
|
||||
pub fn ratio<A: Into<BigInt>, B: Into<BigInt>>(a: A, b: B) -> Self {
|
||||
Number::new(BigRational::new(a.into(), b.into()))
|
||||
}
|
||||
|
||||
pub fn round(self) -> Self {
|
||||
Number {
|
||||
val: self.val.round(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:0>2x}", self.val.to_integer())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigInt> for Number {
|
||||
@ -84,6 +100,12 @@ impl Add for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Number {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.val += other.val
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Number {
|
||||
type Output = Self;
|
||||
|
||||
@ -94,6 +116,12 @@ impl Sub for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Number {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
self.val -= other.val
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Number {
|
||||
type Output = Self;
|
||||
|
||||
|
@ -76,3 +76,23 @@ test!(
|
||||
"a {\n color: rgba(1, 2, 3, 50%);\n}\n",
|
||||
"a {\n color: rgba(1, 2, 3, 0.5);\n}\n"
|
||||
);
|
||||
test!(
|
||||
hsl_basic,
|
||||
"a {\n color: hsl(193, 67%, 99);\n}\n",
|
||||
"a {\n color: #fbfdfe;\n}\n"
|
||||
);
|
||||
test!(
|
||||
hsla_basic,
|
||||
"a {\n color: hsla(193, 67%, 99, .6);\n}\n",
|
||||
"a {\n color: rgba(251, 253, 254, 0.6);\n}\n"
|
||||
);
|
||||
test!(
|
||||
hsl_named,
|
||||
"a {\n color: hsl($hue: 193, $saturation: 67%, $luminance: 99);\n}\n",
|
||||
"a {\n color: #fbfdfe;\n}\n"
|
||||
);
|
||||
test!(
|
||||
hsla_named,
|
||||
"a {\n color: hsla($hue: 193, $saturation: 67%, $luminance: 99, $alpha: .6);\n}\n",
|
||||
"a {\n color: rgba(251, 253, 254, 0.6);\n}\n"
|
||||
);
|
||||
|
@ -32,4 +32,4 @@ test!(
|
||||
length_named_arg,
|
||||
"a {\n color: str-length($string: aBc123);\n}\n",
|
||||
"a {\n color: 6;\n}\n"
|
||||
);
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user