mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Implement more color-mix() color-spaces
We had code to convert between these and the latest draft supports them so... Differential Revision: https://phabricator.services.mozilla.com/D147004
This commit is contained in:
parent
95e9898db4
commit
6fc4355dc2
2 changed files with 305 additions and 168 deletions
|
@ -9,6 +9,7 @@ use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|||
use crate::values::generics::color::{Color as GenericColor, ComplexColorRatios};
|
||||
use crate::values::specified::color::{ColorInterpolationMethod, ColorSpace, HueInterpolationMethod};
|
||||
use euclid::default::{Transform3D, Vector3D};
|
||||
use std::f32::consts::{PI, TAU};
|
||||
|
||||
/// An animated RGBA color.
|
||||
///
|
||||
|
@ -27,6 +28,8 @@ pub struct RGBA {
|
|||
pub alpha: f32,
|
||||
}
|
||||
|
||||
const RAD_PER_DEG: f32 = PI / 180.0;
|
||||
|
||||
impl RGBA {
|
||||
/// Returns a transparent color.
|
||||
#[inline]
|
||||
|
@ -135,36 +138,17 @@ impl Color {
|
|||
right_color: &Color,
|
||||
right_weight: f32,
|
||||
) -> Self {
|
||||
match interpolation.space {
|
||||
ColorSpace::Srgb => Self::mix_in::<RGBA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
interpolation.hue,
|
||||
),
|
||||
ColorSpace::Xyz => Self::mix_in::<XYZA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
interpolation.hue,
|
||||
),
|
||||
ColorSpace::Lab => Self::mix_in::<LABA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
interpolation.hue,
|
||||
),
|
||||
ColorSpace::Lch => Self::mix_in::<LCHA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
interpolation.hue,
|
||||
),
|
||||
}
|
||||
let mix_function = match interpolation.space {
|
||||
ColorSpace::Srgb => Self::mix_in::<RGBA>,
|
||||
ColorSpace::LinearSrgb => Self::mix_in::<LinearRGBA>,
|
||||
ColorSpace::Xyz => Self::mix_in::<XYZD65A>,
|
||||
ColorSpace::XyzD50 => Self::mix_in::<XYZD50A>,
|
||||
ColorSpace::Lab => Self::mix_in::<LABA>,
|
||||
ColorSpace::Hwb => Self::mix_in::<HWBA>,
|
||||
ColorSpace::Hsl => Self::mix_in::<HSLA>,
|
||||
ColorSpace::Lch => Self::mix_in::<LCHA>,
|
||||
};
|
||||
mix_function(left_color, left_weight, right_color, right_weight, interpolation.hue)
|
||||
}
|
||||
|
||||
fn mix_in<S>(
|
||||
|
@ -388,9 +372,18 @@ fn interpolate_premultiplied_component(
|
|||
(left * left_weight * left_alpha + right * right_weight * right_alpha) * inverse_of_result_alpha
|
||||
}
|
||||
|
||||
fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolationMethod) {
|
||||
use std::f32::consts::{PI, TAU};
|
||||
// Normalize hue into [0, 2 * PI)
|
||||
fn normalize_hue(mut v: f32) -> f32 {
|
||||
while v < 0. {
|
||||
v += TAU;
|
||||
}
|
||||
while v >= TAU {
|
||||
v -= TAU;
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolationMethod) {
|
||||
// Adjust the hue angle as per
|
||||
// https://drafts.csswg.org/css-color/#hue-interpolation.
|
||||
//
|
||||
|
@ -413,18 +406,8 @@ fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolati
|
|||
return;
|
||||
}
|
||||
|
||||
// Normalize hue into [0, 2 * PI)
|
||||
fn normalize(v: &mut f32) {
|
||||
while *v < 0. {
|
||||
*v += TAU;
|
||||
}
|
||||
while *v > TAU {
|
||||
*v -= TAU;
|
||||
}
|
||||
}
|
||||
|
||||
normalize(left);
|
||||
normalize(right);
|
||||
*left = normalize_hue(*left);
|
||||
*right = normalize_hue(*right);
|
||||
|
||||
match hue_interpolation {
|
||||
// https://drafts.csswg.org/css-color/#shorter
|
||||
|
@ -534,57 +517,158 @@ macro_rules! impl_lerp {
|
|||
|
||||
impl_lerp!(RGBA, None);
|
||||
|
||||
/// An animated XYZA colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct XYZA {
|
||||
/// The x component.
|
||||
pub x: f32,
|
||||
/// The y component.
|
||||
pub y: f32,
|
||||
/// The z component.
|
||||
pub z: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
struct LinearRGBA {
|
||||
red: f32,
|
||||
green: f32,
|
||||
blue: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(XYZA, None);
|
||||
impl_lerp!(LinearRGBA, None);
|
||||
|
||||
/// An animated LABA colour.
|
||||
/// An animated XYZ D65 colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LABA {
|
||||
/// The lightness component.
|
||||
pub lightness: f32,
|
||||
/// The a component.
|
||||
pub a: f32,
|
||||
/// The b component.
|
||||
pub b: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
struct XYZD65A {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(XYZD65A, None);
|
||||
|
||||
/// An animated XYZ D50 colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct XYZD50A {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(XYZD50A, None);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct LABA {
|
||||
lightness: f32,
|
||||
a: f32,
|
||||
b: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(LABA, None);
|
||||
|
||||
/// An animated LCHA colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LCHA {
|
||||
/// The lightness component.
|
||||
pub lightness: f32,
|
||||
/// The chroma component.
|
||||
pub chroma: f32,
|
||||
/// The hua component.
|
||||
pub hue: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
#[repr(C)]
|
||||
struct LCHA {
|
||||
lightness: f32,
|
||||
chroma: f32,
|
||||
hue: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(LCHA, Some(2));
|
||||
|
||||
impl From<RGBA> for XYZA {
|
||||
/// Convert an RGBA colour to XYZ as specified in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||
/// An animated hwb() color.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct HWBA {
|
||||
hue: f32,
|
||||
white: f32,
|
||||
black: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(HWBA, Some(0));
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct HSLA {
|
||||
hue: f32,
|
||||
sat: f32,
|
||||
light: f32,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl_lerp!(HSLA, Some(0));
|
||||
|
||||
// https://drafts.csswg.org/css-color/#rgb-to-hsl
|
||||
//
|
||||
// We also return min/max for the hwb conversion.
|
||||
fn rgb_to_hsl(rgba: RGBA) -> (HSLA, f32, f32) {
|
||||
let RGBA { red, green, blue, alpha } = rgba;
|
||||
let max = red.max(green).max(blue);
|
||||
let min = red.min(green).min(blue);
|
||||
let mut hue = std::f32::NAN;
|
||||
let mut sat = 0.;
|
||||
let light = (min + max) / 2.;
|
||||
let d = max - min;
|
||||
|
||||
if d != 0. {
|
||||
sat = if light == 0.0 || light == 1.0 {
|
||||
0.
|
||||
} else {
|
||||
(max - light) / light.min(1. - light)
|
||||
};
|
||||
|
||||
if max == red {
|
||||
hue = (green - blue) / d + if green < blue { 6. } else { 0. }
|
||||
} else if max == green {
|
||||
hue = (blue - red) / d + 2.;
|
||||
} else {
|
||||
hue = (red - green) / d + 4.;
|
||||
}
|
||||
|
||||
hue *= 60.;
|
||||
hue *= RAD_PER_DEG;
|
||||
}
|
||||
|
||||
(HSLA { hue, sat, light, alpha }, min, max)
|
||||
}
|
||||
|
||||
impl From<RGBA> for HSLA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
rgb_to_hsl(rgba).0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HSLA> for RGBA {
|
||||
fn from(hsla: HSLA) -> Self {
|
||||
// cssparser expects hue in the 0..1 range.
|
||||
let hue_normalized = normalize_hue(hsla.hue) / TAU;
|
||||
let (r, g, b) = cssparser::hsl_to_rgb(hue_normalized, hsla.sat, hsla.light);
|
||||
RGBA::new(r, g, b, hsla.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for HWBA {
|
||||
// https://drafts.csswg.org/css-color/#rgb-to-hwb
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
let (hsl, min, max) = rgb_to_hsl(rgba);
|
||||
Self {
|
||||
hue: hsl.hue,
|
||||
white: min,
|
||||
black: 1. - max,
|
||||
alpha: rgba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HWBA> for RGBA {
|
||||
fn from(hwba: HWBA) -> Self {
|
||||
let hue_normalized = normalize_hue(hwba.hue) / TAU;
|
||||
let (r, g, b) = cssparser::hwb_to_rgb(hue_normalized, hwba.white, hwba.black);
|
||||
RGBA::new(r, g, b, hwba.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for LinearRGBA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
fn linearize(value: f32) -> f32 {
|
||||
let sign = if value < 0. { -1. } else { 1. };
|
||||
|
@ -595,15 +679,39 @@ impl From<RGBA> for XYZA {
|
|||
|
||||
sign * ((abs + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
Self {
|
||||
red: linearize(rgba.red),
|
||||
green: linearize(rgba.green),
|
||||
blue: linearize(rgba.blue),
|
||||
alpha: rgba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const SRGB_TO_XYZ: Transform3D<f32> = Transform3D::new(
|
||||
0.41239079926595934, 0.21263900587151027, 0.01933081871559182, 0.,
|
||||
0.357584339383878, 0.715168678767756, 0.11919477979462598, 0.,
|
||||
0.1804807884018343, 0.07219231536073371, 0.9505321522496607, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
impl From<LinearRGBA> for RGBA {
|
||||
fn from(lrgba: LinearRGBA) -> Self {
|
||||
fn delinearize(value: f32) -> f32 {
|
||||
let sign = if value < 0. { -1. } else { 1. };
|
||||
let abs = value.abs();
|
||||
|
||||
if abs > 0.0031308 {
|
||||
sign * (1.055 * abs.powf(1. / 2.4) - 0.055)
|
||||
} else {
|
||||
12.92 * value
|
||||
}
|
||||
}
|
||||
Self {
|
||||
red: delinearize(lrgba.red),
|
||||
green: delinearize(lrgba.green),
|
||||
blue: delinearize(lrgba.blue),
|
||||
alpha: lrgba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZD65A> for XYZD50A {
|
||||
fn from(d65: XYZD65A) -> Self {
|
||||
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const BRADFORD: Transform3D<f32> = Transform3D::new(
|
||||
1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0.,
|
||||
|
@ -611,34 +719,100 @@ impl From<RGBA> for XYZA {
|
|||
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
// 1. Convert from sRGB to linear-light sRGB (undo gamma encoding).
|
||||
let rgb = Vector3D::new(
|
||||
linearize(rgba.red),
|
||||
linearize(rgba.green),
|
||||
linearize(rgba.blue),
|
||||
);
|
||||
|
||||
// 2. Convert from linear sRGB to CIE XYZ.
|
||||
// 3. Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in XYZ
|
||||
// with the Bradford transform.
|
||||
let xyz = SRGB_TO_XYZ.then(&BRADFORD).transform_vector3d(rgb);
|
||||
|
||||
XYZA {
|
||||
x: xyz.x,
|
||||
y: xyz.y,
|
||||
z: xyz.z,
|
||||
alpha: rgba.alpha,
|
||||
let d50 = BRADFORD.transform_vector3d(Vector3D::new(d65.x, d65.y, d65.z));
|
||||
Self {
|
||||
x: d50.x,
|
||||
y: d50.y,
|
||||
z: d50.z,
|
||||
alpha: d65.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZA> for LABA {
|
||||
impl From<XYZD50A> for XYZD65A {
|
||||
fn from(d50: XYZD50A) -> Self {
|
||||
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const BRADFORD_INVERSE: Transform3D<f32> = Transform3D::new(
|
||||
0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.,
|
||||
-0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.,
|
||||
0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
let d65 = BRADFORD_INVERSE.transform_vector3d(Vector3D::new(d50.x, d50.y, d50.z));
|
||||
Self {
|
||||
x: d65.x,
|
||||
y: d65.y,
|
||||
z: d65.z,
|
||||
alpha: d50.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LinearRGBA> for XYZD65A {
|
||||
fn from(lrgba: LinearRGBA) -> Self {
|
||||
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const LSRGB_TO_XYZ: Transform3D<f32> = Transform3D::new(
|
||||
0.41239079926595934, 0.21263900587151027, 0.01933081871559182, 0.,
|
||||
0.357584339383878, 0.715168678767756, 0.11919477979462598, 0.,
|
||||
0.1804807884018343, 0.07219231536073371, 0.9505321522496607, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
let linear_rgb = Vector3D::new(lrgba.red, lrgba.green, lrgba.blue);
|
||||
let xyz = LSRGB_TO_XYZ.transform_vector3d(linear_rgb);
|
||||
Self {
|
||||
x: xyz.x,
|
||||
y: xyz.y,
|
||||
z: xyz.z,
|
||||
alpha: lrgba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZD65A> for LinearRGBA {
|
||||
fn from(d65: XYZD65A) -> Self {
|
||||
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const XYZ_TO_LSRGB: Transform3D<f32> = Transform3D::new(
|
||||
3.2409699419045226, -0.9692436362808796, 0.05563007969699366, 0.,
|
||||
-1.537383177570094, 1.8759675015077202, -0.20397695888897652, 0.,
|
||||
-0.4986107602930034, 0.04155505740717559, 1.0569715142428786, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
let xyz = Vector3D::new(d65.x, d65.y, d65.z);
|
||||
let rgb = XYZ_TO_LSRGB.transform_vector3d(xyz);
|
||||
Self {
|
||||
red: rgb.x,
|
||||
green: rgb.y,
|
||||
blue: rgb.z,
|
||||
alpha: d65.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZD65A> for RGBA {
|
||||
fn from(d65: XYZD65A) -> Self {
|
||||
Self::from(LinearRGBA::from(d65))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for XYZD65A {
|
||||
/// Convert an RGBA colour to XYZ as specified in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
Self::from(LinearRGBA::from(rgba))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZD50A> for LABA {
|
||||
/// Convert an XYZ colour to LAB as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||
/// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||
fn from(xyza: XYZA) -> Self {
|
||||
fn from(xyza: XYZD50A) -> Self {
|
||||
const WHITE: [f32; 3] = [0.96422, 1., 0.82521];
|
||||
|
||||
fn compute_f(value: f32) -> f32 {
|
||||
|
@ -704,7 +878,7 @@ impl From<LCHA> for LABA {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<LABA> for XYZA {
|
||||
impl From<LABA> for XYZD50A {
|
||||
/// Convert a CIELAB color to XYZ as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined
|
||||
|
@ -735,7 +909,7 @@ impl From<LABA> for XYZA {
|
|||
(116. * f2 - 16.) / KAPPA
|
||||
};
|
||||
|
||||
XYZA {
|
||||
Self {
|
||||
x: x * WHITE[0],
|
||||
y: y * WHITE[1],
|
||||
z: z * WHITE[2],
|
||||
|
@ -744,85 +918,38 @@ impl From<LABA> for XYZA {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<XYZA> for RGBA {
|
||||
/// Convert an XYZ color to sRGB as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://www.w3.org/TR/css-color-4/#lab-to-predefined
|
||||
/// [2]: https://www.w3.org/TR/css-color-4/#color-conversion-code
|
||||
fn from(xyza: XYZA) -> Self {
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const BRADFORD_INVERSE: Transform3D<f32> = Transform3D::new(
|
||||
0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.,
|
||||
-0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.,
|
||||
0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
impl From<XYZD50A> for RGBA {
|
||||
fn from(d50: XYZD50A) -> Self {
|
||||
Self::from(XYZD65A::from(d50))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const XYZ_TO_SRGB: Transform3D<f32> = Transform3D::new(
|
||||
3.2409699419045226, -0.9692436362808796, 0.05563007969699366, 0.,
|
||||
-1.537383177570094, 1.8759675015077202, -0.20397695888897652, 0.,
|
||||
-0.4986107602930034, 0.04155505740717559, 1.0569715142428786, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
// 2. Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint
|
||||
// used in sRGB, with the Bradford transform.
|
||||
// 3. Convert from (D65-adapted) CIE XYZ to linear-light srgb
|
||||
let xyz = Vector3D::new(xyza.x, xyza.y, xyza.z);
|
||||
let linear_rgb = BRADFORD_INVERSE.then(&XYZ_TO_SRGB).transform_vector3d(xyz);
|
||||
|
||||
// 4. Convert from linear-light srgb to srgb (do gamma encoding).
|
||||
fn delinearize(value: f32) -> f32 {
|
||||
let sign = if value < 0. { -1. } else { 1. };
|
||||
let abs = value.abs();
|
||||
|
||||
if abs > 0.0031308 {
|
||||
sign * (1.055 * abs.powf(1. / 2.4) - 0.055)
|
||||
} else {
|
||||
12.92 * value
|
||||
}
|
||||
}
|
||||
|
||||
let red = delinearize(linear_rgb.x);
|
||||
let green = delinearize(linear_rgb.y);
|
||||
let blue = delinearize(linear_rgb.z);
|
||||
|
||||
RGBA {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha: xyza.alpha,
|
||||
}
|
||||
impl From<RGBA> for XYZD50A {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
Self::from(XYZD65A::from(rgba))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for LABA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
let xyza: XYZA = rgba.into();
|
||||
xyza.into()
|
||||
Self::from(XYZD50A::from(rgba))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LABA> for RGBA {
|
||||
fn from(laba: LABA) -> Self {
|
||||
let xyza: XYZA = laba.into();
|
||||
xyza.into()
|
||||
Self::from(XYZD50A::from(laba))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for LCHA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
let xyza: XYZA = rgba.into();
|
||||
let laba: LABA = xyza.into();
|
||||
laba.into()
|
||||
Self::from(LABA::from(rgba))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LCHA> for RGBA {
|
||||
fn from(lcha: LCHA) -> Self {
|
||||
let laba: LABA = lcha.into();
|
||||
let xyza: XYZA = laba.into();
|
||||
xyza.into()
|
||||
Self::from(LABA::from(lcha))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,20 +26,30 @@ use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
|
|||
pub enum ColorSpace {
|
||||
/// The sRGB color space.
|
||||
Srgb,
|
||||
/// The linear-sRGB color space.
|
||||
LinearSrgb,
|
||||
/// The CIEXYZ color space.
|
||||
#[parse(aliases = "xyz-d65")]
|
||||
Xyz,
|
||||
/// https://drafts.csswg.org/css-color-4/#valdef-color-xyz
|
||||
XyzD50,
|
||||
/// The CIELAB color space.
|
||||
Lab,
|
||||
/// https://drafts.csswg.org/css-color-4/#valdef-hsl-hsl
|
||||
Hsl,
|
||||
/// https://drafts.csswg.org/css-color-4/#valdef-hwb-hwb
|
||||
Hwb,
|
||||
/// The CIELAB color space, expressed in cylindrical coordinates.
|
||||
Lch,
|
||||
// TODO: Oklab, Lch
|
||||
}
|
||||
|
||||
impl ColorSpace {
|
||||
/// Returns whether this is a `<polar-color-space>`.
|
||||
pub fn is_polar(self) -> bool {
|
||||
match self {
|
||||
Self::Srgb | Self::Xyz | Self::Lab => false,
|
||||
Self::Lch => true,
|
||||
Self::Srgb | Self::LinearSrgb | Self::Xyz | Self::XyzD50 | Self::Lab => false,
|
||||
Self::Hsl | Self::Hwb | Self::Lch => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue