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)))
|
Some(Value::Color(Color::from_values(red, green, blue, alpha)))
|
||||||
} else {
|
} 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, _| {
|
decl!(f "red", |args, _| {
|
||||||
match arg!(args, 0, "red") {
|
match arg!(args, 0, "red") {
|
||||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.red())), Unit::None)),
|
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;
|
use crate::value::Number;
|
||||||
pub(crate) use name::ColorName;
|
pub(crate) use name::ColorName;
|
||||||
|
|
||||||
|
use num_traits::cast::ToPrimitive;
|
||||||
|
|
||||||
mod name;
|
mod name;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
@ -41,6 +43,104 @@ impl Color {
|
|||||||
self.alpha.clone()
|
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 {
|
pub fn from_values(red: u16, green: u16, blue: u16, alpha: Number) -> Self {
|
||||||
let repr = if alpha >= Number::from(1) {
|
let repr = if alpha >= Number::from(1) {
|
||||||
format!("#{:0>2x}{:0>2x}{:0>2x}", red, green, blue)
|
format!("#{:0>2x}{:0>2x}{:0>2x}", red, green, blue)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt::{self, Display, Write};
|
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_bigint::BigInt;
|
||||||
use num_rational::BigRational;
|
use num_rational::BigRational;
|
||||||
@ -20,6 +20,22 @@ impl Number {
|
|||||||
pub fn to_integer(&self) -> BigInt {
|
pub fn to_integer(&self) -> BigInt {
|
||||||
self.val.to_integer()
|
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 {
|
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 {
|
impl Sub for Number {
|
||||||
type Output = Self;
|
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 {
|
impl Mul for Number {
|
||||||
type Output = Self;
|
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, 50%);\n}\n",
|
||||||
"a {\n color: rgba(1, 2, 3, 0.5);\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"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user