style: Add lab(), lch(), oklab(), oklch() to specified colors

Use new changes from cssparser and use the new lab/lch/oklab/oklch color
formats.

Introduced a new color type AbsoluteColor.  It represents any kind of
color that has absolute numerical values.  It is also tied to a color
space and therefore can be trivially converted to another color space.

Differential Revision: https://phabricator.services.mozilla.com/D163579
This commit is contained in:
Tiaan Louw 2023-01-20 10:55:51 +00:00 committed by Martin Robinson
parent 6ce64abe7e
commit 4559546fbb
12 changed files with 512 additions and 90 deletions

View file

@ -21,7 +21,7 @@ shmem = ["dep:to_shmem", "dep:to_shmem_derive"]
[dependencies]
bitflags = "1.0"
cssparser = "0.29"
cssparser = "0.30"
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign"] }
fxhash = "0.2"
log = "0.4"

View file

@ -41,7 +41,7 @@ arrayvec = "0.7"
atomic_refcell = "0.1"
bitflags = "1.0"
byteorder = "1.0"
cssparser = "0.29"
cssparser = "0.30"
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "from"] }
encoding_rs = { version = "0.8", optional = true }
euclid = "0.22"

View file

@ -18,20 +18,18 @@ use std::cmp::max;
/// Convert a given RGBA value to `nscolor`.
pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
((rgba.alpha as u32) << 24) |
((rgba.blue as u32) << 16) |
((rgba.green as u32) << 8) |
(rgba.red as u32)
u32::from_le_bytes([
rgba.red,
rgba.green,
rgba.blue,
(rgba.alpha * 255.0).round() as u8,
])
}
/// Convert a given `nscolor` to a Servo RGBA value.
pub fn convert_nscolor_to_rgba(color: u32) -> RGBA {
RGBA::new(
(color & 0xff) as u8,
(color >> 8 & 0xff) as u8,
(color >> 16 & 0xff) as u8,
(color >> 24 & 0xff) as u8,
)
let [r, g, b, a] = color.to_le_bytes();
RGBA::new(r, g, b, a as f32 / 255.0)
}
/// Round `width` down to the nearest device pixel, but any non-zero value that

View file

