Bug 1822041 - Color interpolation takes none keyword into account r=emilio,layout-reviewers

Now that the none keyword is available, we can take it into account when
interpolating colors following the rules from the spec here:

https://drafts.csswg.org/css-color-4/#interpolation-missing

Differential Revision: https://phabricator.services.mozilla.com/D172666
This commit is contained in:
Tiaan Louw 2023-03-19 10:09:53 +00:00
parent 9d5cddea34
commit 03828b4c67
5 changed files with 176 additions and 458 deletions

View File

@ -25,7 +25,7 @@ inline StyleAbsoluteColor StyleAbsoluteColor::FromColor(nscolor aColor) {
inline StyleAbsoluteColor StyleAbsoluteColor::Srgb(float red, float green, inline StyleAbsoluteColor StyleAbsoluteColor::Srgb(float red, float green,
float blue, float alpha) { float blue, float alpha) {
return StyleAbsoluteColor{StyleColorComponents{red, green, blue}, alpha, return StyleAbsoluteColor{StyleColorComponents{red, green, blue}, alpha,
StyleColorSpace::Srgb, StyleSerializationFlags{0}}; StyleColorSpace::Srgb, StyleColorFlags{0}};
} }
inline StyleAbsoluteColor StyleAbsoluteColor::Transparent() { inline StyleAbsoluteColor StyleAbsoluteColor::Transparent() {

View File

@ -4,7 +4,7 @@
//! Color mixing/interpolation. //! Color mixing/interpolation.
use super::{AbsoluteColor, ColorComponents, ColorSpace}; use super::{AbsoluteColor, ColorComponents, ColorFlags, ColorSpace};
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use cssparser::Parser; use cssparser::Parser;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -135,24 +135,6 @@ impl ToCss for ColorInterpolationMethod {
} }
} }
/// A color modelled in a specific color space (such as sRGB or CIE XYZ).
///
/// For now, colors modelled in other spaces need to be convertible to and from
/// `RGBA` because we use sRGB for displaying colors.
trait ModelledColor: Clone + Copy {
/// Linearly interpolate between the left and right colors.
///
/// The HueInterpolationMethod parameter is only for color spaces where the hue is
/// represented as an angle (e.g., CIE LCH).
fn lerp(
left_bg: &Self,
left_weight: f32,
right_bg: &Self,
right_weight: f32,
hue_interpolation: HueInterpolationMethod,
) -> Self;
}
/// Mix two colors into one. /// Mix two colors into one.
pub fn mix( pub fn mix(
interpolation: ColorInterpolationMethod, interpolation: ColorInterpolationMethod,
@ -187,6 +169,38 @@ pub fn mix(
) )
} }
/// What the outcome of each component should be in a mix result.
#[derive(Clone, Copy)]
#[repr(u8)]
enum ComponentMixOutcome {
/// Mix the left and right sides to give the result.
Mix,
/// Carry the left side forward to the result.
UseLeft,
/// Carry the right side forward to the result.
UseRight,
/// The resulting component should also be none.
None,
}
impl ComponentMixOutcome {
fn from_colors(
left: &AbsoluteColor,
right: &AbsoluteColor,
flags_to_check: ColorFlags,
) -> Self {
match (
left.flags.contains(flags_to_check),
right.flags.contains(flags_to_check),
) {
(true, true) => Self::None,
(true, false) => Self::UseRight,
(false, true) => Self::UseLeft,
(false, false) => Self::Mix,
}
}
}
fn mix_in( fn mix_in(
color_space: ColorSpace, color_space: ColorSpace,
left_color: &AbsoluteColor, left_color: &AbsoluteColor,
@ -196,6 +210,13 @@ fn mix_in(
hue_interpolation: HueInterpolationMethod, hue_interpolation: HueInterpolationMethod,
alpha_multiplier: f32, alpha_multiplier: f32,
) -> AbsoluteColor { ) -> AbsoluteColor {
let outcomes = [
ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C1_IS_NONE),
ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C2_IS_NONE),
ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C3_IS_NONE),
ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::ALPHA_IS_NONE),
];
// Convert both colors into the interpolation color space. // Convert both colors into the interpolation color space.
let left = left_color.to_color_space(color_space); let left = left_color.to_color_space(color_space);
let left = left.raw_components(); let left = left.raw_components();
@ -203,13 +224,14 @@ fn mix_in(
let right = right_color.to_color_space(color_space); let right = right_color.to_color_space(color_space);
let right = right.raw_components(); let right = right.raw_components();
let result = interpolate_premultiplied( let (result, result_flags) = interpolate_premultiplied(
&left, &left,
left_weight, left_weight,
&right, &right,
right_weight, right_weight,
color_space.hue_index(), color_space.hue_index(),
hue_interpolation, hue_interpolation,
&outcomes,
); );
let alpha = if alpha_multiplier != 1.0 { let alpha = if alpha_multiplier != 1.0 {
@ -225,11 +247,19 @@ fn mix_in(
// then divide after calculations? // then divide after calculations?
let alpha = (alpha * 1000.0).round() / 1000.0; let alpha = (alpha * 1000.0).round() / 1000.0;
AbsoluteColor::new( let mut result = AbsoluteColor::new(
color_space, color_space,
ColorComponents(result[0], result[1], result[2]), ColorComponents(result[0], result[1], result[2]),
alpha, alpha,
) );
result.flags = result_flags;
// If both sides are legacy RGB, then the result stays in legacy RGB.
if !left_color.is_legacy_color() || !right_color.is_legacy_color() {
result.flags.insert(ColorFlags::AS_COLOR_FUNCTION);
}
result
} }
fn interpolate_premultiplied_component( fn interpolate_premultiplied_component(
@ -239,9 +269,8 @@ fn interpolate_premultiplied_component(
right: f32, right: f32,
right_weight: f32, right_weight: f32,
right_alpha: f32, right_alpha: f32,
inverse_of_result_alpha: f32,
) -> f32 { ) -> f32 {
(left * left_weight * left_alpha + right * right_weight * right_alpha) * inverse_of_result_alpha left * left_weight * left_alpha + right * right_weight * right_alpha
} }
// Normalize hue into [0, 360) // Normalize hue into [0, 360)
@ -323,6 +352,63 @@ fn interpolate_hue(
left * left_weight + right * right_weight left * left_weight + right * right_weight
} }
struct InterpolatedAlpha {
/// The adjusted left alpha value.
left: f32,
/// The adjusted right alpha value.
right: f32,
/// The interpolated alpha value.
interpolated: f32,
/// Whether the alpha component should be `none`.
is_none: bool,
}
fn interpolate_alpha(
left: f32,
left_weight: f32,
right: f32,
right_weight: f32,
outcome: ComponentMixOutcome,
) -> InterpolatedAlpha {
// <https://drafts.csswg.org/css-color-4/#interpolation-missing>
let mut result = match outcome {
ComponentMixOutcome::Mix => {
let interpolated = left * left_weight + right * right_weight;
InterpolatedAlpha {
left,
right,
interpolated,
is_none: false,
}
},
ComponentMixOutcome::UseLeft => InterpolatedAlpha {
left,
right: left,
interpolated: left,
is_none: false,
},
ComponentMixOutcome::UseRight => InterpolatedAlpha {
left: right,
right,
interpolated: right,
is_none: false,
},
ComponentMixOutcome::None => InterpolatedAlpha {
left: 1.0,
right: 1.0,
interpolated: 0.0,
is_none: true,
},
};
// Clip all alpha values to [0.0..1.0].
result.left = result.left.clamp(0.0, 1.0);
result.right = result.right.clamp(0.0, 1.0);
result.interpolated = result.interpolated.clamp(0.0, 1.0);
result
}
fn interpolate_premultiplied( fn interpolate_premultiplied(
left: &[f32; 4], left: &[f32; 4],
left_weight: f32, left_weight: f32,
@ -330,39 +416,60 @@ fn interpolate_premultiplied(
right_weight: f32, right_weight: f32,
hue_index: Option<usize>, hue_index: Option<usize>,
hue_interpolation: HueInterpolationMethod, hue_interpolation: HueInterpolationMethod,
) -> [f32; 4] { outcomes: &[ComponentMixOutcome; 4],
let left_alpha = left[3]; ) -> ([f32; 4], ColorFlags) {
let right_alpha = right[3]; let alpha = interpolate_alpha(left[3], left_weight, right[3], right_weight, outcomes[3]);
let result_alpha = (left_alpha * left_weight + right_alpha * right_weight).min(1.); let mut flags = if alpha.is_none {
ColorFlags::ALPHA_IS_NONE
} else {
ColorFlags::empty()
};
let mut result = [0.; 4]; let mut result = [0.; 4];
if result_alpha <= 0. {
return result;
}
let inverse_of_result_alpha = 1. / result_alpha;
for i in 0..3 { for i in 0..3 {
let is_hue = hue_index == Some(i); match outcomes[i] {
result[i] = if is_hue { ComponentMixOutcome::Mix => {
interpolate_hue( let is_hue = hue_index == Some(i);
left[i], result[i] = if is_hue {
left_weight, normalize_hue(interpolate_hue(
right[i], left[i],
right_weight, left_weight,
hue_interpolation, right[i],
) right_weight,
} else { hue_interpolation,
interpolate_premultiplied_component( ))
left[i], } else {
left_weight, let interpolated = interpolate_premultiplied_component(
left_alpha, left[i],
right[i], left_weight,
right_weight, alpha.left,
right_alpha, right[i],
inverse_of_result_alpha, right_weight,
) alpha.right,
}; );
}
result[3] = result_alpha;
result if alpha.interpolated == 0.0 {
interpolated
} else {
interpolated / alpha.interpolated
}
};
},
ComponentMixOutcome::UseLeft => result[i] = left[i],
ComponentMixOutcome::UseRight => result[i] = right[i],
ComponentMixOutcome::None => {
result[i] = 0.0;
match i {
0 => flags.insert(ColorFlags::C1_IS_NONE),
1 => flags.insert(ColorFlags::C2_IS_NONE),
2 => flags.insert(ColorFlags::C3_IS_NONE),
_ => unreachable!(),
}
},
}
}
result[3] = alpha.interpolated;
(result, flags)
} }

View File

@ -130,7 +130,7 @@ bitflags! {
/// Flags used when serializing colors. /// Flags used when serializing colors.
#[derive(Default, MallocSizeOf, ToShmem)] #[derive(Default, MallocSizeOf, ToShmem)]
#[repr(C)] #[repr(C)]
pub struct SerializationFlags : u8 { pub struct ColorFlags : u8 {
/// If set, serializes sRGB colors into `color(srgb ...)` instead of /// If set, serializes sRGB colors into `color(srgb ...)` instead of
/// `rgba(...)`. /// `rgba(...)`.
const AS_COLOR_FUNCTION = 1 << 0; const AS_COLOR_FUNCTION = 1 << 0;
@ -157,7 +157,7 @@ pub struct AbsoluteColor {
/// The current color space that the components represent. /// The current color space that the components represent.
pub color_space: ColorSpace, pub color_space: ColorSpace,
/// Extra flags used durring serialization of this color. /// Extra flags used durring serialization of this color.
pub flags: SerializationFlags, pub flags: ColorFlags,
} }
/// Given an [`AbsoluteColor`], return the 4 float components as the type given, /// Given an [`AbsoluteColor`], return the 4 float components as the type given,
@ -207,7 +207,7 @@ impl AbsoluteColor {
components, components,
alpha: alpha.clamp(0.0, 1.0), alpha: alpha.clamp(0.0, 1.0),
color_space, color_space,
flags: SerializationFlags::empty(), flags: ColorFlags::empty(),
} }
} }
@ -242,7 +242,7 @@ impl AbsoluteColor {
pub fn is_legacy_color(&self) -> bool { pub fn is_legacy_color(&self) -> bool {
// rgb(), rgba(), hsl(), hsla(), hwb(), hwba() // rgb(), rgba(), hsl(), hsla(), hwb(), hwba()
match self.color_space { match self.color_space {
ColorSpace::Srgb => !self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION), ColorSpace::Srgb => !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION),
ColorSpace::Hsl | ColorSpace::Hwb => true, ColorSpace::Hsl | ColorSpace::Hwb => true,
_ => false, _ => false,
} }
@ -377,7 +377,7 @@ impl ToCss for AbsoluteColor {
{ {
macro_rules! value_or_none { macro_rules! value_or_none {
($v:expr,$flag:tt) => {{ ($v:expr,$flag:tt) => {{
if self.flags.contains(SerializationFlags::$flag) { if self.flags.contains(ColorFlags::$flag) {
None None
} else { } else {
Some($v) Some($v)
@ -402,7 +402,7 @@ impl ToCss for AbsoluteColor {
Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest) Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest)
}, },
ColorSpace::Srgb if !self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION) => { ColorSpace::Srgb if !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION) => {
// Althought we are passing Option<_> in here, the to_css fn // Althought we are passing Option<_> in here, the to_css fn
// knows that the "none" keyword is not supported in the // knows that the "none" keyword is not supported in the
// rgb/rgba legacy syntax. // rgb/rgba legacy syntax.
@ -431,7 +431,7 @@ impl ToCss for AbsoluteColor {
let color_space = match self.color_space { let color_space = match self.color_space {
ColorSpace::Srgb => { ColorSpace::Srgb => {
debug_assert!( debug_assert!(
self.flags.contains(SerializationFlags::AS_COLOR_FUNCTION), self.flags.contains(ColorFlags::AS_COLOR_FUNCTION),
"The case without this flag should be handled in the wrapping match case!!" "The case without this flag should be handled in the wrapping match case!!"
); );

View File

@ -6,7 +6,7 @@
use super::AllowQuirks; use super::AllowQuirks;
use crate::color::mix::ColorInterpolationMethod; use crate::color::mix::ColorInterpolationMethod;
use crate::color::{AbsoluteColor, ColorComponents, ColorSpace, SerializationFlags}; use crate::color::{AbsoluteColor, ColorComponents, ColorFlags, 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};
@ -400,14 +400,14 @@ fn new_absolute(
c3: Option<f32>, c3: Option<f32>,
alpha: Option<f32>, alpha: Option<f32>,
) -> Color { ) -> Color {
let mut flags = SerializationFlags::empty(); let mut flags = ColorFlags::empty();
macro_rules! c { macro_rules! c {
($v:expr,$flag:tt) => {{ ($v:expr,$flag:tt) => {{
if let Some(value) = $v { if let Some(value) = $v {
value value
} else { } else {
flags |= SerializationFlags::$flag; flags |= ColorFlags::$flag;
0.0 0.0
} }
}}; }};
@ -505,7 +505,7 @@ impl cssparser::FromParsedColor for Color {
let mut result = new_absolute(color_space.into(), c1, c2, c3, alpha); let mut result = new_absolute(color_space.into(), c1, c2, c3, alpha);
if let Color::Absolute(ref mut absolute) = result { if let Color::Absolute(ref mut absolute) = result {
if matches!(absolute.color.color_space, ColorSpace::Srgb) { if matches!(absolute.color.color_space, ColorSpace::Srgb) {
absolute.color.flags |= SerializationFlags::AS_COLOR_FUNCTION; absolute.color.flags |= ColorFlags::AS_COLOR_FUNCTION;
} }
} }
result result
@ -621,10 +621,8 @@ impl Color {
absolute.color.color_space, absolute.color.color_space,
ColorSpace::Srgb | ColorSpace::Hsl ColorSpace::Srgb | ColorSpace::Hsl
); );
let is_color_function = absolute let is_color_function =
.color absolute.color.flags.contains(ColorFlags::AS_COLOR_FUNCTION);
.flags
.contains(SerializationFlags::AS_COLOR_FUNCTION);
let pref_enabled = static_prefs::pref!("layout.css.more_color_4.enabled"); let pref_enabled = static_prefs::pref!("layout.css.more_color_4.enabled");
(is_legacy_color && !is_color_function) || pref_enabled (is_legacy_color && !is_color_function) || pref_enabled

View File

@ -1,387 +0,0 @@
[color-computed-color-mix-function.html]
[Property color value 'color-mix(in hsl, hsl(none none none), hsl(30deg 40% 80%))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(120deg 20% 40%), hsl(none none none))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(120deg 20% none), hsl(30deg 40% 60%))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(120deg 20% 40%), hsl(30deg 20% none))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(none 20% 40%), hsl(30deg none 80%))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(120deg 40% 40% / none), hsl(0deg 40% 40%))']
expected: FAIL
[Property color value 'color-mix(in hsl, hsl(120deg 40% 40% / none), hsl(0deg 40% 40% / none))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(none none none), hwb(30deg 30% 40%))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% 20%), hwb(none none none))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% none), hwb(30deg 30% 40%))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% none))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(none 10% 20%), hwb(30deg none 40%))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40%))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40% / 0.5))']
expected: FAIL
[Property color value 'color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40% / none))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(100 0 50deg), lch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(100 0 330deg), lch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in lch shorter hue, lch(100 0 50deg), lch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in lch shorter hue, lch(100 0 330deg), lch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in lch increasing hue, lch(100 0 330deg), lch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in lch decreasing hue, lch(100 0 50deg), lch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(none none none), lch(none none none))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(none none none), lch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 30deg), lch(none none none))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 none), lch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 30deg), lch(50 60 none))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(none 20 30deg), lch(50 none 70deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / 0.5))']
expected: FAIL
[Property color value 'color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / none))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(100 0 50deg), oklch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(100 0 330deg), oklch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in oklch shorter hue, oklch(100 0 50deg), oklch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in oklch shorter hue, oklch(100 0 330deg), oklch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in oklch increasing hue, oklch(100 0 330deg), oklch(100 0 50deg))']
expected: FAIL
[Property color value 'color-mix(in oklch decreasing hue, oklch(100 0 50deg), oklch(100 0 330deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(none none none), oklch(none none none))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(none none none), oklch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(none none none))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 none), oklch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 30deg), oklch(50 60 none))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(none 20 30deg), oklch(50 none 70deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / 0.5))']
expected: FAIL
[Property color value 'color-mix(in oklch, oklch(10 20 30deg / none), oklch(50 60 70deg / none))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(none none none), lab(none none none))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(none none none), lab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 30), lab(none none none))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 none), lab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 30), lab(50 60 none))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(none 20 30), lab(50 none 70))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / none))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(none none none), oklab(none none none))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(none none none), oklab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(none none none))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 none), oklab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 30), oklab(50 60 none))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(none 20 30), oklab(50 none 70))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in oklab, oklab(10 20 30 / none), oklab(50 60 70 / none))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 25%, color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, 25% color(srgb .1 .2 .3), color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 .7) 25%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), 25% color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 25%, color(srgb .5 .6 .7) 75%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 30%, color(srgb .5 .6 .7) 90%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 12.5%, color(srgb .5 .6 .7) 37.5%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3) 0%, color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .5), color(srgb .5 .6 .7 / .8))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8))']
expected: FAIL
[Property color value 'color-mix(in srgb, 25% color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8) 25%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4), 25% color(srgb .5 .6 .7 / .8))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8) 75%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4) 30%, color(srgb .5 .6 .7 / .8) 90%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / .4) 0%, color(srgb .5 .6 .7 / .8))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb 2 3 4 / 5), color(srgb 4 6 8 / 10))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb -2 -3 -4), color(srgb -4 -6 -8))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb -2 -3 -4 / -5), color(srgb -4 -6 -8 / -10))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb none none none), color(srgb none none none))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb none none none), color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), color(srgb none none none))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 none), color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 none))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb none .2 .3), color(srgb .5 none .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / none))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear none none none))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear none none none))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 none), color(srgb-linear .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 none))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear none .2 .3), color(srgb-linear .5 none .7))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / none))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz none none none), color(xyz .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 none), color(xyz .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 none))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz none .2 .3), color(xyz .5 none .7))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 none), color(xyz-d50 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 none .2 .3), color(xyz-d50 .5 none .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 none none none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 none), color(xyz-d65 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 none))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 none .2 .3), color(xyz-d65 .5 none .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / 0.5))']
expected: FAIL
[Property color value 'color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / none))']
expected: FAIL