Skip to content

Commit

Permalink
A bunch of updates for hwb colors.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaj committed Sep 16, 2024
1 parent 039d315 commit 0284bcd
Show file tree
Hide file tree
Showing 42 changed files with 243 additions and 292 deletions.
4 changes: 2 additions & 2 deletions rsass/src/css/valueformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ impl<'a> Display for Formatted<'a, Value> {
write!(out, "get-function(\"{name}\")")
}
Value::Numeric(ref num, _) => num.format(self.format).fmt(out),
Value::Color(ref rgba, ref name) => {
Value::Color(ref col, ref name) => {
if let Some(ref name) = *name {
name.fmt(out)
} else {
rgba.format(self.format).fmt(out)
col.format(self.format).fmt(out)
}
}
Value::List(ref v, sep, brackets) => {
Expand Down
88 changes: 34 additions & 54 deletions rsass/src/sass/functions/color/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,61 +14,41 @@ pub enum Channels {
impl TryFrom<Value> for Channels {
type Error = ChaError;
fn try_from(channels: Value) -> Result<Self, ChaError> {
if is_special(&channels) {
Ok(Self::Special(channels))
} else if let Value::List(vec, sep, bracketed) = channels.clone() {
if bracketed {
Err(ChaError::Bracketed)
} else {
use crate::value::Operator::Div;
match sep {
Some(ListSeparator::Comma) => {
return Err(ChaError::BadSep);
}
Some(ListSeparator::Slash) => match vec.as_slice() {
[Value::List(inner, i_s, i_b), a] => {
if *i_b {
return Err(ChaError::Bracketed);
}
if i_s.unwrap_or_default() != ListSeparator::Space
{
return Err(ChaError::BadSep);
}
Ok(inner_channels(inner)?.map_or_else(
|| Self::Special(channels),
|[c1, c2, c3]| {
Self::Data([c1, c2, c3, a.clone()])
},
))
}
[h, _a] => {
if is_special(h) {
Ok(Self::Special(channels))
} else {
Err(ChaError::Missing1)
}
}
list => Err(ChaError::SlashBadNum(list.len())),
},
_ => match vec.as_slice() {
[r, g, Value::BinOp(op)] if op.op() == Div => {
Ok(Self::Data([
r.clone(),
g.clone(),
op.a().clone(),
op.b().clone(),
]))
}
other => Ok(inner_channels(other)?
.map(|[c1, c2, c3]| {
Self::Data([c1, c2, c3, Value::Null])
})
.unwrap_or_else(|| Self::Special(channels))),
},
}
use crate::value::Operator::Div;
match &channels {
c if is_special(c) => Ok(Self::Special(channels)),
Value::List(_, _, true) => Err(ChaError::Bracketed),
Value::List(_, Some(ListSeparator::Comma), _) => {
Err(ChaError::BadSep)
}
} else {
Err(ChaError::Missing1)
Value::List(v, Some(ListSeparator::Slash), _) => match &v[..] {
[Value::List(_, _, true), _] => Err(ChaError::Bracketed),
[Value::List(_, Some(i_s), _), _]
if *i_s != ListSeparator::Space =>
{
Err(ChaError::BadSep)
}
[Value::List(inner, _, _), a] => Ok(inner_channels(inner)?
.map(|[c1, c2, c3]| Self::Data([c1, c2, c3, a.clone()]))
.unwrap_or_else(|| Self::Special(channels))),
[h, _a] if is_special(h) => Ok(Self::Special(channels)),
[_, _] => Err(ChaError::Missing1),
list => Err(ChaError::SlashBadNum(list.len())),
},
Value::List(vec, _, false) => match &vec[..] {
[r, g, Value::BinOp(op)] if op.op() == Div => {
Ok(Self::Data([
r.clone(),
g.clone(),
op.a().clone(),
op.b().clone(),
]))
}
other => Ok(inner_channels(other)?
.map(|[c1, c2, c3]| Self::Data([c1, c2, c3, Value::Null]))
.unwrap_or_else(|| Self::Special(channels))),
},
_ => Err(ChaError::Missing1),
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions rsass/src/sass/functions/color/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ pub fn register(f: &mut Scope) {
});
def!(f, grayscale(color), |args| match args.get(name!(color))? {
Value::Color(col, _) => {
let is_rgb = col.is_rgb();
let col = col.to_hsla();
Ok(Hsla::new(col.hue(), zero(), col.lum(), col.alpha(), false)
.into())
Ok(
Hsla::new(col.hue(), zero(), col.lum(), col.alpha(), !is_rgb)
.into(),
)
}
v @ Value::Numeric(..) => Ok(Value::call("grayscale", [v])),
v => Err(is_not(&v, "a color")).named(name!(color)),
Expand Down
41 changes: 29 additions & 12 deletions rsass/src/sass/functions/color/hwb.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
use super::super::FunctionMap;
use super::hsl::percentage;
use super::{
check_alpha, check_expl_pct, eval_inner, is_not, CallError, CheckedArg,
ResolvedArgs,
check_alpha, check_expl_pct, eval_inner, is_not, relative_color,
CallError, CheckedArg, ResolvedArgs,
};
use crate::css::{CallArgs, Value};
use crate::output::Format;
use crate::sass::FormalArgs;
use crate::value::{Color, Hwba, ListSeparator, Numeric, Rational, Unit};
use crate::value::{
Color, Hwba, ListSeparator, Numeric, Rational, Rgba, Unit,
};
use crate::Scope;
use num_traits::{one, zero};

pub fn register(f: &mut Scope) {
def_va!(f, hwb(kwargs), hwb);
def!(f, blackness(color), |s| {
// Blackness of the rgb approximation that can be represented in css.
let (r, g, b, _a) = Color::to_rgba(&s.get(name!(color))?).to_bytes();
let max_c = *[r, g, b].iter().max().unwrap();
Ok(percentage(Rational::new((255 - max_c).into(), 255)))
let color: Color = s.get(name!(color))?;
let hwb = color.to_hwba();
Ok(percentage(hwb.blackness()))
});
def!(f, whiteness(color), |s| {
// Whiteness of the rgb approximation that can be represented in css.
let (r, g, b, _a) = Color::to_rgba(&s.get(name!(color))?).to_bytes();
let min_c = *[r, g, b].iter().min().unwrap();
Ok(percentage(Rational::new(min_c.into(), 255)))
let color: Color = s.get(name!(color))?;
let hwb = color.to_hwba();
Ok(percentage(hwb.whiteness()))
});
}

pub fn expose(m: &Scope, global: &mut FunctionMap) {
global.insert(name!(hwb), m.get_lfunction(&name!(hwb)));
}

fn hwb(s: &ResolvedArgs) -> Result<Value, CallError> {
let args = s.get_map(name!(kwargs), CallArgs::from_value)?;
if relative_color(&args) {
return Ok(Value::Call("hwb".to_string(), args));
}
let (hue, w, b, a) = if args.len() < 3 {
let a1 = FormalArgs::new(vec![one_arg!(channels)]);
eval_inner(&name!(hwb), &a1, s, args)
Expand All @@ -51,7 +60,15 @@ fn hwb(s: &ResolvedArgs) -> Result<Value, CallError> {
let w = check_expl_pct(w).named(name!(whiteness))?;
let b = check_expl_pct(b).named(name!(blackness))?;
let a = check_alpha(a).named(name!(alpha))?;
Ok(Hwba::new(hue, w, b, a).into())
// I don't really agree with this, but it makes tests pass.
let hue = if w + b >= one() { zero() } else { hue };
let hwba = Hwba::new(hue, w, b, a);
let rgba = Rgba::from(&hwba);
if rgba.is_integer() {
Ok(rgba.into())
} else {
Ok(hwba.into())
}
}

fn hwb_from_channels(
Expand Down
1 change: 1 addition & 0 deletions rsass/src/sass/functions/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub fn create_module() -> Scope {
pub fn expose(m: &Scope, global: &mut FunctionMap) {
rgb::expose(m, global);
hsl::expose(m, global);
hwb::expose(m, global);
other::expose(m, global);
}

Expand Down
Loading

0 comments on commit 0284bcd

Please sign in to comment.