mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
style: Move AbsoluteColor to common color module
Differential Revision: https://phabricator.services.mozilla.com/D169607
This commit is contained in:
parent
169c2682d5
commit
a9998e899b
4 changed files with 299 additions and 287 deletions
|
@ -7,6 +7,9 @@
|
||||||
/// cbindgen:ignore
|
/// cbindgen:ignore
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
use style_traits::{CssWriter, ToCss};
|
||||||
|
|
||||||
/// The 3 components that make up a color. (Does not include the alpha component)
|
/// The 3 components that make up a color. (Does not include the alpha component)
|
||||||
#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
pub struct ColorComponents(pub f32, pub f32, pub f32);
|
pub struct ColorComponents(pub f32, pub f32, pub f32);
|
||||||
|
@ -17,3 +20,283 @@ impl ColorComponents {
|
||||||
Self(f(self.0), f(self.1), f(self.2))
|
Self(f(self.0), f(self.1), f(self.2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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! {
|
||||||
|
/// Flags used when serializing colors.
|
||||||
|
#[derive(Default, MallocSizeOf, ToShmem)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SerializationFlags : u8 {
|
||||||
|
/// If set, serializes sRGB colors into `color(srgb ...)` instead of
|
||||||
|
/// `rgba(...)`.
|
||||||
|
const AS_COLOR_FUNCTION = 0x01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
/// The 3 components that make up colors in any color space.
|
||||||
|
pub components: ColorComponents,
|
||||||
|
/// The alpha component of the color.
|
||||||
|
pub alpha: f32,
|
||||||
|
/// The current color space that the components represent.
|
||||||
|
pub color_space: ColorSpace,
|
||||||
|
/// Extra flags used durring serialization of this color.
|
||||||
|
pub 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, components: ColorComponents, alpha: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
components,
|
||||||
|
alpha,
|
||||||
|
color_space,
|
||||||
|
flags: SerializationFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the alpha component.
|
||||||
|
#[inline]
|
||||||
|
pub fn alpha(&self) -> f32 {
|
||||||
|
self.alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this color to the specified color space.
|
||||||
|
pub fn to_color_space(&self, color_space: ColorSpace) -> Self {
|
||||||
|
use ColorSpace::*;
|
||||||
|
|
||||||
|
if self.color_space == color_space {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (xyz, white_point) = match self.color_space {
|
||||||
|
Lab => convert::to_xyz::<convert::Lab>(&self.components),
|
||||||
|
Lch => convert::to_xyz::<convert::Lch>(&self.components),
|
||||||
|
Oklab => convert::to_xyz::<convert::Oklab>(&self.components),
|
||||||
|
Oklch => convert::to_xyz::<convert::Oklch>(&self.components),
|
||||||
|
Srgb => convert::to_xyz::<convert::Srgb>(&self.components),
|
||||||
|
SrgbLinear => convert::to_xyz::<convert::SrgbLinear>(&self.components),
|
||||||
|
DisplayP3 => convert::to_xyz::<convert::DisplayP3>(&self.components),
|
||||||
|
A98Rgb => convert::to_xyz::<convert::A98Rgb>(&self.components),
|
||||||
|
ProphotoRgb => convert::to_xyz::<convert::ProphotoRgb>(&self.components),
|
||||||
|
Rec2020 => convert::to_xyz::<convert::Rec2020>(&self.components),
|
||||||
|
XyzD50 => convert::to_xyz::<convert::XyzD50>(&self.components),
|
||||||
|
XyzD65 => convert::to_xyz::<convert::XyzD65>(&self.components),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = match color_space {
|
||||||
|
Lab => convert::from_xyz::<convert::Lab>(&xyz, white_point),
|
||||||
|
Lch => convert::from_xyz::<convert::Lch>(&xyz, white_point),
|
||||||
|
Oklab => convert::from_xyz::<convert::Oklab>(&xyz, white_point),
|
||||||
|
Oklch => convert::from_xyz::<convert::Oklch>(&xyz, white_point),
|
||||||
|
Srgb => convert::from_xyz::<convert::Srgb>(&xyz, white_point),
|
||||||
|
SrgbLinear => convert::from_xyz::<convert::SrgbLinear>(&xyz, white_point),
|
||||||
|
DisplayP3 => convert::from_xyz::<convert::DisplayP3>(&xyz, white_point),
|
||||||
|
A98Rgb => convert::from_xyz::<convert::A98Rgb>(&xyz, white_point),
|
||||||
|
ProphotoRgb => convert::from_xyz::<convert::ProphotoRgb>(&xyz, white_point),
|
||||||
|
Rec2020 => convert::from_xyz::<convert::Rec2020>(&xyz, white_point),
|
||||||
|
XyzD50 => convert::from_xyz::<convert::XyzD50>(&xyz, white_point),
|
||||||
|
XyzD65 => convert::from_xyz::<convert::XyzD65>(&xyz, white_point),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::new(color_space, result, self.alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<cssparser::PredefinedColorSpace> for ColorSpace {
|
||||||
|
fn from(value: cssparser::PredefinedColorSpace) -> Self {
|
||||||
|
match value {
|
||||||
|
cssparser::PredefinedColorSpace::Srgb => ColorSpace::Srgb,
|
||||||
|
cssparser::PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear,
|
||||||
|
cssparser::PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3,
|
||||||
|
cssparser::PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb,
|
||||||
|
cssparser::PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb,
|
||||||
|
cssparser::PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020,
|
||||||
|
cssparser::PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50,
|
||||||
|
cssparser::PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
ColorComponents(lab.lightness, lab.a, lab.b),
|
||||||
|
lab.alpha,
|
||||||
|
),
|
||||||
|
|
||||||
|
cssparser::AbsoluteColor::Lch(lch) => Self::new(
|
||||||
|
ColorSpace::Lch,
|
||||||
|
ColorComponents(lch.lightness, lch.chroma, lch.hue),
|
||||||
|
lch.alpha,
|
||||||
|
),
|
||||||
|
|
||||||
|
cssparser::AbsoluteColor::Oklab(oklab) => Self::new(
|
||||||
|
ColorSpace::Oklab,
|
||||||
|
ColorComponents(oklab.lightness, oklab.a, oklab.b),
|
||||||
|
oklab.alpha,
|
||||||
|
),
|
||||||
|
|
||||||
|
cssparser::AbsoluteColor::Oklch(oklch) => Self::new(
|
||||||
|
ColorSpace::Oklch,
|
||||||
|
ColorComponents(oklch.lightness, oklch.chroma, oklch.hue),
|
||||||
|
oklch.alpha,
|
||||||
|
),
|
||||||
|
|
||||||
|
cssparser::AbsoluteColor::ColorFunction(c) => {
|
||||||
|
let mut result = AbsoluteColor::new(
|
||||||
|
c.color_space.into(),
|
||||||
|
ColorComponents(c.c1, c.c2, c.c3),
|
||||||
|
c.alpha,
|
||||||
|
);
|
||||||
|
|
||||||
|
if matches!(c.color_space, cssparser::PredefinedColorSpace::Srgb) {
|
||||||
|
result.flags |= SerializationFlags::AS_COLOR_FUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
let color_space = match self.color_space {
|
||||||
|
ColorSpace::Lab | ColorSpace::Lch | ColorSpace::Oklab | ColorSpace::Oklch => {
|
||||||
|
unreachable!("Handle these in the wrapping match case!!")
|
||||||
|
},
|
||||||
|
ColorSpace::Srgb => {
|
||||||
|
debug_assert!(
|
||||||
|
self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION),
|
||||||
|
"The case without this flag should be handled in the wrapping match case!!"
|
||||||
|
);
|
||||||
|
|
||||||
|
cssparser::PredefinedColorSpace::Srgb
|
||||||
|
},
|
||||||
|
ColorSpace::SrgbLinear => cssparser::PredefinedColorSpace::SrgbLinear,
|
||||||
|
ColorSpace::DisplayP3 => cssparser::PredefinedColorSpace::DisplayP3,
|
||||||
|
ColorSpace::A98Rgb => cssparser::PredefinedColorSpace::A98Rgb,
|
||||||
|
ColorSpace::ProphotoRgb => cssparser::PredefinedColorSpace::ProphotoRgb,
|
||||||
|
ColorSpace::Rec2020 => cssparser::PredefinedColorSpace::Rec2020,
|
||||||
|
ColorSpace::XyzD50 => cssparser::PredefinedColorSpace::XyzD50,
|
||||||
|
ColorSpace::XyzD65 => cssparser::PredefinedColorSpace::XyzD65,
|
||||||
|
};
|
||||||
|
|
||||||
|
let color_function = cssparser::ColorFunction {
|
||||||
|
color_space,
|
||||||
|
c1: self.components.0,
|
||||||
|
c2: self.components.1,
|
||||||
|
c3: self.components.2,
|
||||||
|
alpha: self.alpha,
|
||||||
|
};
|
||||||
|
let color = cssparser::AbsoluteColor::ColorFunction(color_function);
|
||||||
|
cssparser::ToCss::to_css(&color, dest)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
|
||||||
use crate::values::computed::Percentage;
|
use crate::values::computed::Percentage;
|
||||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
use crate::values::generics::color::{
|
use crate::values::generics::color::{
|
||||||
ColorInterpolationMethod, ColorSpace, GenericColor, GenericColorMix, HueInterpolationMethod,
|
ColorInterpolationMethod, GenericColor, GenericColorMix, HueInterpolationMethod,
|
||||||
|
InterpolationColorSpace,
|
||||||
};
|
};
|
||||||
use euclid::default::{Transform3D, Vector3D};
|
use euclid::default::{Transform3D, Vector3D};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
@ -133,14 +134,14 @@ impl Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mix_function = match interpolation.space {
|
let mix_function = match interpolation.space {
|
||||||
ColorSpace::Srgb => Self::mix_in::<RGBA>,
|
InterpolationColorSpace::Srgb => Self::mix_in::<RGBA>,
|
||||||
ColorSpace::LinearSrgb => Self::mix_in::<LinearRGBA>,
|
InterpolationColorSpace::LinearSrgb => Self::mix_in::<LinearRGBA>,
|
||||||
ColorSpace::Xyz => Self::mix_in::<XYZD65A>,
|
InterpolationColorSpace::Xyz => Self::mix_in::<XYZD65A>,
|
||||||
ColorSpace::XyzD50 => Self::mix_in::<XYZD50A>,
|
InterpolationColorSpace::XyzD50 => Self::mix_in::<XYZD50A>,
|
||||||
ColorSpace::Lab => Self::mix_in::<LABA>,
|
InterpolationColorSpace::Lab => Self::mix_in::<LABA>,
|
||||||
ColorSpace::Hwb => Self::mix_in::<HWBA>,
|
InterpolationColorSpace::Hwb => Self::mix_in::<HWBA>,
|
||||||
ColorSpace::Hsl => Self::mix_in::<HSLA>,
|
InterpolationColorSpace::Hsl => Self::mix_in::<HSLA>,
|
||||||
ColorSpace::Lch => Self::mix_in::<LCHA>,
|
InterpolationColorSpace::Lch => Self::mix_in::<LCHA>,
|
||||||
};
|
};
|
||||||
mix_function(
|
mix_function(
|
||||||
left_color,
|
left_color,
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub enum GenericColor<RGBA, Percentage> {
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ColorSpace {
|
pub enum InterpolationColorSpace {
|
||||||
/// The sRGB color space.
|
/// The sRGB color space.
|
||||||
Srgb,
|
Srgb,
|
||||||
/// The linear-sRGB color space.
|
/// The linear-sRGB color space.
|
||||||
|
@ -63,7 +63,7 @@ pub enum ColorSpace {
|
||||||
// TODO: Oklab, Lch
|
// TODO: Oklab, Lch
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorSpace {
|
impl InterpolationColorSpace {
|
||||||
/// Returns whether this is a `<polar-color-space>`.
|
/// Returns whether this is a `<polar-color-space>`.
|
||||||
pub fn is_polar(self) -> bool {
|
pub fn is_polar(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -120,7 +120,7 @@ pub enum HueInterpolationMethod {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ColorInterpolationMethod {
|
pub struct ColorInterpolationMethod {
|
||||||
/// The color-space the interpolation should be done in.
|
/// The color-space the interpolation should be done in.
|
||||||
pub space: ColorSpace,
|
pub space: InterpolationColorSpace,
|
||||||
/// The hue interpolation method.
|
/// The hue interpolation method.
|
||||||
pub hue: HueInterpolationMethod,
|
pub hue: HueInterpolationMethod,
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ impl ColorInterpolationMethod {
|
||||||
/// Returns the srgb interpolation method.
|
/// Returns the srgb interpolation method.
|
||||||
pub fn srgb() -> Self {
|
pub fn srgb() -> Self {
|
||||||
Self {
|
Self {
|
||||||
space: ColorSpace::Srgb,
|
space: InterpolationColorSpace::Srgb,
|
||||||
hue: HueInterpolationMethod::Shorter,
|
hue: HueInterpolationMethod::Shorter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ impl Parse for ColorInterpolationMethod {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_ident_matching("in")?;
|
input.expect_ident_matching("in")?;
|
||||||
let space = ColorSpace::parse(input)?;
|
let space = InterpolationColorSpace::parse(input)?;
|
||||||
// https://drafts.csswg.org/css-color-4/#hue-interpolation
|
// https://drafts.csswg.org/css-color-4/#hue-interpolation
|
||||||
// Unless otherwise specified, if no specific hue interpolation
|
// Unless otherwise specified, if no specific hue interpolation
|
||||||
// algorithm is selected by the host syntax, the default is shorter.
|
// algorithm is selected by the host syntax, the default is shorter.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! Specified color values.
|
//! Specified color values.
|
||||||
|
|
||||||
use super::AllowQuirks;
|
use super::AllowQuirks;
|
||||||
use crate::color::ColorComponents;
|
use crate::color::{AbsoluteColor, ColorComponents, ColorSpace};
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
||||||
|
@ -107,101 +107,7 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
impl AbsoluteColor {
|
||||||
/// Create a new [AbsoluteColor] with the given [ColorSpace] and components.
|
|
||||||
pub fn new(color_space: ColorSpace, components: ColorComponents, alpha: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
components,
|
|
||||||
alpha,
|
|
||||||
color_space,
|
|
||||||
flags: SerializationFlags::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience function to create a color in the sRGB color space.
|
/// Convenience function to create a color in the sRGB color space.
|
||||||
pub fn from_rgba(rgba: RGBA) -> Self {
|
pub fn from_rgba(rgba: RGBA) -> Self {
|
||||||
let red = rgba.red as f32 / 255.0;
|
let red = rgba.red as f32 / 255.0;
|
||||||
|
@ -215,12 +121,6 @@ impl AbsoluteColor {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// Convert the color to sRGB color space and return it in the RGBA struct.
|
||||||
pub fn to_rgba(&self) -> RGBA {
|
pub fn to_rgba(&self) -> RGBA {
|
||||||
let rgba = self.to_color_space(ColorSpace::Srgb);
|
let rgba = self.to_color_space(ColorSpace::Srgb);
|
||||||
|
@ -231,178 +131,6 @@ impl AbsoluteColor {
|
||||||
|
|
||||||
RGBA::new(red, green, blue, rgba.alpha)
|
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::color::convert;
|
|
||||||
use ColorSpace::*;
|
|
||||||
|
|
||||||
if self.color_space == color_space {
|
|
||||||
return self.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let (xyz, white_point) = match self.color_space {
|
|
||||||
Lab => convert::to_xyz::<convert::Lab>(&self.components),
|
|
||||||
Lch => convert::to_xyz::<convert::Lch>(&self.components),
|
|
||||||
Oklab => convert::to_xyz::<convert::Oklab>(&self.components),
|
|
||||||
Oklch => convert::to_xyz::<convert::Oklch>(&self.components),
|
|
||||||
Srgb => convert::to_xyz::<convert::Srgb>(&self.components),
|
|
||||||
SrgbLinear => convert::to_xyz::<convert::SrgbLinear>(&self.components),
|
|
||||||
DisplayP3 => convert::to_xyz::<convert::DisplayP3>(&self.components),
|
|
||||||
A98Rgb => convert::to_xyz::<convert::A98Rgb>(&self.components),
|
|
||||||
ProphotoRgb => convert::to_xyz::<convert::ProphotoRgb>(&self.components),
|
|
||||||
Rec2020 => convert::to_xyz::<convert::Rec2020>(&self.components),
|
|
||||||
XyzD50 => convert::to_xyz::<convert::XyzD50>(&self.components),
|
|
||||||
XyzD65 => convert::to_xyz::<convert::XyzD65>(&self.components),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = match color_space {
|
|
||||||
Lab => convert::from_xyz::<convert::Lab>(&xyz, white_point),
|
|
||||||
Lch => convert::from_xyz::<convert::Lch>(&xyz, white_point),
|
|
||||||
Oklab => convert::from_xyz::<convert::Oklab>(&xyz, white_point),
|
|
||||||
Oklch => convert::from_xyz::<convert::Oklch>(&xyz, white_point),
|
|
||||||
Srgb => convert::from_xyz::<convert::Srgb>(&xyz, white_point),
|
|
||||||
SrgbLinear => convert::from_xyz::<convert::SrgbLinear>(&xyz, white_point),
|
|
||||||
DisplayP3 => convert::from_xyz::<convert::DisplayP3>(&xyz, white_point),
|
|
||||||
A98Rgb => convert::from_xyz::<convert::A98Rgb>(&xyz, white_point),
|
|
||||||
ProphotoRgb => convert::from_xyz::<convert::ProphotoRgb>(&xyz, white_point),
|
|
||||||
Rec2020 => convert::from_xyz::<convert::Rec2020>(&xyz, white_point),
|
|
||||||
XyzD50 => convert::from_xyz::<convert::XyzD50>(&xyz, white_point),
|
|
||||||
XyzD65 => convert::from_xyz::<convert::XyzD65>(&xyz, white_point),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::new(color_space, result, self.alpha)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<cssparser::PredefinedColorSpace> for ColorSpace {
|
|
||||||
fn from(value: cssparser::PredefinedColorSpace) -> Self {
|
|
||||||
match value {
|
|
||||||
cssparser::PredefinedColorSpace::Srgb => ColorSpace::Srgb,
|
|
||||||
cssparser::PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear,
|
|
||||||
cssparser::PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3,
|
|
||||||
cssparser::PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb,
|
|
||||||
cssparser::PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb,
|
|
||||||
cssparser::PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020,
|
|
||||||
cssparser::PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50,
|
|
||||||
cssparser::PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
ColorComponents(lab.lightness, lab.a, lab.b),
|
|
||||||
lab.alpha,
|
|
||||||
),
|
|
||||||
|
|
||||||
cssparser::AbsoluteColor::Lch(lch) => Self::new(
|
|
||||||
ColorSpace::Lch,
|
|
||||||
ColorComponents(lch.lightness, lch.chroma, lch.hue),
|
|
||||||
lch.alpha,
|
|
||||||
),
|
|
||||||
|
|
||||||
cssparser::AbsoluteColor::Oklab(oklab) => Self::new(
|
|
||||||
ColorSpace::Oklab,
|
|
||||||
ColorComponents(oklab.lightness, oklab.a, oklab.b),
|
|
||||||
oklab.alpha,
|
|
||||||
),
|
|
||||||
|
|
||||||
cssparser::AbsoluteColor::Oklch(oklch) => Self::new(
|
|
||||||
ColorSpace::Oklch,
|
|
||||||
ColorComponents(oklch.lightness, oklch.chroma, oklch.hue),
|
|
||||||
oklch.alpha,
|
|
||||||
),
|
|
||||||
|
|
||||||
cssparser::AbsoluteColor::ColorFunction(c) => {
|
|
||||||
let mut result = AbsoluteColor::new(
|
|
||||||
c.color_space.into(),
|
|
||||||
ColorComponents(c.c1, c.c2, c.c3),
|
|
||||||
c.alpha,
|
|
||||||
);
|
|
||||||
|
|
||||||
if matches!(c.color_space, cssparser::PredefinedColorSpace::Srgb) {
|
|
||||||
result.flags |= SerializationFlags::AS_COLOR_FUNCTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
_ => {
|
|
||||||
let color_space = match self.color_space {
|
|
||||||
ColorSpace::Lab | ColorSpace::Lch | ColorSpace::Oklab | ColorSpace::Oklch => {
|
|
||||||
unreachable!("Handle these in the wrapping match case!!")
|
|
||||||
},
|
|
||||||
ColorSpace::Srgb => {
|
|
||||||
debug_assert!(
|
|
||||||
self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION),
|
|
||||||
"The case without this flag should be handled in the wrapping match case!!"
|
|
||||||
);
|
|
||||||
|
|
||||||
cssparser::PredefinedColorSpace::Srgb
|
|
||||||
},
|
|
||||||
ColorSpace::SrgbLinear => cssparser::PredefinedColorSpace::SrgbLinear,
|
|
||||||
ColorSpace::DisplayP3 => cssparser::PredefinedColorSpace::DisplayP3,
|
|
||||||
ColorSpace::A98Rgb => cssparser::PredefinedColorSpace::A98Rgb,
|
|
||||||
ColorSpace::ProphotoRgb => cssparser::PredefinedColorSpace::ProphotoRgb,
|
|
||||||
ColorSpace::Rec2020 => cssparser::PredefinedColorSpace::Rec2020,
|
|
||||||
ColorSpace::XyzD50 => cssparser::PredefinedColorSpace::XyzD50,
|
|
||||||
ColorSpace::XyzD65 => cssparser::PredefinedColorSpace::XyzD65,
|
|
||||||
};
|
|
||||||
|
|
||||||
let color_function = cssparser::ColorFunction {
|
|
||||||
color_space,
|
|
||||||
c1: self.components.0,
|
|
||||||
c2: self.components.1,
|
|
||||||
c3: self.components.2,
|
|
||||||
alpha: self.alpha,
|
|
||||||
};
|
|
||||||
let color = cssparser::AbsoluteColor::ColorFunction(color_function);
|
|
||||||
cssparser::ToCss::to_css(&color, dest)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container holding an absolute color and the text specified by an author.
|
/// Container holding an absolute color and the text specified by an author.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue