From add16981808b289581ad786abfa40c16af0a63a3 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Mon, 26 Dec 2022 20:20:11 -0500 Subject: [PATCH] unbox color and only store alpha once --- src/ast/expr.rs | 2 +- src/builtin/functions/color/hsl.rs | 22 +++---- src/builtin/functions/color/hwb.rs | 4 +- src/builtin/functions/color/opacity.rs | 4 +- src/builtin/functions/color/other.rs | 30 ++++----- src/builtin/functions/color/rgb.rs | 10 +-- src/color/mod.rs | 85 ++++++++++---------------- src/parse/value.rs | 10 ++- src/value/mod.rs | 5 +- 9 files changed, 72 insertions(+), 100 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index e73d3cb..654efdb 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -55,7 +55,7 @@ pub(crate) enum AstExpr { name: CalculationName, args: Vec, }, - Color(Box), + Color(Color), FunctionCall(FunctionCallExpr), If(Box), InterpolatedFunction(InterpolatedFunction), diff --git a/src/builtin/functions/color/hsl.rs b/src/builtin/functions/color/hsl.rs index fe20a8e..629562b 100644 --- a/src/builtin/functions/color/hsl.rs +++ b/src/builtin/functions/color/hsl.rs @@ -59,12 +59,12 @@ fn hsl_3_args( visitor, )?; - Ok(Value::Color(Box::new(Color::from_hsla_fn( + Ok(Value::Color(Color::from_hsla_fn( Number(hue.num().rem_euclid(360.0)), saturation.num() / Number::from(100), lightness.num() / Number::from(100), Number(alpha), - )))) + ))) } fn inner_hsl( @@ -181,7 +181,7 @@ pub(crate) fn adjust_hue(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas .assert_number_with_name("degrees", args.span())? .num(); - Ok(Value::Color(Box::new(color.adjust_hue(degrees)))) + Ok(Value::Color(color.adjust_hue(degrees))) } fn lighten(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -202,7 +202,7 @@ fn lighten(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult .assert_number_with_name("amount", args.span())?; let amount = bound!(args, "amount", amount.num(), amount.unit, 0, 100) / Number(100.0); - Ok(Value::Color(Box::new(color.lighten(amount)))) + Ok(Value::Color(color.lighten(amount))) } fn darken(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -235,7 +235,7 @@ fn darken(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult .into()) } }; - Ok(Value::Color(Box::new(color.darken(amount)))) + Ok(Value::Color(color.darken(amount))) } fn saturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -293,7 +293,7 @@ fn saturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult SassResult { @@ -326,7 +326,7 @@ fn desaturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult SassResult { @@ -351,7 +351,7 @@ pub(crate) fn grayscale(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass .into()) } }; - Ok(Value::Color(Box::new(color.desaturate(Number::one())))) + Ok(Value::Color(color.desaturate(Number::one()))) } pub(crate) fn complement(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -366,7 +366,7 @@ pub(crate) fn complement(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas .into()) } }; - Ok(Value::Color(Box::new(color.complement()))) + Ok(Value::Color(color.complement())) } pub(crate) fn invert(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -398,9 +398,7 @@ pub(crate) fn invert(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRes } }; match args.get_err(0, "color")? { - Value::Color(c) => Ok(Value::Color(Box::new( - c.invert(weight.unwrap_or_else(Number::one)), - ))), + Value::Color(c) => Ok(Value::Color(c.invert(weight.unwrap_or_else(Number::one)))), Value::Dimension(SassNumber { num: n, unit: u, diff --git a/src/builtin/functions/color/hwb.rs b/src/builtin/functions/color/hwb.rs index 29d7943..c84d32e 100644 --- a/src/builtin/functions/color/hwb.rs +++ b/src/builtin/functions/color/hwb.rs @@ -90,12 +90,12 @@ fn hwb_inner(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult Number::one(), }; - Ok(Value::Color(Box::new(Color::from_hwb( + Ok(Value::Color(Color::from_hwb( hue, whiteness.num, blackness.num, alpha, - )))) + ))) } pub(crate) fn hwb(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { diff --git a/src/builtin/functions/color/opacity.rs b/src/builtin/functions/color/opacity.rs index 60f284a..146165d 100644 --- a/src/builtin/functions/color/opacity.rs +++ b/src/builtin/functions/color/opacity.rs @@ -111,7 +111,7 @@ fn opacify(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult let amount = bound!(args, "amount", amount.num(), amount.unit(), 0, 1); - Ok(Value::Color(Box::new(color.fade_in(amount)))) + Ok(Value::Color(color.fade_in(amount))) } fn transparentize(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { @@ -141,7 +141,7 @@ fn transparentize(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult .into()) } }; - Ok(Value::Color(Box::new(color.fade_out(amount)))) + Ok(Value::Color(color.fade_out(amount))) } pub(crate) fn declare(f: &mut GlobalFunctionMap) { diff --git a/src/builtin/functions/color/other.rs b/src/builtin/functions/color/other.rs index 12507ca..3c8d296 100644 --- a/src/builtin/functions/color/other.rs +++ b/src/builtin/functions/color/other.rs @@ -64,12 +64,12 @@ pub(crate) fn change_color(mut args: ArgumentResult, visitor: &mut Visitor) -> S opt_rgba!(args, blue, "blue", 0, 255); if red.is_some() || green.is_some() || blue.is_some() { - return Ok(Value::Color(Box::new(Color::from_rgba( + return Ok(Value::Color(Color::from_rgba( red.unwrap_or_else(|| color.red()), green.unwrap_or_else(|| color.green()), blue.unwrap_or_else(|| color.blue()), alpha.unwrap_or_else(|| color.alpha()), - )))); + ))); } let hue = match args.default_named_arg("hue", Value::Null) { @@ -91,16 +91,16 @@ pub(crate) fn change_color(mut args: ArgumentResult, visitor: &mut Visitor) -> S 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 Ok(Value::Color(Box::new(Color::from_hsla( + return Ok(Value::Color(Color::from_hsla( hue.unwrap_or(this_hue), saturation.unwrap_or(this_saturation), luminance.unwrap_or(this_luminance), alpha.unwrap_or(this_alpha), - )))); + ))); } Ok(Value::Color(if let Some(a) = alpha { - Box::new(color.with_alpha(a)) + color.with_alpha(a) } else { color })) @@ -124,12 +124,12 @@ pub(crate) fn adjust_color(mut args: ArgumentResult, visitor: &mut Visitor) -> S opt_rgba!(args, blue, "blue", -255, 255); if red.is_some() || green.is_some() || blue.is_some() { - return Ok(Value::Color(Box::new(Color::from_rgba( + return Ok(Value::Color(Color::from_rgba( color.red() + red.unwrap_or_else(Number::zero), color.green() + green.unwrap_or_else(Number::zero), color.blue() + blue.unwrap_or_else(Number::zero), color.alpha() + alpha.unwrap_or_else(Number::zero), - )))); + ))); } let hue = match args.default_named_arg("hue", Value::Null) { @@ -151,17 +151,17 @@ pub(crate) fn adjust_color(mut args: ArgumentResult, visitor: &mut Visitor) -> S 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 Ok(Value::Color(Box::new(Color::from_hsla( + return Ok(Value::Color(Color::from_hsla( this_hue + hue.unwrap_or_else(Number::zero), this_saturation + saturation.unwrap_or_else(Number::zero), this_luminance + luminance.unwrap_or_else(Number::zero), this_alpha + alpha.unwrap_or_else(Number::zero), - )))); + ))); } Ok(Value::Color(if let Some(a) = alpha { let temp_alpha = color.alpha(); - Box::new(color.with_alpha(temp_alpha + a)) + color.with_alpha(temp_alpha + a) } else { color })) @@ -227,7 +227,7 @@ pub(crate) fn scale_color(mut args: ArgumentResult, visitor: &mut Visitor) -> Sa opt_scale_arg!(args, blue, "blue", -100, 100); if red.is_some() || green.is_some() || blue.is_some() { - return Ok(Value::Color(Box::new(Color::from_rgba( + return Ok(Value::Color(Color::from_rgba( scale( color.red(), red.unwrap_or_else(Number::zero), @@ -248,7 +248,7 @@ pub(crate) fn scale_color(mut args: ArgumentResult, visitor: &mut Visitor) -> Sa alpha.unwrap_or_else(Number::zero), Number::one(), ), - )))); + ))); } opt_scale_arg!(args, saturation, "saturation", -100, 100); @@ -257,7 +257,7 @@ pub(crate) fn scale_color(mut args: ArgumentResult, visitor: &mut Visitor) -> Sa if 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 Ok(Value::Color(Box::new(Color::from_hsla( + return Ok(Value::Color(Color::from_hsla( scale(this_hue, Number::zero(), Number::from(360)), scale( this_saturation, @@ -274,12 +274,12 @@ pub(crate) fn scale_color(mut args: ArgumentResult, visitor: &mut Visitor) -> Sa alpha.unwrap_or_else(Number::zero), Number::one(), ), - )))); + ))); } Ok(Value::Color(if let Some(a) = alpha { let temp_alpha = color.alpha(); - Box::new(color.with_alpha(scale(temp_alpha, a, Number::one()))) + color.with_alpha(scale(temp_alpha, a, Number::one())) } else { color })) diff --git a/src/builtin/functions/color/rgb.rs b/src/builtin/functions/color/rgb.rs index b5a94c5..840f20d 100644 --- a/src/builtin/functions/color/rgb.rs +++ b/src/builtin/functions/color/rgb.rs @@ -72,9 +72,9 @@ fn inner_rgb_2_arg( let color = color.assert_color_with_name("color", args.span())?; let alpha = alpha.assert_number_with_name("alpha", args.span())?; - Ok(Value::Color(Box::new(color.with_alpha(Number( + Ok(Value::Color(color.with_alpha(Number( percentage_or_unitless(&alpha, 1.0, "alpha", args.span(), visitor)?, - ))))) + )))) } fn inner_rgb_3_arg( @@ -120,7 +120,7 @@ fn inner_rgb_3_arg( let green = green.assert_number_with_name("green", span)?; let blue = blue.assert_number_with_name("blue", span)?; - Ok(Value::Color(Box::new(Color::from_rgba_fn( + Ok(Value::Color(Color::from_rgba_fn( Number(fuzzy_round(percentage_or_unitless( &red, 255.0, "red", span, visitor, )?)), @@ -144,7 +144,7 @@ fn inner_rgb_3_arg( .transpose()? .unwrap_or(1.0), ), - )))) + ))) } pub(crate) fn percentage_or_unitless( @@ -450,7 +450,7 @@ pub(crate) fn mix(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult .into()) } }; - Ok(Value::Color(Box::new(color1.mix(&color2, weight)))) + Ok(Value::Color(color1.mix(&color2, weight))) } pub(crate) fn declare(f: &mut GlobalFunctionMap) { diff --git a/src/color/mod.rs b/src/color/mod.rs index 649d848..6bd0f88 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -23,8 +23,9 @@ mod name; // todo: only store alpha once on color #[derive(Debug, Clone)] pub(crate) struct Color { - rgba: Rgba, - hsla: Option, + rgba: Rgb, + hsla: Option, + alpha: Number, pub format: ColorFormat, } @@ -41,6 +42,12 @@ pub(crate) enum ColorFormat { impl PartialEq for Color { fn eq(&self, other: &Self) -> bool { + if self.alpha != other.alpha + && !(self.alpha >= Number::one() && other.alpha >= Number::one()) + { + return false; + } + self.rgba == other.rgba } } @@ -56,21 +63,17 @@ impl Color { format: ColorFormat, ) -> Color { Color { - rgba: Rgba::new(red, green, blue, alpha), + rgba: Rgb::new(red, green, blue), + alpha, hsla: None, format, } } - const fn new_hsla( - red: Number, - green: Number, - blue: Number, - alpha: Number, - hsla: Hsla, - ) -> Color { + const fn new_hsla(red: Number, green: Number, blue: Number, alpha: Number, hsla: Hsl) -> Color { Color { - rgba: Rgba::new(red, green, blue, alpha), + rgba: Rgb::new(red, green, blue), + alpha, hsla: Some(hsla), format: ColorFormat::Infer, } @@ -78,14 +81,13 @@ impl Color { } #[derive(Debug, Clone)] -struct Rgba { +struct Rgb { red: Number, green: Number, blue: Number, - alpha: Number, } -impl PartialEq for Rgba { +impl PartialEq for Rgb { fn eq(&self, other: &Self) -> bool { if self.red != other.red && !(self.red >= Number::from(255.0) && other.red >= Number::from(255.0)) @@ -102,47 +104,31 @@ impl PartialEq for Rgba { { return false; } - if self.alpha != other.alpha - && !(self.alpha >= Number::one() && other.alpha >= Number::one()) - { - return false; - } true } } -impl Eq for Rgba {} +impl Eq for Rgb {} -impl Rgba { - pub const fn new(red: Number, green: Number, blue: Number, alpha: Number) -> Self { - Rgba { - red, - green, - blue, - alpha, - } - } - - pub fn alpha(&self) -> Number { - self.alpha +impl Rgb { + pub const fn new(red: Number, green: Number, blue: Number) -> Self { + Rgb { red, green, blue } } } #[derive(Debug, Clone)] -struct Hsla { +struct Hsl { hue: Number, saturation: Number, luminance: Number, - alpha: Number, } -impl Hsla { - pub const fn new(hue: Number, saturation: Number, luminance: Number, alpha: Number) -> Self { - Hsla { +impl Hsl { + pub const fn new(hue: Number, saturation: Number, luminance: Number) -> Self { + Hsl { hue, saturation, luminance, - alpha, } } @@ -157,18 +143,15 @@ impl Hsla { pub fn luminance(&self) -> Number { self.luminance } - - pub fn alpha(&self) -> Number { - self.alpha - } } // RGBA color functions impl Color { pub fn new(red: u8, green: u8, blue: u8, alpha: u8, format: String) -> Self { Color { - rgba: Rgba::new(red.into(), green.into(), blue.into(), alpha.into()), + rgba: Rgb::new(red.into(), green.into(), blue.into()), hsla: None, + alpha: alpha.into(), format: ColorFormat::Literal(format), } } @@ -319,7 +302,7 @@ impl Color { pub fn as_hsla(&self) -> (Number, Number, Number, Number) { if let Some(h) = &self.hsla { - return (h.hue(), h.saturation(), h.luminance(), h.alpha()); + return (h.hue(), h.saturation(), h.luminance(), self.alpha()); } let red = self.red() / Number::from(255.0); @@ -394,12 +377,7 @@ impl Color { /// Create RGBA representation from HSLA values pub fn from_hsla(hue: Number, saturation: Number, lightness: Number, alpha: Number) -> Self { - let hsla = Hsla::new( - hue, - saturation.clamp(0.0, 1.0), - lightness.clamp(0.0, 1.0), - alpha, - ); + let hsla = Hsl::new(hue, saturation.clamp(0.0, 1.0), lightness.clamp(0.0, 1.0)); let scaled_hue = hue.0 / 360.0; let scaled_saturation = saturation.0.clamp(0.0, 1.0); @@ -463,11 +441,10 @@ impl Color { /// Opacity color functions impl Color { pub fn alpha(&self) -> Number { - let a = self.rgba.alpha(); - if a > Number::one() { - a / Number::from(255.0) + if self.alpha > Number::one() { + self.alpha / Number::from(255.0) } else { - a + self.alpha } } diff --git a/src/parse/value.rs b/src/parse/value.rs index f688045..19cd536 100644 --- a/src/parse/value.rs +++ b/src/parse/value.rs @@ -839,7 +839,7 @@ impl<'a, 'c, P: StylesheetParser<'a>> ValueParser<'a, 'c, P> { }) ) { let color = self.parse_hex_color_contents(parser)?; - return Ok(AstExpr::Color(Box::new(color)).span(parser.toks_mut().span_from(start))); + return Ok(AstExpr::Color(color).span(parser.toks_mut().span_from(start))); } let after_hash = parser.toks().cursor(); @@ -847,9 +847,7 @@ impl<'a, 'c, P: StylesheetParser<'a>> ValueParser<'a, 'c, P> { if is_hex_color(&ident) { parser.toks_mut().set_cursor(after_hash); let color = self.parse_hex_color_contents(parser)?; - return Ok( - AstExpr::Color(Box::new(color)).span(parser.toks_mut().span_from(after_hash)) - ); + return Ok(AstExpr::Color(color).span(parser.toks_mut().span_from(after_hash))); } let mut buffer = Interpolation::new(); @@ -1178,13 +1176,13 @@ impl<'a, 'c, P: StylesheetParser<'a>> ValueParser<'a, 'c, P> { } if let Some(color) = NAMED_COLORS.get_by_name(lower_ref.as_str()) { - return Ok(AstExpr::Color(Box::new(Color::new( + return Ok(AstExpr::Color(Color::new( color[0], color[1], color[2], color[3], plain.to_owned(), - ))) + )) .span(parser.toks_mut().span_from(start))); } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 7ff363c..26b0263 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -35,8 +35,7 @@ pub(crate) enum Value { Null, Dimension(SassNumber), List(Vec, ListSeparator, Brackets), - // todo: benchmark unboxing this, now that it's smaller - Color(Box), + Color(Color), String(String, QuoteKind), Map(SassMap), ArgList(ArgList), @@ -239,7 +238,7 @@ impl Value { pub fn assert_color_with_name(self, name: &str, span: Span) -> SassResult { match self { - Value::Color(c) => Ok(*c), + Value::Color(c) => Ok(c), _ => Err(( format!("${name}: {} is not a color.", self.inspect(span)?), span,