diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 77091da01ec..773310dd9b6 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -105,8 +105,44 @@ impl Color { } /// Mix two colors into one. - pub fn mix(left: &Color, right: &Color, progress: f32) -> Self { - left.animate(right, Procedure::Interpolate { progress: progress as f64 }).unwrap() + pub fn mix( + left_color: &Color, + left_weight: f32, + right_color: &Color, + right_weight: f32, + ) -> Self { + let left_bg = left_color.scaled_rgba(); + let right_bg = right_color.scaled_rgba(); + let alpha = (left_bg.alpha * left_weight + + right_bg.alpha * right_weight) + .min(1.); + + let mut fg = 0.; + let mut red = 0.; + let mut green = 0.; + let mut blue = 0.; + + let colors = [ + (left_color, &left_bg, left_weight), + (right_color, &right_bg, right_weight), + ]; + + for &(color, bg, weight) in &colors { + fg += color.ratios.fg * weight; + + red += bg.red * bg.alpha * weight; + green += bg.green * bg.alpha * weight; + blue += bg.blue * bg.alpha * weight; + } + + let color = if alpha <= 0. { + RGBA::transparent() + } else { + let inv = 1. / alpha; + RGBA::new(red * inv, green * inv, blue * inv, alpha) + }; + + Self::new(color, ComplexColorRatios { bg: 1., fg }) } fn scaled_rgba(&self) -> RGBA { diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 6e608d2eb37..21b8abe6de8 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -28,8 +28,9 @@ use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; #[allow(missing_docs)] pub struct ColorMix { pub left: Color, + pub left_percentage: Percentage, pub right: Color, - pub percentage: Percentage, + pub right_percentage: Percentage, } #[cfg(feature = "gecko")] @@ -71,14 +72,27 @@ impl Parse for ColorMix { // TODO: support multiple interpolation spaces. input.expect_ident_matching("srgb")?; input.expect_comma()?; - let left = Color::parse(context, input)?; - let percentage = input.try_parse(|input| { - Percentage::parse(context, input) - }).unwrap_or_else(|_| Percentage::new(0.5)); - input.expect_comma()?; - let right = Color::parse(context, input)?; - Ok(ColorMix { left, right, percentage }) + let left = Color::parse(context, input)?; + let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok(); + + input.expect_comma()?; + + let right = Color::parse(context, input)?; + let right_percentage = input + .try_parse(|input| Percentage::parse(context, input)) + .unwrap_or_else(|_| { + Percentage::new(1.0 - left_percentage.map_or(0.5, |p| p.get())) + }); + + let left_percentage = + left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get())); + Ok(ColorMix { + left, + left_percentage, + right, + right_percentage, + }) }) } } @@ -88,14 +102,31 @@ impl ToCss for ColorMix { where W: Write, { + fn can_omit(percent: &Percentage, other: &Percentage, is_left: bool) -> bool { + if percent.is_calc() { + return false; + } + if percent.get() == 0.5 { + return other.get() == 0.5; + } + if is_left { + return false; + } + (1.0 - percent.get() - other.get()).abs() <= f32::EPSILON + } + dest.write_str("color-mix(in srgb, ")?; self.left.to_css(dest)?; - if self.percentage.get() != 0.5 || self.percentage.is_calc() { + if !can_omit(&self.left_percentage, &self.right_percentage, true) { dest.write_str(" ")?; - self.percentage.to_css(dest)?; + self.left_percentage.to_css(dest)?; } dest.write_str(", ")?; self.right.to_css(dest)?; + if !can_omit(&self.right_percentage, &self.left_percentage, false) { + dest.write_str(" ")?; + self.right_percentage.to_css(dest)?; + } dest.write_str(")") } } @@ -660,7 +691,12 @@ impl Color { let left = mix.left.to_computed_color(context)?.to_animated_value(); let right = mix.right.to_computed_color(context)?.to_animated_value(); - ToAnimatedValue::from_animated_value(AnimatedColor::mix(&left, &right, mix.percentage.get())) + ToAnimatedValue::from_animated_value(AnimatedColor::mix( + &left, + mix.left_percentage.get(), + &right, + mix.right_percentage.get(), + )) }, #[cfg(feature = "gecko")] Color::System(system) => system.compute(context?),