@ -433,9 +433,11 @@ fn tweak_when_ignoring_colors(
return;
}
fn alpha_channel(color: &Color, context: &computed::Context) -> u8 {
fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
// We assume here currentColor is opaque.
let color = color.to_computed_value(context).into_rgba(RGBA::new(0, 0, 0, 255));
let color = color
.to_computed_value(context)
.into_rgba(RGBA::new(0, 0, 0, 1.0));
color.alpha
}
@ -458,7 +460,7 @@ fn tweak_when_ignoring_colors(
// otherwise, this is needed to preserve semi-transparent
// backgrounds.
let alpha = alpha_channel(color, context);
if alpha == 0 {
if alpha == 0.0 {
return;
}
let mut color = context.builder.device.default_background_color();
@ -474,7 +476,7 @@ fn tweak_when_ignoring_colors(
// If the inherited color would be transparent, but we would
// override this with a non-transparent color, then override it with
// the default color. Otherwise just let it inherit through.
if context.builder.get_parent_inherited_text().clone_color().alpha == 0 {
if context.builder.get_parent_inherited_text().clone_color().alpha == 0.0 {
let color = context.builder.device.default_color();
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
specified::ColorPropertyValue(color.into()),

View file

@ -9,7 +9,7 @@
${helpers.predefined_type(
"color",
"ColorPropertyValue",
"::cssparser::RGBA::new(0, 0, 0, 255)",
"::cssparser::RGBA::new(0, 0, 0, 1.0)",
engines="gecko servo",
animation_value_type="AnimatedRGBA",
ignored_when_colors_disabled="True",

View file

@ -78,7 +78,6 @@ ${helpers.predefined_type(
engines="gecko",
spec="https://drafts.csswg.org/css-ui/#caret-color",
animation_value_type="CaretColor",
boxed=True,
ignored_when_colors_disabled=True,
)}
@ -90,7 +89,6 @@ ${helpers.predefined_type(
spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
gecko_pref="layout.css.accent-color.enabled",
animation_value_type="ColorOrAuto",
boxed=True,
ignored_when_colors_disabled=True,
has_effect_on_gecko_scrollbars=False,
)}

View file

@ -47,8 +47,8 @@ impl Parse for FontPaletteOverrideColor {
let location = input.current_source_location();
let color = SpecifiedColor::parse(context, input)?;
// Only absolute colors are accepted here.
if let SpecifiedColor::Numeric { parsed: _, authored: _ } = color {
Ok(FontPaletteOverrideColor{ index, color })
if let SpecifiedColor::Absolute { .. } = color {
Ok(FontPaletteOverrideColor { index, color })
} else {
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
@ -183,11 +183,10 @@ impl FontPaletteValuesRule {
}
}
for c in &self.override_colors {
if let SpecifiedColor::Numeric { parsed, authored: _ } = &c.color {
if let SpecifiedColor::Absolute(ref absolute) = c.color {
let rgba = absolute.color.to_rgba();
unsafe {
Gecko_SetFontPaletteOverride(palette_values,
c.index.0.value(),
*parsed);
Gecko_SetFontPaletteOverride(palette_values, c.index.0.value(), rgba);
}
}
}

View file

@ -168,6 +168,13 @@ impl Color {
rgba.alpha *= alpha_multiplier;
}
// FIXME: In rare cases we end up with 0.999995 in the alpha channel,
// so we reduce the precision to avoid serializing to
// rgba(?, ?, ?, 1). This is not ideal, so we should look into
// ways to avoid it. Maybe pre-multiply all color components and
// then divide after calculations?
rgba.alpha = (rgba.alpha * 1000.0).round() / 1000.0;
rgba
}
}
@ -424,29 +431,56 @@ struct XYZD50A {
impl_lerp!(XYZD50A, None);
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
struct LABA {
lightness: f32,
a: f32,
b: f32,
alpha: f32,
pub struct LABA {
pub lightness: f32,
pub a: f32,
pub b: f32,
pub alpha: f32,
}
impl_lerp!(LABA, None);
/// An animated LCHA colour.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
struct LCHA {
lightness: f32,
chroma: f32,
hue: f32,
alpha: f32,
pub struct LCHA {
pub lightness: f32,
pub chroma: f32,
pub hue: f32,
pub alpha: f32,
}
impl_lerp!(LCHA, Some(2));
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct OKLABA {
pub lightness: f32,
pub a: f32,
pub b: f32,
pub alpha: f32,
}
impl_lerp!(OKLABA, None);
/// An animated OKLCHA colour.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct OKLCHA {
pub lightness: f32,
pub chroma: f32,
pub hue: f32,
pub alpha: f32,
}
impl_lerp!(OKLCHA, Some(2));
/// An animated hwb() color.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
@ -604,6 +638,7 @@ impl From<XYZD65A> for XYZD50A {
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
0., 0., 0., 1.,
);
let d50 = BRADFORD.transform_vector3d(Vector3D::new(d65.x, d65.y, d65.z));
Self {
x: d50.x,
@ -747,6 +782,22 @@ impl From<LABA> for LCHA {
}
}
impl From<OKLABA> for OKLCHA {
/// Convert an OKLAB color to OKLCH as specified in [1].
///
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
fn from(oklaba: OKLABA) -> Self {
let hue = oklaba.b.atan2(oklaba.a) * DEG_PER_RAD;
let chroma = (oklaba.a * oklaba.a + oklaba.b * oklaba.b).sqrt();
OKLCHA {
lightness: oklaba.lightness,
chroma,
hue,
alpha: oklaba.alpha,
}
}
}
impl From<LCHA> for LABA {
/// Convert a LCH color to LAB as specified in [1].
///
@ -764,6 +815,23 @@ impl From<LCHA> for LABA {
}
}
impl From<OKLCHA> for OKLABA {
/// Convert a OKLCH color to OKLAB as specified in [1].
///
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
fn from(oklcha: OKLCHA) -> Self {
let hue_radians = oklcha.hue * RAD_PER_DEG;
let a = oklcha.chroma * hue_radians.cos();
let b = oklcha.chroma * hue_radians.sin();
OKLABA {
lightness: oklcha.lightness,
a,
b,
alpha: oklcha.alpha,
}
}
}
impl From<LABA> for XYZD50A {
/// Convert a CIELAB color to XYZ as specified in [1] and [2].
///
@ -804,6 +872,78 @@ impl From<LABA> for XYZD50A {
}
}
impl From<XYZD65A> for OKLABA {
fn from(xyza: XYZD65A) -> Self {
// https://drafts.csswg.org/css-color-4/#color-conversion-code
#[cfg_attr(rustfmt, rustfmt_skip)]
const XYZ_TO_LMS: Transform3D<f32> = Transform3D::new(
0.8190224432164319, 0.0329836671980271, 0.048177199566046255, 0.,
0.3619062562801221, 0.9292868468965546, 0.26423952494422764, 0.,
-0.12887378261216414, 0.03614466816999844, 0.6335478258136937, 0.,
0., 0., 0., 1.,
);
#[cfg_attr(rustfmt, rustfmt_skip)]
const LMS_TO_OKLAB: Transform3D<f32> = Transform3D::new(
0.2104542553, 1.9779984951, 0.0259040371, 0.,
0.7936177850, -2.4285922050, 0.7827717662, 0.,
-0.0040720468, 0.4505937099, -0.8086757660, 0.,
0., 0., 0., 1.,
);
let lms = XYZ_TO_LMS.transform_vector3d(Vector3D::new(xyza.x, xyza.y, xyza.z));
let lab = LMS_TO_OKLAB.transform_vector3d(Vector3D::new(
lms.x.cbrt(),
lms.y.cbrt(),
lms.z.cbrt(),
));
Self {
lightness: lab.x,
a: lab.y,
b: lab.z,
alpha: xyza.alpha,
}
}
}
impl From<OKLABA> for XYZD65A {
fn from(oklaba: OKLABA) -> Self {
// https://drafts.csswg.org/css-color-4/#color-conversion-code
// Given OKLab, convert to XYZ relative to D65
#[cfg_attr(rustfmt, rustfmt_skip)]
const LMS_TO_XYZ: Transform3D<f32> = Transform3D::new(
1.2268798733741557, -0.04057576262431372, -0.07637294974672142, 0.,
-0.5578149965554813, 1.1122868293970594, -0.4214933239627914, 0.,
0.28139105017721583, -0.07171106666151701, 1.5869240244272418, 0.,
0., 0., 0., 1.,
);
#[cfg_attr(rustfmt, rustfmt_skip)]
const OKLAB_TO_LMS: Transform3D<f32> = Transform3D::new(
0.99999999845051981432, 1.0000000088817607767, 1.0000000546724109177, 0.,
0.39633779217376785678, -0.1055613423236563494, -0.089484182094965759684, 0.,
0.21580375806075880339, -0.063854174771705903402, -1.2914855378640917399, 0.,
0., 0., 0., 1.,
);
let lms =
OKLAB_TO_LMS.transform_vector3d(Vector3D::new(oklaba.lightness, oklaba.a, oklaba.b));
let xyz = LMS_TO_XYZ.transform_vector3d(Vector3D::new(
lms.x.powf(3.0),
lms.y.powf(3.0),
lms.z.powf(3.0),
));
Self {
x: xyz.x,
y: xyz.y,
z: xyz.z,
alpha: oklaba.alpha,
}
}
}
impl From<XYZD50A> for RGBA {
fn from(d50: XYZD50A) -> Self {
Self::from(XYZD65A::from(d50))
@ -828,6 +968,18 @@ impl From<LABA> for RGBA {
}
}
impl From<OKLABA> for RGBA {
fn from(oklaba: OKLABA) -> Self {
Self::from(XYZD65A::from(oklaba))
}
}
impl From<RGBA> for OKLABA {
fn from(rgba: RGBA) -> Self {
Self::from(XYZD65A::from(rgba))
}
}
impl From<RGBA> for LCHA {
fn from(rgba: RGBA) -> Self {
Self::from(LABA::from(rgba))
@ -839,3 +991,15 @@ impl From<LCHA> for RGBA {
Self::from(LABA::from(lcha))
}
}
impl From<OKLCHA> for RGBA {
fn from(oklcha: OKLCHA) -> Self {
Self::from(OKLABA::from(oklcha))
}
}
impl From<RGBA> for OKLCHA {
fn from(rgba: RGBA) -> Self {
Self::from(OKLABA::from(rgba))
}
}

View file

@ -30,7 +30,7 @@ impl ToCss for Color {
{
match *self {
Self::Numeric(ref c) => c.to_css(dest),
Self::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
Self::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
Self::ColorMix(ref m) => m.to_css(dest),
}
}
@ -44,12 +44,12 @@ impl Color {
/// Returns opaque black.
pub fn black() -> Color {
Color::rgba(RGBA::new(0, 0, 0, 255))
Color::rgba(RGBA::new(0, 0, 0, 1.0))
}
/// Returns opaque white.
pub fn white() -> Color {
Color::rgba(RGBA::new(255, 255, 255, 255))
Color::rgba(RGBA::new(255, 255, 255, 1.0))
}
/// Combine this complex color with the given foreground color into

View file

@ -98,18 +98,291 @@ impl ColorMix {
}
}
/// A color space representation in the CSS specification.
///
/// https://w3c.github.io/csswg-drafts/css-color-4/#color-type
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
#[repr(u8)]
pub enum ColorSpace {
/// A color specified in the Lab color format, e.g.
/// "lab(29.2345% 39.3825 20.0664)".
/// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
Lab,
/// A color specified in the Lch color format, e.g.
/// "lch(29.2345% 44.2 27)".
/// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors
Lch,
/// A color specified in the Oklab color format, e.g.
/// "oklab(40.101% 0.1147 0.0453)".
/// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
Oklab,
/// A color specified in the Oklch color format, e.g.
/// "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,
/// A color specified with the color(..) function and the "display-p3"
/// color space, e.g. "color(display-p3 0.84 0.19 0.72)".
DisplayP3,
/// A color specified with the color(..) function and the "a98-rgb" color
/// space, e.g. "color(a98-rgb 0.44091 0.49971 0.37408)".
A98Rgb,
/// A color specified with the color(..) function and the "prophoto-rgb"
/// color space, e.g. "color(prophoto-rgb 0.36589 0.41717 0.31333)".
ProphotoRgb,
/// A color specified with the color(..) function and the "rec2020" color
/// space, e.g. "color(rec2020 0.42210 0.47580 0.35605)".
Rec2020,
/// A color specified with the color(..) function and the "xyz-d50" color
/// space, e.g. "color(xyz-d50 0.2005 0.14089 0.4472)".
XyzD50,
/// A color specified with the color(..) function and the "xyz-d65" or "xyz"
/// color space, e.g. "color(xyz-d65 0.21661 0.14602 0.59452)".
XyzD65,
}
bitflags! {
#[derive(Default, MallocSizeOf, ToShmem)]
#[repr(C)]
struct SerializationFlags : u8 {
const AS_COLOR_FUNCTION = 0x01;
}
}
/// The 3 components that make up a color. (Does not include the alpha component)
#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
struct ColorComponents(f32, f32, f32);
/// An absolutely specified color, using either rgb(), rgba(), lab(), lch(),
/// oklab(), oklch() or color().
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
#[repr(C)]
pub struct AbsoluteColor {
components: ColorComponents,
alpha: f32,
color_space: ColorSpace,
flags: SerializationFlags,
}
macro_rules! color_components_as {
($c:expr, $t:ty) => {{
// This macro is not an inline function, because we can't use the
// generic type ($t) in a constant expression as per:
// https://github.com/rust-lang/rust/issues/76560
const_assert_eq!(std::mem::size_of::<$t>(), std::mem::size_of::<[f32; 4]>());
const_assert_eq!(std::mem::align_of::<$t>(), std::mem::align_of::<[f32; 4]>());
const_assert!(std::mem::size_of::<AbsoluteColor>() >= std::mem::size_of::<$t>());
const_assert_eq!(
std::mem::align_of::<AbsoluteColor>(),
std::mem::align_of::<$t>()
);
std::mem::transmute::<&ColorComponents, &$t>(&$c.components)
}};
}
impl AbsoluteColor {
/// Create a new [AbsoluteColor] with the given [ColorSpace] and components.
pub fn new(color_space: ColorSpace, c1: f32, c2: f32, c3: f32, alpha: f32) -> Self {
Self {
components: ColorComponents(c1, c2, c3),
alpha,
color_space,
flags: SerializationFlags::empty(),
}
}
/// 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, red, green, blue, rgba.alpha)
}
/// Return the alpha component.
#[inline]
pub fn alpha(&self) -> f32 {
self.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)
}
/// Convert this color to the specified color space.
pub fn to_color_space(&self, color_space: ColorSpace) -> Self {
use crate::values::animated::color::{AnimatedRGBA, LABA, LCHA, OKLABA, OKLCHA};
use ColorSpace::*;
if self.color_space == color_space {
return self.clone();
}
match (self.color_space, color_space) {
(Lab, Srgb) => {
let laba = unsafe { color_components_as!(self, LABA) };
let rgba = AnimatedRGBA::from(*laba);
Self::new(Srgb, rgba.red, rgba.green, rgba.blue, rgba.alpha)
},
(Oklab, Srgb) => {
let oklaba: &OKLABA = unsafe { color_components_as!(self, OKLABA) };
let rgba = AnimatedRGBA::from(*oklaba);
Self::new(Srgb, rgba.red, rgba.green, rgba.blue, rgba.alpha)
},
(Lch, Srgb) => {
let lcha: &LCHA = unsafe { color_components_as!(self, LCHA) };
let rgba = AnimatedRGBA::from(*lcha);
Self::new(Srgb, rgba.red, rgba.green, rgba.blue, rgba.alpha)
},
(Oklch, Srgb) => {
let oklcha: &OKLCHA = unsafe { color_components_as!(self, OKLCHA) };
let rgba = AnimatedRGBA::from(*oklcha);
Self::new(Srgb, rgba.red, rgba.green, rgba.blue, rgba.alpha)
},
_ => {
// Conversion to other color spaces is not implemented yet.
log::warn!(
"Can not convert from {:?} to {:?}!",
self.color_space,
color_space
);
Self::from_rgba(RGBA::new(0, 0, 0, 1.0))
},
}
}
}
impl From<cssparser::AbsoluteColor> for AbsoluteColor {
fn from(f: cssparser::AbsoluteColor) -> Self {
match f {
cssparser::AbsoluteColor::Rgba(rgba) => Self::from_rgba(rgba),
cssparser::AbsoluteColor::Lab(lab) => {
Self::new(ColorSpace::Lab, lab.lightness, lab.a, lab.b, lab.alpha)
},
cssparser::AbsoluteColor::Lch(lch) => Self::new(
ColorSpace::Lch,
lch.lightness,
lch.chroma,
lch.hue,
lch.alpha,
),
cssparser::AbsoluteColor::Oklab(oklab) => Self::new(
ColorSpace::Oklab,
oklab.lightness,
oklab.a,
oklab.b,
oklab.alpha,
),
cssparser::AbsoluteColor::Oklch(oklch) => Self::new(
ColorSpace::Oklch,
oklch.lightness,
oklch.chroma,
oklch.hue,
oklch.alpha,
),
}
}
}
impl ToCss for AbsoluteColor {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match self.color_space {
ColorSpace::Srgb if !self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION) => {
cssparser::ToCss::to_css(
&cssparser::RGBA::from_floats(
self.components.0,
self.components.1,
self.components.2,
self.alpha(),
),
dest,
)
},
ColorSpace::Lab => cssparser::ToCss::to_css(
unsafe { color_components_as!(self, cssparser::Lab) },
dest,
),
ColorSpace::Lch => cssparser::ToCss::to_css(
unsafe { color_components_as!(self, cssparser::Lch) },
dest,
),
ColorSpace::Oklab => cssparser::ToCss::to_css(
unsafe { color_components_as!(self, cssparser::Oklab) },
dest,
),
ColorSpace::Oklch => cssparser::ToCss::to_css(
unsafe { color_components_as!(self, cssparser::Oklch) },
dest,
),
// Other color spaces are not implemented yet. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1128204
ColorSpace::Srgb |
ColorSpace::SrgbLinear |
ColorSpace::DisplayP3 |
ColorSpace::A98Rgb |
ColorSpace::ProphotoRgb |
ColorSpace::Rec2020 |
ColorSpace::XyzD50 |
ColorSpace::XyzD65 => todo!(),
}
}
}
/// Container holding an absolute color and the text specified by an author.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub struct Absolute {
/// The specified color.
pub color: AbsoluteColor,
/// Authored representation.
pub authored: Option<Box<str>>,
}
impl ToCss for Absolute {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if let Some(ref authored) = self.authored {
dest.write_str(authored)
} else {
self.color.to_css(dest)
}
}
}
/// Specified color value
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum Color {
/// The 'currentColor' keyword
CurrentColor,
/// A specific RGBA color
Numeric {
/// Parsed RGBA color
parsed: RGBA,
/// Authored representation
authored: Option<Box<str>>,
},
/// An absolute color.
/// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-function
Absolute(Box<Absolute>),
/// A system color.
#[cfg(feature = "gecko")]
System(SystemColor),
@ -477,16 +750,24 @@ impl Color {
let authored = input.expect_ident_cloned().ok();
input.reset(&start);
authored
}
},
};
let compontent_parser = ColorComponentParser(&*context);
match input.try_parse(|i| CSSParserColor::parse_with(&compontent_parser, i)) {
Ok(value) => Ok(match value {
CSSParserColor::CurrentColor => Color::CurrentColor,
CSSParserColor::RGBA(rgba) => Color::Numeric {
parsed: rgba,
authored: authored.map(|s| s.to_ascii_lowercase().into_boxed_str()),
CSSParserColor::Absolute(absolute) => {
let enabled = matches!(absolute, cssparser::AbsoluteColor::Rgba(_)) ||
static_prefs::pref!("layout.css.more_color_4.enabled");
if !enabled {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Color::Absolute(Box::new(Absolute {
color: absolute.into(),
authored: authored.map(|s| s.to_ascii_lowercase().into_boxed_str()),
}))
},
}),
Err(e) => {
@ -497,7 +778,8 @@ impl Color {
}
}
if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i, preserve_authored)) {
if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i, preserve_authored))
{
return Ok(Color::ColorMix(Box::new(mix)));
}
@ -515,7 +797,9 @@ impl Color {
/// Returns whether a given color is valid for authors.
pub fn is_valid(context: &ParserContext, input: &mut Parser) -> bool {
input.parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No)).is_ok()
input
.parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No))
.is_ok()
}
/// Tries to parse a color and compute it with a given device.
@ -526,9 +810,8 @@ impl Color {
) -> Option<ComputedColor> {
use crate::error_reporting::ContextualParseError;
let start = input.position();
let result = input.parse_entirely(|input| {
Self::parse_internal(context, input, PreserveAuthored::No)
});
let result = input
.parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No));
let specified = match result {
Ok(s) => s,
@ -545,14 +828,11 @@ impl Color {
// default and not available on OffscreenCanvas anyways...
if let ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) = e.kind {
let location = e.location.clone();
let error = ContextualParseError::UnsupportedValue(
input.slice_from(start),
e,
);
let error = ContextualParseError::UnsupportedValue(input.slice_from(start), e);
context.log_css_error(location, error);
}
return None;
}
},
};
match device {
@ -572,14 +852,8 @@ impl ToCss for Color {
W: Write,
{
match *self {
Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
Color::Numeric {
authored: Some(ref authored),
..
} => dest.write_str(authored),
Color::Numeric {
parsed: ref rgba, ..
} => rgba.to_css(dest),
Color::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
Color::Absolute(ref absolute) => absolute.to_css(dest),
Color::ColorMix(ref mix) => mix.to_css(dest),
#[cfg(feature = "gecko")]
Color::System(system) => system.to_css(dest),
@ -589,18 +863,6 @@ impl ToCss for Color {
}
}
/// A wrapper of cssparser::Color::parse_hash.
///
/// That function should never return CurrentColor, so it makes no sense to
/// handle a cssparser::Color here. This should really be done in cssparser
/// directly rather than here.
fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
CSSParserColor::parse_hash(value).map(|color| match color {
CSSParserColor::RGBA(rgba) => rgba,
CSSParserColor::CurrentColor => unreachable!("parse_hash should never return currentcolor"),
})
}
impl Color {
/// Returns whether this color is allowed in forced-colors mode.
pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool {
@ -610,7 +872,7 @@ impl Color {
Color::CurrentColor => true,
#[cfg(feature = "gecko")]
Color::System(..) => true,
Color::Numeric { ref parsed, .. } => allow_transparent && parsed.alpha == 0,
Color::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0,
Color::ColorMix(ref mix) => {
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
mix.right.honored_in_forced_colors_mode(allow_transparent)
@ -631,13 +893,13 @@ impl Color {
Color::rgba(RGBA::transparent())
}
/// Returns a numeric RGBA color value.
/// Returns an absolute RGBA color value.
#[inline]
pub fn rgba(rgba: RGBA) -> Self {
Color::Numeric {
parsed: rgba,
Color::Absolute(Box::new(Absolute {
color: AbsoluteColor::from_rgba(rgba),
authored: None,
}
}))
}
/// Parse a color, with quirks.
@ -677,7 +939,7 @@ impl Color {
if ident.len() != 3 && ident.len() != 6 {
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
return parse_hash_color(ident.as_bytes()).map_err(|()| {
return RGBA::parse_hash(ident.as_bytes()).map_err(|()| {
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
});
},
@ -722,7 +984,7 @@ impl Color {
.unwrap();
}
debug_assert_eq!(written, 6);
parse_hash_color(&serialization)
RGBA::parse_hash(&serialization)
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
@ -735,7 +997,7 @@ impl Color {
pub fn to_computed_color(&self, context: Option<&Context>) -> Option<ComputedColor> {
Some(match *self {
Color::CurrentColor => ComputedColor::CurrentColor,
Color::Numeric { ref parsed, .. } => ComputedColor::Numeric(*parsed),
Color::Absolute(ref absolute) => ComputedColor::Numeric(absolute.color.to_rgba()),
Color::ColorMix(ref mix) => {
use crate::values::computed::percentage::Percentage;

View file

@ -17,7 +17,7 @@ gecko = []
[dependencies]
app_units = "0.7"
bitflags = "1.0"
cssparser = "0.29"
cssparser = "0.30"
euclid = "0.22"
lazy_static = "1"
malloc_size_of = { path = "../malloc_size_of" }

View file

@ -501,9 +501,8 @@ 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::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::RGBA);
impl_to_css_for_predefined_type!(::cssparser::Color);
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
/// Define an enum type with unit variants that each correspond to a CSS keyword.