mirror of
https://github.com/servo/servo.git
synced 2025-08-08 06:55:31 +01:00
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:
parent
6ce64abe7e
commit
4559546fbb
12 changed files with 512 additions and 90 deletions
|
@ -21,7 +21,7 @@ shmem = ["dep:to_shmem", "dep:to_shmem_derive"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
cssparser = "0.29"
|
cssparser = "0.30"
|
||||||
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign"] }
|
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign"] }
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -41,7 +41,7 @@ arrayvec = "0.7"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
byteorder = "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"] }
|
derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "from"] }
|
||||||
encoding_rs = { version = "0.8", optional = true }
|
encoding_rs = { version = "0.8", optional = true }
|
||||||
euclid = "0.22"
|
euclid = "0.22"
|
||||||
|
|
|
@ -18,20 +18,18 @@ use std::cmp::max;
|
||||||
|
|
||||||
/// Convert a given RGBA value to `nscolor`.
|
/// Convert a given RGBA value to `nscolor`.
|
||||||
pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
|
pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
|
||||||
((rgba.alpha as u32) << 24) |
|
u32::from_le_bytes([
|
||||||
((rgba.blue as u32) << 16) |
|
rgba.red,
|
||||||
((rgba.green as u32) << 8) |
|
rgba.green,
|
||||||
(rgba.red as u32)
|
rgba.blue,
|
||||||
|
(rgba.alpha * 255.0).round() as u8,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a given `nscolor` to a Servo RGBA value.
|
/// Convert a given `nscolor` to a Servo RGBA value.
|
||||||
pub fn convert_nscolor_to_rgba(color: u32) -> RGBA {
|
pub fn convert_nscolor_to_rgba(color: u32) -> RGBA {
|
||||||
RGBA::new(
|
let [r, g, b, a] = color.to_le_bytes();
|
||||||
(color & 0xff) as u8,
|
RGBA::new(r, g, b, a as f32 / 255.0)
|
||||||
(color >> 8 & 0xff) as u8,
|
|
||||||
(color >> 16 & 0xff) as u8,
|
|
||||||
(color >> 24 & 0xff) as u8,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Round `width` down to the nearest device pixel, but any non-zero value that
|
/// Round `width` down to the nearest device pixel, but any non-zero value that
|
||||||
|
|
|
@ -433,9 +433,11 @@ fn tweak_when_ignoring_colors(
|
||||||
return;
|
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.
|
// 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
|
color.alpha
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +460,7 @@ fn tweak_when_ignoring_colors(
|
||||||
// otherwise, this is needed to preserve semi-transparent
|
// otherwise, this is needed to preserve semi-transparent
|
||||||
// backgrounds.
|
// backgrounds.
|
||||||
let alpha = alpha_channel(color, context);
|
let alpha = alpha_channel(color, context);
|
||||||
if alpha == 0 {
|
if alpha == 0.0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut color = context.builder.device.default_background_color();
|
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
|
// If the inherited color would be transparent, but we would
|
||||||
// override this with a non-transparent color, then override it with
|
// override this with a non-transparent color, then override it with
|
||||||
// the default color. Otherwise just let it inherit through.
|
// 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();
|
let color = context.builder.device.default_color();
|
||||||
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
|
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
|
||||||
specified::ColorPropertyValue(color.into()),
|
specified::ColorPropertyValue(color.into()),
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"color",
|
"color",
|
||||||
"ColorPropertyValue",
|
"ColorPropertyValue",
|
||||||
"::cssparser::RGBA::new(0, 0, 0, 255)",
|
"::cssparser::RGBA::new(0, 0, 0, 1.0)",
|
||||||
engines="gecko servo",
|
engines="gecko servo",
|
||||||
animation_value_type="AnimatedRGBA",
|
animation_value_type="AnimatedRGBA",
|
||||||
ignored_when_colors_disabled="True",
|
ignored_when_colors_disabled="True",
|
||||||
|
|
|
@ -78,7 +78,6 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
spec="https://drafts.csswg.org/css-ui/#caret-color",
|
spec="https://drafts.csswg.org/css-ui/#caret-color",
|
||||||
animation_value_type="CaretColor",
|
animation_value_type="CaretColor",
|
||||||
boxed=True,
|
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -90,7 +89,6 @@ ${helpers.predefined_type(
|
||||||
spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
|
spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
|
||||||
gecko_pref="layout.css.accent-color.enabled",
|
gecko_pref="layout.css.accent-color.enabled",
|
||||||
animation_value_type="ColorOrAuto",
|
animation_value_type="ColorOrAuto",
|
||||||
boxed=True,
|
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
has_effect_on_gecko_scrollbars=False,
|
has_effect_on_gecko_scrollbars=False,
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -47,8 +47,8 @@ impl Parse for FontPaletteOverrideColor {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
let color = SpecifiedColor::parse(context, input)?;
|
let color = SpecifiedColor::parse(context, input)?;
|
||||||
// Only absolute colors are accepted here.
|
// Only absolute colors are accepted here.
|
||||||
if let SpecifiedColor::Numeric { parsed: _, authored: _ } = color {
|
if let SpecifiedColor::Absolute { .. } = color {
|
||||||
Ok(FontPaletteOverrideColor{ index, color })
|
Ok(FontPaletteOverrideColor { index, color })
|
||||||
} else {
|
} else {
|
||||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -183,11 +183,10 @@ impl FontPaletteValuesRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for c in &self.override_colors {
|
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 {
|
unsafe {
|
||||||
Gecko_SetFontPaletteOverride(palette_values,
|
Gecko_SetFontPaletteOverride(palette_values, c.index.0.value(), rgba);
|
||||||
c.index.0.value(),
|
|
||||||
*parsed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,13 @@ impl Color {
|
||||||
rgba.alpha *= alpha_multiplier;
|
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
|
rgba
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,29 +431,56 @@ struct XYZD50A {
|
||||||
|
|
||||||
impl_lerp!(XYZD50A, None);
|
impl_lerp!(XYZD50A, None);
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct LABA {
|
pub struct LABA {
|
||||||
lightness: f32,
|
pub lightness: f32,
|
||||||
a: f32,
|
pub a: f32,
|
||||||
b: f32,
|
pub b: f32,
|
||||||
alpha: f32,
|
pub alpha: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lerp!(LABA, None);
|
impl_lerp!(LABA, None);
|
||||||
|
|
||||||
/// An animated LCHA colour.
|
/// An animated LCHA colour.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct LCHA {
|
pub struct LCHA {
|
||||||
lightness: f32,
|
pub lightness: f32,
|
||||||
chroma: f32,
|
pub chroma: f32,
|
||||||
hue: f32,
|
pub hue: f32,
|
||||||
alpha: f32,
|
pub alpha: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lerp!(LCHA, Some(2));
|
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.
|
/// An animated hwb() color.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -604,6 +638,7 @@ impl From<XYZD65A> for XYZD50A {
|
||||||
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
|
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
|
||||||
0., 0., 0., 1.,
|
0., 0., 0., 1.,
|
||||||
);
|
);
|
||||||
|
|
||||||
let d50 = BRADFORD.transform_vector3d(Vector3D::new(d65.x, d65.y, d65.z));
|
let d50 = BRADFORD.transform_vector3d(Vector3D::new(d65.x, d65.y, d65.z));
|
||||||
Self {
|
Self {
|
||||||
x: d50.x,
|
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 {
|
impl From<LCHA> for LABA {
|
||||||
/// Convert a LCH color to LAB as specified in [1].
|
/// 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 {
|
impl From<LABA> for XYZD50A {
|
||||||
/// Convert a CIELAB color to XYZ as specified in [1] and [2].
|
/// 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 {
|
impl From<XYZD50A> for RGBA {
|
||||||
fn from(d50: XYZD50A) -> Self {
|
fn from(d50: XYZD50A) -> Self {
|
||||||
Self::from(XYZD65A::from(d50))
|
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 {
|
impl From<RGBA> for LCHA {
|
||||||
fn from(rgba: RGBA) -> Self {
|
fn from(rgba: RGBA) -> Self {
|
||||||
Self::from(LABA::from(rgba))
|
Self::from(LABA::from(rgba))
|
||||||
|
@ -839,3 +991,15 @@ impl From<LCHA> for RGBA {
|
||||||
Self::from(LABA::from(lcha))
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl ToCss for Color {
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
Self::Numeric(ref c) => c.to_css(dest),
|
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),
|
Self::ColorMix(ref m) => m.to_css(dest),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,12 +44,12 @@ impl Color {
|
||||||
|
|
||||||
/// Returns opaque black.
|
/// Returns opaque black.
|
||||||
pub fn black() -> Color {
|
pub fn black() -> Color {
|
||||||
Color::rgba(RGBA::new(0, 0, 0, 255))
|
Color::rgba(RGBA::new(0, 0, 0, 1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns opaque white.
|
/// Returns opaque white.
|
||||||
pub fn white() -> Color {
|
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
|
/// Combine this complex color with the given foreground color into
|
||||||
|
|
|
@ -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
|
/// Specified color value
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
/// The 'currentColor' keyword
|
/// The 'currentColor' keyword
|
||||||
CurrentColor,
|
CurrentColor,
|
||||||
/// A specific RGBA color
|
/// An absolute color.
|
||||||
Numeric {
|
/// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-function
|
||||||
/// Parsed RGBA color
|
Absolute(Box<Absolute>),
|
||||||
parsed: RGBA,
|
|
||||||
/// Authored representation
|
|
||||||
authored: Option<Box<str>>,
|
|
||||||
},
|
|
||||||
/// A system color.
|
/// A system color.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
System(SystemColor),
|
System(SystemColor),
|
||||||
|
@ -477,16 +750,24 @@ impl Color {
|
||||||
let authored = input.expect_ident_cloned().ok();
|
let authored = input.expect_ident_cloned().ok();
|
||||||
input.reset(&start);
|
input.reset(&start);
|
||||||
authored
|
authored
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let compontent_parser = ColorComponentParser(&*context);
|
let compontent_parser = ColorComponentParser(&*context);
|
||||||
match input.try_parse(|i| CSSParserColor::parse_with(&compontent_parser, i)) {
|
match input.try_parse(|i| CSSParserColor::parse_with(&compontent_parser, i)) {
|
||||||
Ok(value) => Ok(match value {
|
Ok(value) => Ok(match value {
|
||||||
CSSParserColor::CurrentColor => Color::CurrentColor,
|
CSSParserColor::CurrentColor => Color::CurrentColor,
|
||||||
CSSParserColor::RGBA(rgba) => Color::Numeric {
|
CSSParserColor::Absolute(absolute) => {
|
||||||
parsed: rgba,
|
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()),
|
authored: authored.map(|s| s.to_ascii_lowercase().into_boxed_str()),
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Err(e) => {
|
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)));
|
return Ok(Color::ColorMix(Box::new(mix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +797,9 @@ impl Color {
|
||||||
|
|
||||||
/// Returns whether a given color is valid for authors.
|
/// Returns whether a given color is valid for authors.
|
||||||
pub fn is_valid(context: &ParserContext, input: &mut Parser) -> bool {
|
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.
|
/// Tries to parse a color and compute it with a given device.
|
||||||
|
@ -526,9 +810,8 @@ impl Color {
|
||||||
) -> Option<ComputedColor> {
|
) -> Option<ComputedColor> {
|
||||||
use crate::error_reporting::ContextualParseError;
|
use crate::error_reporting::ContextualParseError;
|
||||||
let start = input.position();
|
let start = input.position();
|
||||||
let result = input.parse_entirely(|input| {
|
let result = input
|
||||||
Self::parse_internal(context, input, PreserveAuthored::No)
|
.parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No));
|
||||||
});
|
|
||||||
|
|
||||||
let specified = match result {
|
let specified = match result {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
@ -545,14 +828,11 @@ impl Color {
|
||||||
// default and not available on OffscreenCanvas anyways...
|
// default and not available on OffscreenCanvas anyways...
|
||||||
if let ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) = e.kind {
|
if let ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) = e.kind {
|
||||||
let location = e.location.clone();
|
let location = e.location.clone();
|
||||||
let error = ContextualParseError::UnsupportedValue(
|
let error = ContextualParseError::UnsupportedValue(input.slice_from(start), e);
|
||||||
input.slice_from(start),
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
context.log_css_error(location, error);
|
context.log_css_error(location, error);
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match device {
|
match device {
|
||||||
|
@ -572,14 +852,8 @@ impl ToCss for Color {
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
|
Color::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
|
||||||
Color::Numeric {
|
Color::Absolute(ref absolute) => absolute.to_css(dest),
|
||||||
authored: Some(ref authored),
|
|
||||||
..
|
|
||||||
} => dest.write_str(authored),
|
|
||||||
Color::Numeric {
|
|
||||||
parsed: ref rgba, ..
|
|
||||||
} => rgba.to_css(dest),
|
|
||||||
Color::ColorMix(ref mix) => mix.to_css(dest),
|
Color::ColorMix(ref mix) => mix.to_css(dest),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(system) => system.to_css(dest),
|
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 {
|
impl Color {
|
||||||
/// Returns whether this color is allowed in forced-colors mode.
|
/// Returns whether this color is allowed in forced-colors mode.
|
||||||
pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool {
|
pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool {
|
||||||
|
@ -610,7 +872,7 @@ impl Color {
|
||||||
Color::CurrentColor => true,
|
Color::CurrentColor => true,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(..) => true,
|
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) => {
|
Color::ColorMix(ref mix) => {
|
||||||
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
|
mix.left.honored_in_forced_colors_mode(allow_transparent) &&
|
||||||
mix.right.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())
|
Color::rgba(RGBA::transparent())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a numeric RGBA color value.
|
/// Returns an absolute RGBA color value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rgba(rgba: RGBA) -> Self {
|
pub fn rgba(rgba: RGBA) -> Self {
|
||||||
Color::Numeric {
|
Color::Absolute(Box::new(Absolute {
|
||||||
parsed: rgba,
|
color: AbsoluteColor::from_rgba(rgba),
|
||||||
authored: None,
|
authored: None,
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a color, with quirks.
|
/// Parse a color, with quirks.
|
||||||
|
@ -677,7 +939,7 @@ impl Color {
|
||||||
if ident.len() != 3 && ident.len() != 6 {
|
if ident.len() != 3 && ident.len() != 6 {
|
||||||
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
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)
|
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -722,7 +984,7 @@ impl Color {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
debug_assert_eq!(written, 6);
|
debug_assert_eq!(written, 6);
|
||||||
parse_hash_color(&serialization)
|
RGBA::parse_hash(&serialization)
|
||||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.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> {
|
pub fn to_computed_color(&self, context: Option<&Context>) -> Option<ComputedColor> {
|
||||||
Some(match *self {
|
Some(match *self {
|
||||||
Color::CurrentColor => ComputedColor::CurrentColor,
|
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) => {
|
Color::ColorMix(ref mix) => {
|
||||||
use crate::values::computed::percentage::Percentage;
|
use crate::values::computed::percentage::Percentage;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ gecko = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
cssparser = "0.29"
|
cssparser = "0.30"
|
||||||
euclid = "0.22"
|
euclid = "0.22"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
malloc_size_of = { path = "../malloc_size_of" }
|
malloc_size_of = { path = "../malloc_size_of" }
|
||||||
|
|
|
@ -501,9 +501,8 @@ impl_to_css_for_predefined_type!(i8);
|
||||||
impl_to_css_for_predefined_type!(i32);
|
impl_to_css_for_predefined_type!(i32);
|
||||||
impl_to_css_for_predefined_type!(u16);
|
impl_to_css_for_predefined_type!(u16);
|
||||||
impl_to_css_for_predefined_type!(u32);
|
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::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);
|
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
|
||||||
|
|
||||||
/// Define an enum type with unit variants that each correspond to a CSS keyword.
|
/// Define an enum type with unit variants that each correspond to a CSS keyword.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue