diff --git a/components/style/color/mix.rs b/components/style/color/mix.rs index 8d114324956..3fef73b07c8 100644 --- a/components/style/color/mix.rs +++ b/components/style/color/mix.rs @@ -6,7 +6,6 @@ use super::{AbsoluteColor, ColorComponents, ColorSpace}; use crate::parser::{Parse, ParserContext}; -use crate::values::animated::color::AnimatedRGBA as RGBA; use cssparser::Parser; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, ToCss}; @@ -65,12 +64,34 @@ pub struct ColorInterpolationMethod { impl ColorInterpolationMethod { /// Returns the srgb interpolation method. - pub fn srgb() -> Self { + pub const fn srgb() -> Self { Self { space: ColorSpace::Srgb, hue: HueInterpolationMethod::Shorter, } } + + /// Return the oklab interpolation method used for default color + /// interpolcation. + pub const fn oklab() -> Self { + Self { + space: ColorSpace::Oklab, + hue: HueInterpolationMethod::Shorter, + } + } + + /// Decides the best method for interpolating between the given colors. + /// https://drafts.csswg.org/css-color-4/#interpolation-space + pub fn best_interpolation_between(left: &AbsoluteColor, right: &AbsoluteColor) -> Self { + // The preferred color space to use for interpolating colors is Oklab. + // However, if either of the colors are in legacy rgb(), hsl() or hwb(), + // then interpolation is done in sRGB. + if !left.is_legacy_color() || !right.is_legacy_color() { + Self::oklab() + } else { + Self::srgb() + } + } } impl Parse for ColorInterpolationMethod { @@ -118,7 +139,7 @@ impl ToCss for ColorInterpolationMethod { /// /// For now, colors modelled in other spaces need to be convertible to and from /// `RGBA` because we use sRGB for displaying colors. -trait ModelledColor: Clone + Copy + From + Into { +trait ModelledColor: Clone + Copy { /// Linearly interpolate between the left and right colors. /// /// The HueInterpolationMethod parameter is only for color spaces where the hue is @@ -134,7 +155,7 @@ trait ModelledColor: Clone + Copy + From + Into { /// Mix two colors into one. pub fn mix( - interpolation: &ColorInterpolationMethod, + interpolation: ColorInterpolationMethod, left_color: &AbsoluteColor, mut left_weight: f32, right_color: &AbsoluteColor, diff --git a/components/style/color/mod.rs b/components/style/color/mod.rs index 69d25020f79..3e49c1d2545 100644 --- a/components/style/color/mod.rs +++ b/components/style/color/mod.rs @@ -13,6 +13,7 @@ use style_traits::{CssWriter, ToCss}; /// The 3 components that make up a color. (Does not include the alpha component) #[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] +#[repr(C)] pub struct ColorComponents(pub f32, pub f32, pub f32); impl ColorComponents { @@ -46,6 +47,11 @@ impl ColorComponents { )] #[repr(u8)] pub enum ColorSpace { + /// A color specified in the sRGB color space with either the rgb/rgba(..) + /// functions or the newer color(srgb ..) function. If the color(..) + /// function is used, the AS_COLOR_FUNCTION flag will be set. Examples: + /// "color(srgb 0.691 0.139 0.259)", "rgb(176, 35, 66)" + Srgb = 0, /// A color specified in the Hsl notation in the sRGB color space, e.g. /// "hsl(289.18 93.136% 65.531%)" /// https://drafts.csswg.org/css-color-4/#the-hsl-notation @@ -70,9 +76,6 @@ pub enum ColorSpace { /// "oklch(40.101% 0.12332 21.555)". /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors Oklch, - /// A color specified with the color(..) function and the "srgb" color - /// space, e.g. "color(srgb 0.691 0.139 0.259)". - Srgb, /// A color specified with the color(..) function and the "srgb-linear" /// color space, e.g. "color(srgb-linear 0.435 0.017 0.055)". SrgbLinear, @@ -141,7 +144,7 @@ bitflags! { /// An absolutely specified color, using either rgb(), rgba(), lab(), lch(), /// oklab(), oklch() or color(). -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] #[repr(C)] pub struct AbsoluteColor { /// The 3 components that make up colors in any color space. @@ -179,22 +182,69 @@ macro_rules! color_components_as { } impl AbsoluteColor { - /// Create a new [AbsoluteColor] with the given [ColorSpace] and components. + /// Create a new [`AbsoluteColor`] with the given [`ColorSpace`] and + /// components. pub fn new(color_space: ColorSpace, components: ColorComponents, alpha: f32) -> Self { + let mut components = components; + + // Lightness must not be less than 0. + if matches!( + color_space, + ColorSpace::Lab | ColorSpace::Lch | ColorSpace::Oklab | ColorSpace::Oklch + ) { + components.0 = components.0.max(0.0); + } + + // Chroma must not be less than 0. + if matches!(color_space, ColorSpace::Lch | ColorSpace::Oklch) { + components.1 = components.1.max(0.0); + } + Self { components, - alpha, + alpha: alpha.clamp(0.0, 1.0), color_space, flags: SerializationFlags::empty(), } } + /// Create a new [`AbsoluteColor`] from rgba values in the sRGB color space. + pub fn srgb(red: f32, green: f32, blue: f32, alpha: f32) -> Self { + Self::new(ColorSpace::Srgb, ColorComponents(red, green, blue), alpha) + } + + /// Create a new transparent color. + pub fn transparent() -> Self { + Self::srgb(0.0, 0.0, 0.0, 0.0) + } + + /// Create a new opaque black color. + pub fn black() -> Self { + Self::srgb(0.0, 0.0, 0.0, 1.0) + } + + /// Create a new opaque white color. + pub fn white() -> Self { + Self::srgb(1.0, 1.0, 1.0, 1.0) + } + /// Return all the components of the color in an array. (Includes alpha) #[inline] pub fn raw_components(&self) -> &[f32; 4] { unsafe { color_components_as!(self, [f32; 4]) } } + /// Returns true if this color is in one of the legacy color formats. + #[inline] + pub fn is_legacy_color(&self) -> bool { + // rgb(), rgba(), hsl(), hsla(), hwb(), hwba() + match self.color_space { + ColorSpace::Srgb => !self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION), + ColorSpace::Hsl | ColorSpace::Hwb => true, + _ => false, + } + } + /// Return the alpha component. #[inline] pub fn alpha(&self) -> f32 { diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 397996f7041..f6a628a2722 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -4,10 +4,11 @@ //! Gecko's media-query device and expression representation. +use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CssEnvironment; use crate::font_metrics::FontMetrics; -use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; +use crate::gecko::values::{convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color}; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs; use crate::media_queries::MediaType; @@ -20,7 +21,6 @@ use crate::values::specified::font::FONT_MEDIUM_PX; use crate::values::specified::ViewportVariant; use crate::values::{CustomIdent, KeyframesName}; use app_units::{Au, AU_PER_PX}; -use cssparser::RGBA; use euclid::default::Size2D; use euclid::{Scale, SideOffsets2D}; use servo_arc::Arc; @@ -189,9 +189,11 @@ impl Device { /// Sets the body text color for the "inherit color from body" quirk. /// /// - pub fn set_body_text_color(&self, color: RGBA) { - self.body_text_color - .store(convert_rgba_to_nscolor(&color) as usize, Ordering::Relaxed) + pub fn set_body_text_color(&self, color: AbsoluteColor) { + self.body_text_color.store( + convert_absolute_color_to_nscolor(&color) as usize, + Ordering::Relaxed, + ) } /// Gets the base size given a generic font family and a language. @@ -268,8 +270,8 @@ impl Device { } /// Returns the body text color. - pub fn body_text_color(&self) -> RGBA { - convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32) + pub fn body_text_color(&self) -> AbsoluteColor { + convert_nscolor_to_absolute_color(self.body_text_color.load(Ordering::Relaxed) as u32) } /// Gets the document pointer. @@ -494,17 +496,17 @@ impl Device { /// /// This is only for forced-colors/high-contrast, so looking at light colors /// is ok. - pub fn default_background_color(&self) -> RGBA { + pub fn default_background_color(&self) -> AbsoluteColor { let normal = ColorScheme::normal(); - convert_nscolor_to_rgba(self.system_nscolor(SystemColor::Canvas, &normal)) + convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvas, &normal)) } /// Returns the default foreground color. /// /// See above for looking at light colors only. - pub fn default_color(&self) -> RGBA { + pub fn default_color(&self) -> AbsoluteColor { let normal = ColorScheme::normal(); - convert_nscolor_to_rgba(self.system_nscolor(SystemColor::Canvastext, &normal)) + convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvastext, &normal)) } /// Returns the current effective text zoom. diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs index 23e9b6e7502..2fc08859062 100644 --- a/components/style/gecko/values.rs +++ b/components/style/gecko/values.rs @@ -6,6 +6,7 @@ //! Different kind of helpers to interact with Gecko values. +use crate::color::{AbsoluteColor, ColorSpace}; use crate::counter_style::{Symbol, Symbols}; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs::CounterStylePtr; @@ -13,23 +14,28 @@ use crate::values::generics::CounterStyle; use crate::values::Either; use crate::Atom; use app_units::Au; -use cssparser::RGBA; use std::cmp::max; -/// Convert a given RGBA value to `nscolor`. -pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 { +/// Convert a color value to `nscolor`. +pub fn convert_absolute_color_to_nscolor(color: &AbsoluteColor) -> u32 { + let srgb = color.to_color_space(ColorSpace::Srgb); u32::from_le_bytes([ - rgba.red, - rgba.green, - rgba.blue, - (rgba.alpha * 255.0).round() as u8, + (srgb.components.0 * 255.0).round() as u8, + (srgb.components.1 * 255.0).round() as u8, + (srgb.components.2 * 255.0).round() as u8, + (srgb.alpha * 255.0).round() as u8, ]) } -/// Convert a given `nscolor` to a Servo RGBA value. -pub fn convert_nscolor_to_rgba(color: u32) -> RGBA { +/// Convert a given `nscolor` to a Servo AbsoluteColor value. +pub fn convert_nscolor_to_absolute_color(color: u32) -> AbsoluteColor { let [r, g, b, a] = color.to_le_bytes(); - RGBA::new(r, g, b, a as f32 / 255.0) + AbsoluteColor::srgb( + r as f32 / 255.0, + g as f32 / 255.0, + b as f32 / 255.0, + a as f32 / 255.0, + ) } /// Round `width` down to the nearest device pixel, but any non-zero value that diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 0b61ea4776c..441aadfafe4 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -5,6 +5,7 @@ //! The main cascading algorithm of the style system. use crate::applicable_declarations::CascadePriority; +use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CustomPropertiesBuilder; use crate::dom::TElement; @@ -411,9 +412,8 @@ fn tweak_when_ignoring_colors( declaration: &mut Cow, declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden, ) { - use crate::values::specified::Color; use crate::values::computed::ToComputedValue; - use cssparser::RGBA; + use crate::values::specified::Color; if !longhand_id.ignored_when_document_colors_disabled() { return; @@ -445,7 +445,7 @@ fn tweak_when_ignoring_colors( // We assume here currentColor is opaque. let color = color .to_computed_value(context) - .into_rgba(RGBA::new(0, 0, 0, 1.0)); + .resolve_into_absolute(&AbsoluteColor::black()); color.alpha } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 2bf274d32cc..73c9b092af3 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -16,8 +16,6 @@ #[allow(unused_imports)] use app_units::Au; #[allow(unused_imports)] - use cssparser::{Color as CSSParserColor, RGBA}; - #[allow(unused_imports)] use crate::values::specified::AllowQuirks; #[allow(unused_imports)] use crate::Zero; diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 0fc77e0cae9..48eaaecd52f 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -9,9 +9,9 @@ ${helpers.predefined_type( "color", "ColorPropertyValue", - "::cssparser::RGBA::new(0, 0, 0, 1.0)", + "crate::color::AbsoluteColor::black()", engines="gecko servo", - animation_value_type="AnimatedRGBA", + animation_value_type="AbsoluteColor", ignored_when_colors_disabled="True", spec="https://drafts.csswg.org/css-color/#color", )} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 840d4e5be6c..b1b12e24395 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -19,7 +19,7 @@ use std::{ops, ptr}; use std::fmt::{self, Write}; use std::mem; -use cssparser::{Parser, RGBA, TokenSerializationType}; +use cssparser::{Parser, TokenSerializationType}; use cssparser::ParserInput; #[cfg(feature = "servo")] use euclid::SideOffsets2D; use crate::context::QuirksMode; @@ -3233,8 +3233,9 @@ impl ComputedValues { /// let top_color = /// style.resolve_color(style.get_border().clone_border_top_color()); #[inline] - pub fn resolve_color(&self, color: computed::Color) -> RGBA { - color.into_rgba(self.get_inherited_text().clone_color()) + pub fn resolve_color(&self, color: computed::Color) -> crate::color::AbsoluteColor { + let current_color = self.get_inherited_text().clone_color(); + color.resolve_into_absolute(¤t_color) } /// Returns which longhand properties have different values in the two diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs index 4140b1fd378..21aeb1692fb 100644 --- a/components/style/servo/media_queries.rs +++ b/components/style/servo/media_queries.rs @@ -17,7 +17,6 @@ use crate::values::specified::font::FONT_MEDIUM_PX; use crate::values::specified::ViewportVariant; use crate::values::KeyframesName; use app_units::Au; -use cssparser::RGBA; use euclid::default::Size2D as UntypedSize2D; use euclid::{Scale, SideOffsets2D, Size2D}; use mime::Mime; diff --git a/components/style/stylesheets/font_palette_values_rule.rs b/components/style/stylesheets/font_palette_values_rule.rs index b6085b7884d..55a4ebd4e3b 100644 --- a/components/style/stylesheets/font_palette_values_rule.rs +++ b/components/style/stylesheets/font_palette_values_rule.rs @@ -184,9 +184,12 @@ impl FontPaletteValuesRule { } for c in &self.override_colors { if let SpecifiedColor::Absolute(ref absolute) = c.color { - let rgba = absolute.color.to_rgba(); unsafe { - Gecko_SetFontPaletteOverride(palette_values, c.index.0.value(), rgba); + Gecko_SetFontPaletteOverride( + palette_values, + c.index.0.value(), + (&absolute.color) as *const _ as *mut _, + ); } } } diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 535ed5b84d6..b6cef8a9228 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -5,82 +5,21 @@ //! Animated types for CSS colors. use crate::color::mix::ColorInterpolationMethod; -use crate::color::{AbsoluteColor, ColorComponents, ColorSpace}; +use crate::color::AbsoluteColor; use crate::values::animated::{Animate, Procedure, ToAnimatedZero}; use crate::values::computed::Percentage; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::color::{GenericColor, GenericColorMix}; -/// An animated RGBA color. -/// -/// Unlike in computed values, each component value may exceed the -/// range `[0.0, 1.0]`. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero, ToAnimatedValue)] -#[repr(C)] -pub struct AnimatedRGBA { - /// The red component. - pub red: f32, - /// The green component. - pub green: f32, - /// The blue component. - pub blue: f32, - /// The alpha component. - pub alpha: f32, -} - -impl From for AnimatedRGBA { - fn from(value: AbsoluteColor) -> Self { - let srgb = value.to_color_space(ColorSpace::Srgb); - - Self::new( - srgb.components.0, - srgb.components.1, - srgb.components.2, - srgb.alpha, - ) - } -} - -impl From for AbsoluteColor { - fn from(value: AnimatedRGBA) -> Self { - Self::new( - ColorSpace::Srgb, - ColorComponents(value.red, value.green, value.blue), - value.alpha, - ) - } -} - -use self::AnimatedRGBA as RGBA; - -impl RGBA { - /// Returns a transparent color. - #[inline] - pub fn transparent() -> Self { - Self::new(0., 0., 0., 0.) - } - - /// Returns a new color. - #[inline] - pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self { - RGBA { - red, - green, - blue, - alpha, - } - } -} - -impl Animate for RGBA { +impl Animate for AbsoluteColor { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result { let (left_weight, right_weight) = procedure.weights(); Ok(crate::color::mix::mix( - &ColorInterpolationMethod::srgb(), - &AbsoluteColor::from(self.clone()), + ColorInterpolationMethod::best_interpolation_between(self, other), + self, left_weight as f32, - &AbsoluteColor::from(other.clone()), + other, right_weight as f32, /* normalize_weights = */ false, ) @@ -88,20 +27,20 @@ impl Animate for RGBA { } } -impl ComputeSquaredDistance for RGBA { +impl ComputeSquaredDistance for AbsoluteColor { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { let start = [ self.alpha, - self.red * self.alpha, - self.green * self.alpha, - self.blue * self.alpha, + self.components.0 * self.alpha, + self.components.1 * self.alpha, + self.components.2 * self.alpha, ]; let end = [ other.alpha, - other.red * other.alpha, - other.green * other.alpha, - other.blue * other.alpha, + other.components.0 * other.alpha, + other.components.1 * other.alpha, + other.components.2 * other.alpha, ]; start .iter() @@ -112,19 +51,11 @@ impl ComputeSquaredDistance for RGBA { } /// An animated value for ``. -pub type Color = GenericColor; +pub type Color = GenericColor; /// An animated value for ``. pub type ColorMix = GenericColorMix; -impl Color { - fn to_rgba(&self, current_color: RGBA) -> RGBA { - let mut clone = self.clone(); - clone.simplify(Some(¤t_color)); - *clone.as_numeric().unwrap() - } -} - impl Animate for Color { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result { @@ -146,15 +77,16 @@ impl Animate for Color { impl ComputeSquaredDistance for Color { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { - let current_color = RGBA::transparent(); - self.to_rgba(current_color) - .compute_squared_distance(&other.to_rgba(current_color)) + let current_color = AbsoluteColor::transparent(); + self.clone() + .resolve_into_absolute(¤t_color) + .compute_squared_distance(&other.clone().resolve_into_absolute(¤t_color)) } } impl ToAnimatedZero for Color { #[inline] fn to_animated_zero(&self) -> Result { - Ok(Color::rgba(RGBA::transparent())) + Ok(Color::Absolute(AbsoluteColor::transparent())) } } diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index 4bf31f110f3..deb83fb1cbf 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -8,6 +8,7 @@ //! computed values and need yet another intermediate representation. This //! module's raison d'ĂȘtre is to ultimately contain all these types. +use crate::color::AbsoluteColor; use crate::properties::PropertyId; use crate::values::computed::length::LengthPercentage; use crate::values::computed::url::ComputedUrl; @@ -380,6 +381,7 @@ trivial_to_animated_value!(ComputedUrl); trivial_to_animated_value!(bool); trivial_to_animated_value!(f32); trivial_to_animated_value!(i32); +trivial_to_animated_value!(AbsoluteColor); // Note: This implementation is for ToAnimatedValue of ShapeSource. // // SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index 96e1aae8d32..b54c9c6beea 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -4,24 +4,24 @@ //! Computed color values. -use crate::values::animated::color::AnimatedRGBA; -use crate::values::animated::ToAnimatedValue; +use crate::color::AbsoluteColor; +use crate::values::animated::ToAnimatedZero; use crate::values::computed::percentage::Percentage; use crate::values::generics::color::{GenericCaretColor, GenericColor, GenericColorOrAuto}; -use cssparser::{Color as CSSParserColor, RGBA}; +use cssparser::Color as CSSParserColor; use std::fmt; use style_traits::{CssWriter, ToCss}; -pub use crate::values::specified::color::{ColorScheme, PrintColorAdjust, ForcedColorAdjust}; +pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust}; /// The computed value of the `color` property. -pub type ColorPropertyValue = RGBA; +pub type ColorPropertyValue = AbsoluteColor; /// The computed value of `-moz-font-smoothing-background-color`. -pub type MozFontSmoothingBackgroundColor = RGBA; +pub type MozFontSmoothingBackgroundColor = AbsoluteColor; /// A computed value for ``. -pub type Color = GenericColor; +pub type Color = GenericColor; impl ToCss for Color { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result @@ -29,7 +29,7 @@ impl ToCss for Color { W: fmt::Write, { match *self { - Self::Numeric(ref c) => c.to_css(dest), + Self::Absolute(ref c) => c.to_css(dest), Self::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest), Self::ColorMix(ref m) => m.to_css(dest), } @@ -39,44 +39,30 @@ impl ToCss for Color { impl Color { /// Returns a complex color value representing transparent. pub fn transparent() -> Color { - Color::rgba(RGBA::transparent()) + Color::Absolute(AbsoluteColor::transparent()) } /// Returns opaque black. pub fn black() -> Color { - Color::rgba(RGBA::new(0, 0, 0, 1.0)) + Color::Absolute(AbsoluteColor::black()) } /// Returns opaque white. pub fn white() -> Color { - Color::rgba(RGBA::new(255, 255, 255, 1.0)) + Color::Absolute(AbsoluteColor::white()) } - /// Combine this complex color with the given foreground color into - /// a numeric RGBA color. - pub fn into_rgba(mut self, current_color: RGBA) -> RGBA { + /// Combine this complex color with the given foreground color into an + /// absolute color. + pub fn resolve_into_absolute(mut self, current_color: &AbsoluteColor) -> AbsoluteColor { self.simplify(Some(¤t_color)); - *self.as_numeric().unwrap() + *self.as_absolute().unwrap() } } -impl ToAnimatedValue for RGBA { - type AnimatedValue = AnimatedRGBA; - - #[inline] - fn to_animated_value(self) -> Self::AnimatedValue { - AnimatedRGBA::new( - self.red_f32(), - self.green_f32(), - self.blue_f32(), - self.alpha_f32(), - ) - } - - #[inline] - fn from_animated_value(animated: Self::AnimatedValue) -> Self { - // RGBA::from_floats clamps each component values. - RGBA::from_floats(animated.red, animated.green, animated.blue, animated.alpha) +impl ToAnimatedZero for AbsoluteColor { + fn to_animated_zero(&self) -> Result { + Ok(Self::transparent()) } } diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs index 9f31dc1c9a6..784f45cddad 100644 --- a/components/style/values/generics/color.rs +++ b/components/style/values/generics/color.rs @@ -6,7 +6,6 @@ use crate::color::mix::ColorInterpolationMethod; use crate::color::AbsoluteColor; -use crate::values::animated::color::AnimatedRGBA; use crate::values::animated::ToAnimatedValue; use crate::values::specified::percentage::ToPercentage; use std::fmt::{self, Write}; @@ -16,9 +15,9 @@ use style_traits::{CssWriter, ToCss}; /// the current foreground color (currentcolor keyword). #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)] #[repr(C)] -pub enum GenericColor { +pub enum GenericColor { /// The actual numeric color. - Numeric(RGBA), + Absolute(AbsoluteColor), /// The `CurrentColor` keyword. CurrentColor, /// The color-mix() function. @@ -92,17 +91,18 @@ impl ToCss for ColorMix ColorMix, Percentage> { - fn to_rgba(&self) -> Option +impl ColorMix, Percentage> { + /// Mix the colors so that we get a single color. If any of the 2 colors are + /// not mixable (perhaps not absolute?), then return None. + fn mix_into_absolute(&self) -> Option where - RGBA: Clone + ToAnimatedValue, Percentage: ToPercentage, { - let left = AbsoluteColor::from(self.left.as_numeric()?.clone().to_animated_value()); - let right = AbsoluteColor::from(self.right.as_numeric()?.clone().to_animated_value()); + let left = self.left.as_absolute()?.to_animated_value(); + let right = self.right.as_absolute()?.to_animated_value(); let mixed = crate::color::mix::mix( - &self.interpolation, + self.interpolation, &left, self.left_percentage.to_percentage(), &right, @@ -116,36 +116,34 @@ impl ColorMix, Percentage> { pub use self::GenericColor as Color; -impl Color { - /// Returns the numeric rgba value if this color is numeric, or None - /// otherwise. - pub fn as_numeric(&self) -> Option<&RGBA> { +impl Color { + /// If this color is absolute return it's value, otherwise return None. + pub fn as_absolute(&self) -> Option<&AbsoluteColor> { match *self { - Self::Numeric(ref rgba) => Some(rgba), + Self::Absolute(ref absolute) => Some(absolute), _ => None, } } /// Simplifies the color-mix()es to the extent possible given a current /// color (or not). - pub fn simplify(&mut self, current_color: Option<&RGBA>) + pub fn simplify(&mut self, current_color: Option<&AbsoluteColor>) where - RGBA: Clone + ToAnimatedValue, Percentage: ToPercentage, { match *self { - Self::Numeric(..) => {}, + Self::Absolute(..) => {}, Self::CurrentColor => { if let Some(c) = current_color { - *self = Self::Numeric(c.clone()); + *self = Self::Absolute(c.clone()); } }, Self::ColorMix(ref mut mix) => { mix.left.simplify(current_color); mix.right.simplify(current_color); - if let Some(mix) = mix.to_rgba() { - *self = Self::Numeric(mix); + if let Some(mix) = mix.mix_into_absolute() { + *self = Self::Absolute(mix); } }, } @@ -156,19 +154,14 @@ impl Color { Self::CurrentColor } - /// Returns a numeric color representing the given RGBA value. - pub fn rgba(color: RGBA) -> Self { - Self::Numeric(color) - } - /// Whether it is a currentcolor value (no numeric color component). pub fn is_currentcolor(&self) -> bool { matches!(*self, Self::CurrentColor) } - /// Whether it is a numeric color (no currentcolor component). - pub fn is_numeric(&self) -> bool { - matches!(*self, Self::Numeric(..)) + /// Whether this color is an absolute color. + pub fn is_absolute(&self) -> bool { + matches!(*self, Self::Absolute(..)) } } diff --git a/components/style/values/resolved/color.rs b/components/style/values/resolved/color.rs index ea912945c5a..79dfd8685fe 100644 --- a/components/style/values/resolved/color.rs +++ b/components/style/values/resolved/color.rs @@ -6,12 +6,13 @@ use super::{Context, ToResolvedValue}; +use crate::color::AbsoluteColor; use crate::values::computed::color as computed; use crate::values::generics::color as generics; impl ToResolvedValue for computed::Color { // A resolved color value is an rgba color, with currentcolor resolved. - type ResolvedValue = cssparser::RGBA; + type ResolvedValue = AbsoluteColor; #[inline] fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue { @@ -20,14 +21,14 @@ impl ToResolvedValue for computed::Color { #[inline] fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { - generics::Color::rgba(resolved) + generics::Color::Absolute(resolved) } } impl ToResolvedValue for computed::CaretColor { // A resolved caret-color value is an rgba color, with auto resolving to // currentcolor. - type ResolvedValue = cssparser::RGBA; + type ResolvedValue = AbsoluteColor; #[inline] fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue { diff --git a/components/style/values/resolved/mod.rs b/components/style/values/resolved/mod.rs index 49fb222d459..7f15ee452f5 100644 --- a/components/style/values/resolved/mod.rs +++ b/components/style/values/resolved/mod.rs @@ -9,7 +9,6 @@ use crate::media_queries::Device; use crate::properties::ComputedValues; use crate::ArcSlice; -use cssparser; use servo_arc::Arc; use smallvec::SmallVec; @@ -87,7 +86,7 @@ trivial_to_resolved_value!(usize); trivial_to_resolved_value!(String); trivial_to_resolved_value!(Box); trivial_to_resolved_value!(crate::OwnedStr); -trivial_to_resolved_value!(cssparser::RGBA); +trivial_to_resolved_value!(crate::color::AbsoluteColor); trivial_to_resolved_value!(crate::Atom); trivial_to_resolved_value!(crate::values::AtomIdent); trivial_to_resolved_value!(app_units::Au); diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index cad78bdcb26..f914216afbe 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -106,32 +106,6 @@ impl ColorMix { } } -impl AbsoluteColor { - /// Convenience function to create a color in the sRGB color space. - pub fn from_rgba(rgba: RGBA) -> Self { - let red = rgba.red as f32 / 255.0; - let green = rgba.green as f32 / 255.0; - let blue = rgba.blue as f32 / 255.0; - - Self::new( - ColorSpace::Srgb, - ColorComponents(red, green, blue), - rgba.alpha, - ) - } - - /// Convert the color to sRGB color space and return it in the RGBA struct. - pub fn to_rgba(&self) -> RGBA { - let rgba = self.to_color_space(ColorSpace::Srgb); - - let red = (rgba.components.0 * 255.0).round() as u8; - let green = (rgba.components.1 * 255.0).round() as u8; - let blue = (rgba.components.2 * 255.0).round() as u8; - - RGBA::new(red, green, blue, rgba.alpha) - } -} - /// Container holding an absolute color and the text specified by an author. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub struct Absolute { @@ -172,6 +146,13 @@ pub enum Color { InheritFromBodyQuirk, } +impl From for Color { + #[inline] + fn from(value: AbsoluteColor) -> Self { + Self::from_absolute_color(value) + } +} + /// System colors. A bunch of these are ad-hoc, others come from Windows: /// /// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor @@ -413,7 +394,7 @@ pub enum SystemColor { impl SystemColor { #[inline] fn compute(&self, cx: &Context) -> ComputedColor { - use crate::gecko::values::convert_nscolor_to_rgba; + use crate::gecko::values::convert_nscolor_to_absolute_color; use crate::gecko_bindings::bindings; // TODO: We should avoid cloning here most likely, though it's @@ -423,13 +404,7 @@ impl SystemColor { if color == bindings::NS_SAME_AS_FOREGROUND_COLOR { return ComputedColor::currentcolor(); } - ComputedColor::rgba(convert_nscolor_to_rgba(color)) - } -} - -impl From for Color { - fn from(value: RGBA) -> Self { - Color::rgba(value) + ComputedColor::Absolute(convert_nscolor_to_absolute_color(color)) } } @@ -711,12 +686,12 @@ impl Color { pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool { match *self { #[cfg(feature = "gecko")] - Color::InheritFromBodyQuirk => false, - Color::CurrentColor => true, + Self::InheritFromBodyQuirk => false, + Self::CurrentColor => true, #[cfg(feature = "gecko")] - Color::System(..) => true, - Color::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0, - Color::ColorMix(ref mix) => { + Self::System(..) => true, + Self::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0, + Self::ColorMix(ref mix) => { mix.left.honored_in_forced_colors_mode(allow_transparent) && mix.right.honored_in_forced_colors_mode(allow_transparent) }, @@ -725,22 +700,21 @@ impl Color { /// Returns currentcolor value. #[inline] - pub fn currentcolor() -> Color { - Color::CurrentColor + pub fn currentcolor() -> Self { + Self::CurrentColor } /// Returns transparent value. #[inline] - pub fn transparent() -> Color { + pub fn transparent() -> Self { // We should probably set authored to "transparent", but maybe it doesn't matter. - Color::rgba(RGBA::transparent()) + Self::from_absolute_color(AbsoluteColor::transparent()) } - /// Returns an absolute RGBA color value. - #[inline] - pub fn rgba(rgba: RGBA) -> Self { + /// Create a color from an [`AbsoluteColor`]. + pub fn from_absolute_color(color: AbsoluteColor) -> Self { Color::Absolute(Box::new(Absolute { - color: AbsoluteColor::from_rgba(rgba), + color, authored: None, })) } @@ -758,7 +732,14 @@ impl Color { return Err(e); } Color::parse_quirky_color(input) - .map(Color::rgba) + .map(|rgba| { + Color::from_absolute_color(AbsoluteColor::srgb( + rgba.red as f32 / 255.0, + rgba.green as f32 / 255.0, + rgba.blue as f32 / 255.0, + rgba.alpha, // alpha value is already a float and in range [0..1] + )) + }) .map_err(|_| e) }) } @@ -840,7 +821,7 @@ impl Color { pub fn to_computed_color(&self, context: Option<&Context>) -> Option { Some(match *self { Color::CurrentColor => ComputedColor::CurrentColor, - Color::Absolute(ref absolute) => ComputedColor::Numeric(absolute.color.to_rgba()), + Color::Absolute(ref absolute) => ComputedColor::Absolute(absolute.color), Color::ColorMix(ref mix) => { use crate::values::computed::percentage::Percentage; @@ -860,7 +841,9 @@ impl Color { #[cfg(feature = "gecko")] Color::System(system) => system.compute(context?), #[cfg(feature = "gecko")] - Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()), + Color::InheritFromBodyQuirk => { + ComputedColor::Absolute(context?.device().body_text_color()) + }, }) } } @@ -874,7 +857,7 @@ impl ToComputedValue for Color { fn from_computed_value(computed: &ComputedColor) -> Self { match *computed { - ComputedColor::Numeric(ref color) => Color::rgba(*color), + ComputedColor::Absolute(ref color) => Self::from_absolute_color(color.clone()), ComputedColor::CurrentColor => Color::CurrentColor, ComputedColor::ColorMix(ref mix) => { Color::ColorMix(Box::new(ToComputedValue::from_computed_value(&**mix))) @@ -902,16 +885,16 @@ impl Parse for MozFontSmoothingBackgroundColor { } impl ToComputedValue for MozFontSmoothingBackgroundColor { - type ComputedValue = RGBA; + type ComputedValue = AbsoluteColor; - fn to_computed_value(&self, context: &Context) -> RGBA { + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { self.0 .to_computed_value(context) - .into_rgba(RGBA::transparent()) + .resolve_into_absolute(&AbsoluteColor::transparent()) } - fn from_computed_value(computed: &RGBA) -> Self { - MozFontSmoothingBackgroundColor(Color::rgba(*computed)) + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + MozFontSmoothingBackgroundColor(Color::from_absolute_color(*computed)) } } @@ -949,18 +932,19 @@ impl SpecifiedValueInfo for Color { pub struct ColorPropertyValue(pub Color); impl ToComputedValue for ColorPropertyValue { - type ComputedValue = RGBA; + type ComputedValue = AbsoluteColor; #[inline] - fn to_computed_value(&self, context: &Context) -> RGBA { + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + let current_color = context.builder.get_parent_inherited_text().clone_color(); self.0 .to_computed_value(context) - .into_rgba(context.builder.get_parent_inherited_text().clone_color()) + .resolve_into_absolute(¤t_color) } #[inline] - fn from_computed_value(computed: &RGBA) -> Self { - ColorPropertyValue(Color::rgba(*computed).into()) + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ColorPropertyValue(Color::from_absolute_color(*computed).into()) } } diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 3ce97a06057..4e5d3bc3483 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -501,7 +501,6 @@ impl_to_css_for_predefined_type!(i8); impl_to_css_for_predefined_type!(i32); impl_to_css_for_predefined_type!(u16); impl_to_css_for_predefined_type!(u32); -impl_to_css_for_predefined_type!(::cssparser::RGBA); impl_to_css_for_predefined_type!(::cssparser::Token<'a>); impl_to_css_for_predefined_type!(::cssparser::UnicodeRange); diff --git a/components/to_shmem/lib.rs b/components/to_shmem/lib.rs index ebe18534306..e01c144cae0 100644 --- a/components/to_shmem/lib.rs +++ b/components/to_shmem/lib.rs @@ -224,7 +224,6 @@ impl_trivial_to_shmem!( usize ); -impl_trivial_to_shmem!(cssparser::RGBA); impl_trivial_to_shmem!(cssparser::SourceLocation); impl_trivial_to_shmem!(cssparser::TokenSerializationType);