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 4ea1a80
Show file tree
Hide file tree
Showing 41 changed files with 209 additions and 238 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
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
129 changes: 66 additions & 63 deletions rsass/src/sass/functions/color/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn register(f: &mut Scope) {
a
}
}
let rgba: Color = s.get(name!(color))?;
let color: Color = s.get(name!(color))?;
let mut args = s.get_map(name!(kwargs), CallArgs::from_value)?;
no_more_positional(&args)?;
let a_adj = take_opt(&mut args, name!(alpha), check_alpha_pm)?;
Expand All @@ -27,7 +27,7 @@ pub fn register(f: &mut Scope) {
let blu = take_opt(&mut args, name!(blue), check_channel_pm)?;
if red.is_some() || gre.is_some() || blu.is_some() {
no_more_in_space(&args, "rgb")?;
let rgba = rgba.to_rgba();
let rgba = color.to_rgba();
Ok(Rgba::new(
opt_add(rgba.red(), red),
opt_add(rgba.green(), gre),
Expand All @@ -47,18 +47,18 @@ pub fn register(f: &mut Scope) {
take_opt(&mut args, name!(blackness), check_pct_expl_pm)?;
let whi =
take_opt(&mut args, name!(whiteness), check_pct_expl_pm)?;
args.check_no_named().map_err(CallError::msg)?;
no_more_in_space(&args, "rgb")?;
if bla.is_some() || whi.is_some() {
let hwba = rgba.to_hwba();
Ok(Hwba::new(
let hwba = color.to_hwba();
let hwba = Hwba::new(
opt_add(hwba.hue(), hue),
opt_add(hwba.whiteness(), whi),
opt_add(hwba.blackness(), bla),
opt_add(hwba.alpha(), a_adj),
)
.into())
);
Ok(Rgba::from(&hwba).into())
} else if hue.is_some() || sat.is_some() || lig.is_some() {
let hsla = rgba.to_hsla();
let hsla = color.to_hsla();
let sat = opt_add(hsla.sat(), sat);
let lum = opt_add(hsla.lum(), lig);
Ok(Hsla::new(
Expand All @@ -73,9 +73,9 @@ pub fn register(f: &mut Scope) {
)
.into())
} else {
let mut rgba = rgba.clone();
rgba.set_alpha(opt_add(rgba.get_alpha(), a_adj));
Ok(rgba.into())
let mut color = color.clone();
color.set_alpha(opt_add(color.get_alpha(), a_adj));
Ok(color.into())
}
}
});
Expand All @@ -94,33 +94,39 @@ pub fn register(f: &mut Scope) {
let one: Rational = one();
let ff = Rational::from_integer(255);

let rgba: Color = s.get(name!(color))?;
let color: Color = s.get(name!(color))?;
let mut args = s.get_map(name!(kwargs), CallArgs::from_value)?;
no_more_positional(&args)?;
let a_adj = take_opt(&mut args, name!(alpha), check_pct_expl_pm)?;

take_opt(&mut args, name!(hue), check_none_scalable)?;

let red = take_opt(&mut args, name!(red), check_pct_expl_pm)?;
let gre = take_opt(&mut args, name!(green), check_pct_expl_pm)?;
let blu = take_opt(&mut args, name!(blue), check_pct_expl_pm)?;
let sat = take_opt(&mut args, name!(saturation), check_pct_expl_pm)?;
let lig = take_opt(&mut args, name!(lightness), check_pct_expl_pm)?;
let bla = take_opt(&mut args, name!(blackness), check_pct_expl_pm)?;
let whi = take_opt(&mut args, name!(whiteness), check_pct_expl_pm)?;
let a_adj = take_opt(&mut args, name!(alpha), check_pct_expl_pm)?;
args.check_no_named().map_err(CallError::msg)?;

if red.is_some() || gre.is_some() || blu.is_some() {
check_none(&[bla, whi], "RGB", "HWB")?;
check_none(&[sat, lig], "RGB", "HSL")?;
let rgba = rgba.to_rgba();
Ok(Rgba::new(
no_more_in_space(&args, "rgb")?;
let rgba = color.to_rgba();
let rgba = Rgba::new(
cmb(rgba.red(), red, ff),
cmb(rgba.green(), gre, ff),
cmb(rgba.blue(), blu, ff),
cmb(rgba.alpha(), a_adj, one),
RgbFormat::Name,
)
.into())
} else if bla.is_none() && whi.is_none() {
let hsla = rgba.to_hsla();
);
return Ok(rgba.into());
}
let sat = take_opt(&mut args, name!(saturation), check_pct_expl_pm)?;
let lig = take_opt(&mut args, name!(lightness), check_pct_expl_pm)?;
if sat.is_some() || lig.is_some() {
no_more_in_space(&args, "hsl")?;
}
let bla = take_opt(&mut args, name!(blackness), check_pct_expl_pm)?;
let whi = take_opt(&mut args, name!(whiteness), check_pct_expl_pm)?;
no_more_in_space(&args, "rgb")?;

if bla.is_none() && whi.is_none() {
let hsla = color.to_hsla();
Ok(Hsla::new(
hsla.hue(),
cmb(hsla.sat(), sat, one),
Expand All @@ -130,15 +136,19 @@ pub fn register(f: &mut Scope) {
)
.into())
} else {
check_none(&[sat, lig], "HSL", "HWB")?;
let hwba = rgba.to_hwba();
Ok(Hwba::new(
let is_rgb = color.is_rgb();
let hwba = color.to_hwba();
let hwba = Hwba::new(
hwba.hue(),
cmb(hwba.whiteness(), whi, one),
cmb(hwba.blackness(), bla, one),
cmb(hwba.alpha(), a_adj, one),
)
.into())
);
if is_rgb {
Ok(Rgba::from(&hwba).into())
} else {
Ok(hwba.into())
}
}
});

Expand All @@ -160,31 +170,35 @@ pub fn register(f: &mut Scope) {
let rgba: Color = s.get(name!(color))?;
let mut args = s.get_map(name!(kwargs), CallArgs::from_value)?;
no_more_positional(&args)?;
let alp = take_opt(&mut args, name!(alpha), check_alpha_range)?;

let red = take_opt(&mut args, name!(red), check_channel_range)?;
let gre = take_opt(&mut args, name!(green), check_channel_range)?;
let blu = take_opt(&mut args, name!(blue), check_channel_range)?;
let hue = take_opt(&mut args, name!(hue), check_hue)?;
let sat =
take_opt(&mut args, name!(saturation), check_pct_opt_range)?;
let lig = take_opt(&mut args, name!(lightness), check_pct_opt_range)?;
let bla = take_opt(&mut args, name!(blackness), check_expl_pct)?;
let whi = take_opt(&mut args, name!(whiteness), check_expl_pct)?;
let alp = take_opt(&mut args, name!(alpha), check_alpha_range)?;
args.check_no_named().map_err(CallError::msg)?;

if red.is_some() || gre.is_some() || blu.is_some() {
check_none(&[hue, sat, lig], "RGB", "HSL")?;
check_none(&[bla, whi], "RGB", "HWB")?;
no_more_in_space(&args, "rgb")?;
let rgba = rgba.to_rgba();
Ok(Rgba::new(
let rgba = Rgba::new(
red.unwrap_or_else(|| rgba.red()),
gre.unwrap_or_else(|| rgba.green()),
blu.unwrap_or_else(|| rgba.blue()),
alp.unwrap_or_else(|| rgba.alpha()),
rgba.source(),
)
.into())
} else if bla.is_none() && whi.is_none() {
);
return Ok(rgba.into());
}
let hue = take_opt(&mut args, name!(hue), check_hue)?;
let sat =
take_opt(&mut args, name!(saturation), check_pct_opt_range)?;
let lig = take_opt(&mut args, name!(lightness), check_pct_opt_range)?;
if sat.is_some() || lig.is_some() {
no_more_in_space(&args, "hsl")?;
}
let bla = take_opt(&mut args, name!(blackness), check_expl_pct)?;
let whi = take_opt(&mut args, name!(whiteness), check_expl_pct)?;
no_more_in_space(&args, "rgb")?;

if bla.is_none() && whi.is_none() {
let hsla = rgba.to_hsla();
Ok(Hsla::new(
hue.unwrap_or_else(|| hsla.hue()),
Expand All @@ -195,15 +209,14 @@ pub fn register(f: &mut Scope) {
)
.into())
} else {
check_none(&[sat, lig], "HSL", "HWB")?;
let hwba = rgba.to_hwba();
Ok(Hwba::new(
let hwba = Hwba::new(
hue.unwrap_or_else(|| hwba.hue()),
whi.unwrap_or_else(|| hwba.whiteness()),
bla.unwrap_or_else(|| hwba.blackness()),
alp.unwrap_or_else(|| hwba.alpha()),
)
.into())
);
Ok(Rgba::from(&hwba).into())
}
});
def!(f, ie_hex_str(color), |s| {
Expand Down Expand Up @@ -247,18 +260,8 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
}
}

fn check_none(
args: &[Option<Rational>],
kind: &str,
with_kind: &str,
) -> Result<(), CallError> {
if args.iter().all(Option::is_none) {
Ok(())
} else {
Err(CallError::msg(format!(
"{kind} parameters may not be passed along with {with_kind} parameters."
)))
}
fn check_none_scalable(_: Value) -> Result<(), String> {
Err("Channel isn't scalable.".into())
}

fn no_more_positional(args: &CallArgs) -> Result<(), CallError> {
Expand Down
17 changes: 2 additions & 15 deletions rsass/src/sass/functions/color/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn register(f: &mut Scope) {
match s.get(name!(color))? {
Value::Color(col, _) => {
let w = s.get_map(name!(weight), check_pct_range)?;
Ok(do_invert(col, w))
Ok(col.invert(w).into())
}
col => {
let w = s.get_map(name!(weight), check_pct_range)?;
Expand Down Expand Up @@ -94,7 +94,7 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
match s.get(name!(color))? {
Value::Color(col, _) => {
let w = s.get_map(name!(weight), check_pct_range)?;
Ok(do_invert(col, w))
Ok(col.invert(w).into())
}
col => {
let w = s.get_map(name!(weight), check_pct_range)?;
Expand All @@ -116,19 +116,6 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
}
}

fn do_invert(col: Color, w: Rational) -> Value {
let rgba = col.to_rgba();
let inv = |v: Rational| -(v - 255) * w + v * -(w - 1);
Rgba::new(
inv(rgba.red()),
inv(rgba.green()),
inv(rgba.blue()),
rgba.alpha(),
rgba.source(),
)
.into()
}

fn do_rgba(fn_name: &Name, s: &ResolvedArgs) -> Result<Value, CallError> {
let a1 = FormalArgs::new(vec![one_arg!(channels)]);
let a1b = FormalArgs::new(vec![one_arg!(color), one_arg!(alpha)]);
Expand Down
14 changes: 13 additions & 1 deletion rsass/src/value/colors/hsla.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::output::{Format, Formatted};
use crate::value::{Number, Rational};
use num_traits::{one, zero, Signed};
use num_traits::{one, zero, One as _, Signed};
use std::fmt::{self, Display};

/// A color defined by hue, saturation, luminance, and alpha.
Expand Down Expand Up @@ -55,6 +55,18 @@ impl Hsla {
pub fn set_alpha(&mut self, alpha: Rational) {
self.alpha = alpha.clamp(zero(), one());
}

pub(crate) fn invert(&self, weight: Rational) -> Self {
let one = Rational::one();
Self {
hue: deg_mod(self.hue + 180),
sat: self.sat,
lum: (one - self.lum) * weight + self.lum * (one - weight),
alpha: self.alpha,
hsla_format: self.hsla_format,
}
}

pub(crate) fn reset_source(&mut self) {
self.hsla_format = false;
}
Expand Down
Loading

0 comments on commit 4ea1a80

Please sign in to comment.