diff options
Diffstat (limited to 'servo/components/style/color/parsing.rs')
-rw-r--r-- | servo/components/style/color/parsing.rs | 516 |
1 files changed, 257 insertions, 259 deletions
diff --git a/servo/components/style/color/parsing.rs b/servo/components/style/color/parsing.rs index 68bcee6c56..0c8fb2dbaf 100644 --- a/servo/components/style/color/parsing.rs +++ b/servo/components/style/color/parsing.rs @@ -4,17 +4,15 @@ #![deny(missing_docs)] -//! Fairly complete css-color implementation. -//! Relative colors, color-mix, system colors, and other such things require better calc() support -//! and integration. +//! Parsing for CSS colors. use super::{ color_function::ColorFunction, component::{ColorComponent, ColorComponentType}, - AbsoluteColor, + AbsoluteColor, ColorFlags, ColorSpace, }; use crate::{ - parser::ParserContext, + parser::{Parse, ParserContext}, values::{ generics::calc::CalcUnits, specified::{ @@ -27,7 +25,6 @@ use cssparser::{ color::{clamp_floor_256_f32, clamp_unit_f32, parse_hash_color, PredefinedColorSpace, OPAQUE}, match_ignore_ascii_case, CowRcStr, Parser, Token, }; -use std::str::FromStr; use style_traits::{ParseError, StyleParseErrorKind}; /// Returns true if the relative color syntax pref is enabled. @@ -36,11 +33,35 @@ pub fn rcs_enabled() -> bool { static_prefs::pref!("layout.css.relative-color-syntax.enabled") } -impl From<u8> for ColorComponent<u8> { - #[inline] - fn from(value: u8) -> Self { - ColorComponent::Value(value) - } +/// Represents a channel keyword inside a color. +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, PartialOrd, ToCss, ToShmem)] +pub enum ChannelKeyword { + /// alpha + Alpha, + /// a + A, + /// b, blackness, blue + B, + /// chroma + C, + /// green + G, + /// hue + H, + /// lightness + L, + /// red + R, + /// saturation + S, + /// whiteness + W, + /// x + X, + /// y + Y, + /// z + Z, } /// Return the named color with the given name. @@ -65,7 +86,7 @@ pub fn parse_color_keyword(ident: &str) -> Result<SpecifiedColor, ()> { /// Parse a CSS color using the specified [`ColorParser`] and return a new color /// value on success. pub fn parse_color_with<'i, 't>( - color_parser: &ColorParser<'_, '_>, + context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<SpecifiedColor, ParseError<'i>> { let location = input.current_source_location(); @@ -80,7 +101,7 @@ pub fn parse_color_with<'i, 't>( let name = name.clone(); return input.parse_nested_block(|arguments| { Ok(SpecifiedColor::from_absolute_color( - parse_color_function(color_parser, name, arguments)?.resolve_to_absolute(), + parse_color_function(context, name, arguments)?.resolve_to_absolute(), )) }); }, @@ -92,19 +113,26 @@ pub fn parse_color_with<'i, 't>( /// Parse one of the color functions: rgba(), lab(), color(), etc. #[inline] fn parse_color_function<'i, 't>( - color_parser: &ColorParser<'_, '_>, + context: &ParserContext, name: CowRcStr<'i>, arguments: &mut Parser<'i, 't>, ) -> Result<ColorFunction, ParseError<'i>> { + let origin_color = parse_origin_color(context, arguments)?; + + let component_parser = ComponentParser { + context, + origin_color, + }; + let color = match_ignore_ascii_case! { &name, - "rgb" | "rgba" => parse_rgb(color_parser, arguments), - "hsl" | "hsla" => parse_hsl(color_parser, arguments), - "hwb" => parse_hwb(color_parser, arguments), - "lab" => parse_lab_like(color_parser, arguments, ColorFunction::Lab), - "lch" => parse_lch_like(color_parser, arguments, ColorFunction::Lch), - "oklab" => parse_lab_like(color_parser, arguments, ColorFunction::Oklab), - "oklch" => parse_lch_like(color_parser, arguments, ColorFunction::Oklch), - "color" => parse_color_with_color_space(color_parser, arguments), + "rgb" | "rgba" => parse_rgb(&component_parser, arguments), + "hsl" | "hsla" => parse_hsl(&component_parser, arguments), + "hwb" => parse_hwb(&component_parser, arguments), + "lab" => parse_lab_like(&component_parser, arguments, ColorSpace::Lab, ColorFunction::Lab), + "lch" => parse_lch_like(&component_parser, arguments, ColorSpace::Lch, ColorFunction::Lch), + "oklab" => parse_lab_like(&component_parser, arguments, ColorSpace::Oklab, ColorFunction::Oklab), + "oklch" => parse_lch_like(&component_parser, arguments, ColorSpace::Oklch, ColorFunction::Oklch), + "color" =>parse_color_with_color_space(&component_parser, arguments), _ => return Err(arguments.new_unexpected_token_error(Token::Ident(name))), }?; @@ -113,34 +141,6 @@ fn parse_color_function<'i, 't>( Ok(color) } -fn parse_legacy_alpha<'i, 't>( - color_parser: &ColorParser<'_, '_>, - arguments: &mut Parser<'i, 't>, -) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { - if !arguments.is_exhausted() { - arguments.expect_comma()?; - color_parser.parse_number_or_percentage(arguments, false) - } else { - Ok(ColorComponent::Value(NumberOrPercentage::Number { - value: OPAQUE, - })) - } -} - -fn parse_modern_alpha<'i, 't>( - color_parser: &ColorParser<'_, '_>, - arguments: &mut Parser<'i, 't>, -) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { - if !arguments.is_exhausted() { - arguments.expect_delim('/')?; - color_parser.parse_number_or_percentage(arguments, true) - } else { - Ok(ColorComponent::Value(NumberOrPercentage::Number { - value: OPAQUE, - })) - } -} - impl ColorComponent<NumberOrPercentage> { /// Return true if the component contains a percentage. pub fn is_percentage(&self) -> Result<bool, ()> { @@ -153,9 +153,9 @@ impl ColorComponent<NumberOrPercentage> { /// Parse the relative color syntax "from" syntax `from <color>`. fn parse_origin_color<'i, 't>( - color_parser: &ColorParser<'_, '_>, + context: &ParserContext, arguments: &mut Parser<'i, 't>, -) -> Result<Option<SpecifiedColor>, ParseError<'i>> { +) -> Result<Option<AbsoluteColor>, ParseError<'i>> { if !rcs_enabled() { return Ok(None); } @@ -169,72 +169,112 @@ fn parse_origin_color<'i, 't>( return Ok(None); } + let location = arguments.current_source_location(); + // We still fail if we can't parse the origin color. - parse_color_with(color_parser, arguments).map(|color| Some(color)) + let origin_color = SpecifiedColor::parse(context, arguments)?; + + // Right now we only handle absolute colors. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1890972 + let Some(computed) = origin_color.to_computed_color(None) else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + }; + + Ok(Some(computed.resolve_to_absolute(&AbsoluteColor::BLACK))) } #[inline] fn parse_rgb<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, ) -> Result<ColorFunction, ParseError<'i>> { - let origin_color = parse_origin_color(color_parser, arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser.origin_color.map(|c| { + let mut c = c.to_color_space(ColorSpace::Srgb); + c.flags.insert(ColorFlags::IS_LEGACY_SRGB); + c + }), + }; let location = arguments.current_source_location(); - let maybe_red = color_parser.parse_number_or_percentage(arguments, true)?; + let maybe_red = component_parser.parse_number_or_percentage(arguments, true)?; // If the first component is not "none" and is followed by a comma, then we // are parsing the legacy syntax. Legacy syntax also doesn't support an // origin color. - let is_legacy_syntax = origin_color.is_none() && + let is_legacy_syntax = component_parser.origin_color.is_none() && !maybe_red.is_none() && arguments.try_parse(|p| p.expect_comma()).is_ok(); - let (red, green, blue, alpha) = if is_legacy_syntax { + Ok(if is_legacy_syntax { let Ok(is_percentage) = maybe_red.is_percentage() else { return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); }; let (red, green, blue) = if is_percentage { let red = maybe_red.map_value(|v| clamp_unit_f32(v.to_number(1.0))); - let green = color_parser + let green = component_parser .parse_percentage(arguments, false)? .map_value(clamp_unit_f32); arguments.expect_comma()?; - let blue = color_parser + let blue = component_parser .parse_percentage(arguments, false)? .map_value(clamp_unit_f32); (red, green, blue) } else { let red = maybe_red.map_value(|v| clamp_floor_256_f32(v.to_number(255.0))); - let green = color_parser + let green = component_parser .parse_number(arguments, false)? .map_value(clamp_floor_256_f32); arguments.expect_comma()?; - let blue = color_parser + let blue = component_parser .parse_number(arguments, false)? .map_value(clamp_floor_256_f32); (red, green, blue) }; - let alpha = parse_legacy_alpha(color_parser, arguments)?; + let alpha = component_parser.parse_legacy_alpha(arguments)?; - (red, green, blue, alpha) + ColorFunction::Rgb(red, green, blue, alpha) } else { - let red = maybe_red.map_value(|v| clamp_floor_256_f32(v.to_number(255.0))); - let green = color_parser - .parse_number_or_percentage(arguments, true)? - .map_value(|v| clamp_floor_256_f32(v.to_number(255.0))); - let blue = color_parser - .parse_number_or_percentage(arguments, true)? - .map_value(|v| clamp_floor_256_f32(v.to_number(255.0))); - - let alpha = parse_modern_alpha(color_parser, arguments)?; + let green = component_parser.parse_number_or_percentage(arguments, true)?; + let blue = component_parser.parse_number_or_percentage(arguments, true)?; + + let alpha = component_parser.parse_modern_alpha(arguments)?; + + // When using the relative color syntax (having an origin color), the + // resulting color is always in the modern syntax. + if component_parser.origin_color.is_some() { + fn adjust(v: NumberOrPercentage) -> NumberOrPercentage { + if let NumberOrPercentage::Number { value } = v { + NumberOrPercentage::Number { + value: value / 255.0, + } + } else { + v + } + } + + ColorFunction::Color( + PredefinedColorSpace::Srgb, + maybe_red.map_value(adjust), + green.map_value(adjust), + blue.map_value(adjust), + alpha, + ) + } else { + fn clamp(v: NumberOrPercentage) -> u8 { + clamp_floor_256_f32(v.to_number(255.0)) + } - (red, green, blue, alpha) - }; + let red = maybe_red.map_value(clamp); + let green = green.map_value(clamp); + let blue = blue.map_value(clamp); - Ok(ColorFunction::Rgb(red, green, blue, alpha)) + ColorFunction::Rgb(red, green, blue, alpha) + } + }) } /// Parses hsl syntax. @@ -242,43 +282,48 @@ fn parse_rgb<'i, 't>( /// <https://drafts.csswg.org/css-color/#the-hsl-notation> #[inline] fn parse_hsl<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, ) -> Result<ColorFunction, ParseError<'i>> { - let origin_color = parse_origin_color(color_parser, arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser + .origin_color + .map(|c| c.to_color_space(ColorSpace::Hsl)), + }; - let hue = color_parser.parse_number_or_angle(arguments, true)?; + let hue = component_parser.parse_number_or_angle(arguments, true)?; // If the hue is not "none" and is followed by a comma, then we are parsing // the legacy syntax. Legacy syntax also doesn't support an origin color. - let is_legacy_syntax = origin_color.is_none() && + let is_legacy_syntax = component_parser.origin_color.is_none() && !hue.is_none() && arguments.try_parse(|p| p.expect_comma()).is_ok(); let (saturation, lightness, alpha) = if is_legacy_syntax { - let saturation = color_parser + let saturation = component_parser .parse_percentage(arguments, false)? .map_value(|unit_value| NumberOrPercentage::Percentage { unit_value }); arguments.expect_comma()?; - let lightness = color_parser + let lightness = component_parser .parse_percentage(arguments, false)? .map_value(|unit_value| NumberOrPercentage::Percentage { unit_value }); - ( - saturation, - lightness, - parse_legacy_alpha(color_parser, arguments)?, - ) + let alpha = component_parser.parse_legacy_alpha(arguments)?; + (saturation, lightness, alpha) } else { - let saturation = color_parser.parse_number_or_percentage(arguments, true)?; - let lightness = color_parser.parse_number_or_percentage(arguments, true)?; - ( - saturation, - lightness, - parse_modern_alpha(color_parser, arguments)?, - ) + let saturation = component_parser.parse_number_or_percentage(arguments, true)?; + let lightness = component_parser.parse_number_or_percentage(arguments, true)?; + let alpha = component_parser.parse_modern_alpha(arguments)?; + (saturation, lightness, alpha) }; - Ok(ColorFunction::Hsl(hue, saturation, lightness, alpha)) + Ok(ColorFunction::Hsl( + hue, + saturation, + lightness, + alpha, + component_parser.origin_color.is_none(), + )) } /// Parses hwb syntax. @@ -286,20 +331,29 @@ fn parse_hsl<'i, 't>( /// <https://drafts.csswg.org/css-color/#the-hbw-notation> #[inline] fn parse_hwb<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, ) -> Result<ColorFunction, ParseError<'i>> { - let _origin_color = parse_origin_color(color_parser, arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser + .origin_color + .map(|c| c.to_color_space(ColorSpace::Hwb)), + }; - let (hue, whiteness, blackness, alpha) = parse_components( - color_parser, - arguments, - ColorParser::parse_number_or_angle, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - )?; + let hue = component_parser.parse_number_or_angle(arguments, true)?; + let whiteness = component_parser.parse_number_or_percentage(arguments, true)?; + let blackness = component_parser.parse_number_or_percentage(arguments, true)?; - Ok(ColorFunction::Hwb(hue, whiteness, blackness, alpha)) + let alpha = component_parser.parse_modern_alpha(arguments)?; + + Ok(ColorFunction::Hwb( + hue, + whiteness, + blackness, + alpha, + component_parser.origin_color.is_none(), + )) } type IntoLabFn<Output> = fn( @@ -311,19 +365,23 @@ type IntoLabFn<Output> = fn( #[inline] fn parse_lab_like<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, + color_space: ColorSpace, into_color: IntoLabFn<ColorFunction>, ) -> Result<ColorFunction, ParseError<'i>> { - let _origin_color = parse_origin_color(color_parser, arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser + .origin_color + .map(|c| c.to_color_space(color_space)), + }; - let (lightness, a, b, alpha) = parse_components( - color_parser, - arguments, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - )?; + let lightness = component_parser.parse_number_or_percentage(arguments, true)?; + let a = component_parser.parse_number_or_percentage(arguments, true)?; + let b = component_parser.parse_number_or_percentage(arguments, true)?; + + let alpha = component_parser.parse_modern_alpha(arguments)?; Ok(into_color(lightness, a, b, alpha)) } @@ -337,19 +395,23 @@ type IntoLchFn<Output> = fn( #[inline] fn parse_lch_like<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, + color_space: ColorSpace, into_color: IntoLchFn<ColorFunction>, ) -> Result<ColorFunction, ParseError<'i>> { - let _origin_color = parse_origin_color(color_parser, arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser + .origin_color + .map(|c| c.to_color_space(color_space)), + }; - let (lightness, chroma, hue, alpha) = parse_components( - color_parser, - arguments, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_angle, - )?; + let lightness = component_parser.parse_number_or_percentage(arguments, true)?; + let chroma = component_parser.parse_number_or_percentage(arguments, true)?; + let hue = component_parser.parse_number_or_angle(arguments, true)?; + + let alpha = component_parser.parse_modern_alpha(arguments)?; Ok(into_color(lightness, chroma, hue, alpha)) } @@ -357,72 +419,24 @@ fn parse_lch_like<'i, 't>( /// Parse the color() function. #[inline] fn parse_color_with_color_space<'i, 't>( - color_parser: &ColorParser<'_, '_>, + component_parser: &ComponentParser<'_, '_>, arguments: &mut Parser<'i, 't>, ) -> Result<ColorFunction, ParseError<'i>> { - let _origin_color = parse_origin_color(color_parser, arguments)?; - - let color_space = { - let location = arguments.current_source_location(); - - let ident = arguments.expect_ident()?; - PredefinedColorSpace::from_str(ident) - .map_err(|_| location.new_unexpected_token_error(Token::Ident(ident.clone())))? + let color_space = PredefinedColorSpace::parse(arguments)?; + let component_parser = ComponentParser { + context: component_parser.context, + origin_color: component_parser + .origin_color + .map(|c| c.to_color_space(ColorSpace::from(color_space))), }; - let (c1, c2, c3, alpha) = parse_components( - color_parser, - arguments, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - ColorParser::parse_number_or_percentage, - )?; + let c1 = component_parser.parse_number_or_percentage(arguments, true)?; + let c2 = component_parser.parse_number_or_percentage(arguments, true)?; + let c3 = component_parser.parse_number_or_percentage(arguments, true)?; - Ok(ColorFunction::Color(color_space, c1, c2, c3, alpha)) -} + let alpha = component_parser.parse_modern_alpha(arguments)?; -type ComponentParseResult<'i, R1, R2, R3> = Result< - ( - ColorComponent<R1>, - ColorComponent<R2>, - ColorComponent<R3>, - ColorComponent<NumberOrPercentage>, - ), - ParseError<'i>, ->; - -/// Parse the color components and alpha with the modern [color-4] syntax. -pub fn parse_components<'a, 'b: 'a, 'i, 't, F1, F2, F3, R1, R2, R3>( - color_parser: &ColorParser<'a, 'b>, - input: &mut Parser<'i, 't>, - f1: F1, - f2: F2, - f3: F3, -) -> ComponentParseResult<'i, R1, R2, R3> -where - F1: FnOnce( - &ColorParser<'a, 'b>, - &mut Parser<'i, 't>, - bool, - ) -> Result<ColorComponent<R1>, ParseError<'i>>, - F2: FnOnce( - &ColorParser<'a, 'b>, - &mut Parser<'i, 't>, - bool, - ) -> Result<ColorComponent<R2>, ParseError<'i>>, - F3: FnOnce( - &ColorParser<'a, 'b>, - &mut Parser<'i, 't>, - bool, - ) -> Result<ColorComponent<R3>, ParseError<'i>>, -{ - let r1 = f1(color_parser, input, true)?; - let r2 = f2(color_parser, input, true)?; - let r3 = f3(color_parser, input, true)?; - - let alpha = parse_modern_alpha(color_parser, input)?; - - Ok((r1, r2, r3, alpha)) + Ok(ColorFunction::Color(color_space, c1, c2, c3, alpha)) } /// Either a number or a percentage. @@ -453,6 +467,10 @@ impl NumberOrPercentage { } impl ColorComponentType for NumberOrPercentage { + fn from_value(value: f32) -> Self { + Self::Number { value } + } + fn units() -> CalcUnits { CalcUnits::PERCENTAGE } @@ -503,6 +521,10 @@ impl NumberOrAngle { } impl ColorComponentType for NumberOrAngle { + fn from_value(value: f32) -> Self { + Self::Number { value } + } + fn units() -> CalcUnits { CalcUnits::ANGLE } @@ -538,6 +560,10 @@ impl ColorComponentType for NumberOrAngle { /// The raw f32 here is for <number>. impl ColorComponentType for f32 { + fn from_value(value: f32) -> Self { + value + } + fn units() -> CalcUnits { CalcUnits::empty() } @@ -560,19 +586,29 @@ impl ColorComponentType for f32 { } /// Used to parse the components of a color. -pub struct ColorParser<'a, 'b: 'a> { +pub struct ComponentParser<'a, 'b: 'a> { /// Parser context used for parsing the colors. pub context: &'a ParserContext<'b>, + /// The origin color that will be used to resolve relative components. + pub origin_color: Option<AbsoluteColor>, } -impl<'a, 'b: 'a> ColorParser<'a, 'b> { +impl<'a, 'b: 'a> ComponentParser<'a, 'b> { + /// Create a new [ColorParser] with the given context. + pub fn new(context: &'a ParserContext<'b>) -> Self { + Self { + context, + origin_color: None, + } + } + /// Parse an `<number>` or `<angle>` value. fn parse_number_or_angle<'i, 't>( &self, input: &mut Parser<'i, 't>, allow_none: bool, ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>> { - ColorComponent::parse(self.context, input, allow_none) + ColorComponent::parse(self.context, input, allow_none, self.origin_color.as_ref()) } /// Parse a `<percentage>` value. @@ -587,7 +623,12 @@ impl<'a, 'b: 'a> ColorParser<'a, 'b> { // doesn't have any more overhead than just parsing a percentage on its // own. Ok( - match ColorComponent::<NumberOrPercentage>::parse(self.context, input, allow_none)? { + match ColorComponent::<NumberOrPercentage>::parse( + self.context, + input, + allow_none, + self.origin_color.as_ref(), + )? { ColorComponent::None => ColorComponent::None, ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) => { ColorComponent::Value(unit_value) @@ -603,7 +644,7 @@ impl<'a, 'b: 'a> ColorParser<'a, 'b> { input: &mut Parser<'i, 't>, allow_none: bool, ) -> Result<ColorComponent<f32>, ParseError<'i>> { - ColorComponent::parse(self.context, input, allow_none) + ColorComponent::parse(self.context, input, allow_none, self.origin_color.as_ref()) } /// Parse a `<number>` or `<percentage>` value. @@ -612,77 +653,34 @@ impl<'a, 'b: 'a> ColorParser<'a, 'b> { input: &mut Parser<'i, 't>, allow_none: bool, ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { - ColorComponent::parse(self.context, input, allow_none) + ColorComponent::parse(self.context, input, allow_none, self.origin_color.as_ref()) } -} -/// This trait is used by the [`ColorParser`] to construct colors of any type. -pub trait FromParsedColor { - /// Construct a new color from the CSS `currentcolor` keyword. - fn from_current_color() -> Self; - - /// Construct a new color from red, green, blue and alpha components. - fn from_rgba( - red: ColorComponent<u8>, - green: ColorComponent<u8>, - blue: ColorComponent<u8>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from hue, saturation, lightness and alpha components. - fn from_hsl( - hue: ColorComponent<NumberOrAngle>, - saturation: ColorComponent<NumberOrPercentage>, - lightness: ColorComponent<NumberOrPercentage>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from hue, blackness, whiteness and alpha components. - fn from_hwb( - hue: ColorComponent<NumberOrAngle>, - whiteness: ColorComponent<NumberOrPercentage>, - blackness: ColorComponent<NumberOrPercentage>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from the `lab` notation. - fn from_lab( - lightness: ColorComponent<NumberOrPercentage>, - a: ColorComponent<NumberOrPercentage>, - b: ColorComponent<NumberOrPercentage>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from the `lch` notation. - fn from_lch( - lightness: ColorComponent<NumberOrPercentage>, - chroma: ColorComponent<NumberOrPercentage>, - hue: ColorComponent<NumberOrAngle>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from the `oklab` notation. - fn from_oklab( - lightness: ColorComponent<NumberOrPercentage>, - a: ColorComponent<NumberOrPercentage>, - b: ColorComponent<NumberOrPercentage>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color from the `oklch` notation. - fn from_oklch( - lightness: ColorComponent<NumberOrPercentage>, - chroma: ColorComponent<NumberOrPercentage>, - hue: ColorComponent<NumberOrAngle>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; - - /// Construct a new color with a predefined color space. - fn from_color_function( - color_space: PredefinedColorSpace, - c1: ColorComponent<NumberOrPercentage>, - c2: ColorComponent<NumberOrPercentage>, - c3: ColorComponent<NumberOrPercentage>, - alpha: ColorComponent<NumberOrPercentage>, - ) -> Self; + fn parse_legacy_alpha<'i, 't>( + &self, + arguments: &mut Parser<'i, 't>, + ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { + if !arguments.is_exhausted() { + arguments.expect_comma()?; + self.parse_number_or_percentage(arguments, false) + } else { + Ok(ColorComponent::Value(NumberOrPercentage::Number { + value: OPAQUE, + })) + } + } + + fn parse_modern_alpha<'i, 't>( + &self, + arguments: &mut Parser<'i, 't>, + ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { + if !arguments.is_exhausted() { + arguments.expect_delim('/')?; + self.parse_number_or_percentage(arguments, true) + } else { + Ok(ColorComponent::Value(NumberOrPercentage::Number { + value: self.origin_color.map(|c| c.alpha).unwrap_or(OPAQUE), + })) + } + } } |