Skip to content

Commit 999f556

Browse files
committed
Bug 2007772 - Parse multiple items for color-mix() function r=layout-reviewers,firefox-style-system-reviewers,dshin
The color-mix() function can now take multiple color/percentage pairs and mix them all together using the new algorithm. Tests for the serialization is pending here: web-platform-tests/wpt#57234 Differential Revision: https://phabricator.services.mozilla.com/D278117
1 parent f51305b commit 999f556

File tree

6 files changed

+165
-113
lines changed

6 files changed

+165
-113
lines changed

modules/libpref/init/StaticPrefList.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10216,6 +10216,13 @@
1021610216
mirror: always
1021710217
rust: true
1021810218

10219+
# Is multiple color items upported for color-mix()?
10220+
- name: layout.css.color-mix-multi-color.enabled
10221+
type: RelaxedAtomicBool
10222+
value: @IS_NIGHTLY_BUILD@
10223+
mirror: always
10224+
rust: true
10225+
1021910226
# Is support for the contrast-color() function enabled?
1022010227
- name: layout.css.contrast-color.enabled
1022110228
type: RelaxedAtomicBool

servo/components/style/values/animated/color.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
//! Animated types for CSS colors.
66
7+
use style_traits::owned_slice::OwnedSlice;
8+
79
use crate::color::mix::ColorInterpolationMethod;
810
use crate::color::AbsoluteColor;
911
use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
@@ -64,16 +66,19 @@ impl Animate for Color {
6466
#[inline]
6567
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
6668
let (left_weight, right_weight) = procedure.weights();
69+
6770
Ok(Self::from_color_mix(ColorMix {
6871
interpolation: ColorInterpolationMethod::srgb(),
69-
left: GenericColorMixItem {
70-
color: self.clone(),
71-
percentage: Percentage(left_weight as f32),
72-
},
73-
right: GenericColorMixItem {
74-
color: other.clone(),
75-
percentage: Percentage(right_weight as f32),
76-
},
72+
items: OwnedSlice::from_slice(&[
73+
GenericColorMixItem {
74+
color: self.clone(),
75+
percentage: Percentage(left_weight as f32),
76+
},
77+
GenericColorMixItem {
78+
color: other.clone(),
79+
percentage: Percentage(right_weight as f32),
80+
},
81+
]),
7782
// See https://github.com/w3c/csswg-drafts/issues/7324
7883
flags: ColorMixFlags::empty(),
7984
}))

servo/components/style/values/computed/color.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ impl Color {
7777
Self::ColorMix(ref mix) => {
7878
use crate::color::mix;
7979

80-
let left = mix.left.color.resolve_to_absolute(current_color);
81-
let right = mix.right.color.resolve_to_absolute(current_color);
8280
mix::mix_many(
8381
mix.interpolation,
84-
[
85-
mix::ColorMixItem::new(left, mix.left.percentage.to_percentage()),
86-
mix::ColorMixItem::new(right, mix.right.percentage.to_percentage()),
87-
],
82+
mix.items.iter().map(|item| {
83+
mix::ColorMixItem::new(
84+
item.color.resolve_to_absolute(current_color),
85+
item.percentage.to_percentage(),
86+
)
87+
}),
8888
mix.flags,
8989
)
9090
},

servo/components/style/values/generics/color.rs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
//! Generic types for color properties.
66
7+
use crate::color::ColorMixItemList;
78
use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
89
use crate::derives::*;
910
use crate::values::{
1011
computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,
1112
};
1213
use std::fmt::{self, Write};
13-
use style_traits::{CssWriter, ToCss};
14+
use style_traits::{owned_slice::OwnedSlice, CssWriter, ToCss};
1415

1516
/// This struct represents a combined color from a numeric color and
1617
/// the current foreground color (currentcolor keyword).
@@ -78,8 +79,7 @@ pub struct GenericColorMixItem<Color, Percentage> {
7879
#[repr(C)]
7980
pub struct GenericColorMix<Color, Percentage> {
8081
pub interpolation: ColorInterpolationMethod,
81-
pub left: GenericColorMixItem<Color, Percentage>,
82-
pub right: GenericColorMixItem<Color, Percentage>,
82+
pub items: OwnedSlice<GenericColorMixItem<Color, Percentage>>,
8383
pub flags: ColorMixFlags,
8484
}
8585

@@ -90,23 +90,6 @@ impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, P
9090
where
9191
W: Write,
9292
{
93-
fn can_omit<Percentage: ToPercentage>(
94-
a: &Percentage,
95-
b: &Percentage,
96-
is_left: bool,
97-
) -> bool {
98-
if a.is_calc() {
99-
return false;
100-
}
101-
if a.to_percentage() == 0.5 {
102-
return b.to_percentage() == 0.5;
103-
}
104-
if is_left {
105-
return false;
106-
}
107-
(1.0 - a.to_percentage() - b.to_percentage()).abs() <= f32::EPSILON
108-
}
109-
11093
dest.write_str("color-mix(")?;
11194

11295
// If the color interpolation method is oklab (which is now the default),
@@ -117,18 +100,51 @@ impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, P
117100
dest.write_str(", ")?;
118101
}
119102

120-
self.left.color.to_css(dest)?;
121-
if !can_omit(&self.left.percentage, &self.right.percentage, true) {
122-
dest.write_char(' ')?;
123-
self.left.percentage.to_css(dest)?;
124-
}
103+
let uniform = self
104+
.items
105+
.split_first()
106+
.map(|(first, rest)| {
107+
rest.iter()
108+
.all(|item| item.percentage.to_percentage() == first.percentage.to_percentage())
109+
})
110+
.unwrap_or(false);
111+
let uniform_value = 1.0 / self.items.len() as f32;
112+
113+
let is_pair = self.items.len() == 2;
125114

126-
dest.write_str(", ")?;
115+
for (index, item) in self.items.iter().enumerate() {
116+
if index != 0 {
117+
dest.write_str(", ")?;
118+
}
119+
120+
item.color.to_css(dest)?;
121+
122+
let omit = if is_pair {
123+
let can_omit = |a: &Percentage, b: &Percentage, is_left| {
124+
if a.is_calc() {
125+
return false;
126+
}
127+
if a.to_percentage() == 0.5 {
128+
return b.to_percentage() == 0.5;
129+
}
130+
if is_left {
131+
return false;
132+
}
133+
(1.0 - a.to_percentage() - b.to_percentage()).abs() <= f32::EPSILON
134+
};
135+
136+
let other = &self.items[1 - index].percentage;
137+
can_omit(&item.percentage, other, index == 0)
138+
} else {
139+
!item.percentage.is_calc()
140+
&& uniform
141+
&& item.percentage.to_percentage() == uniform_value
142+
};
127143

128-
self.right.color.to_css(dest)?;
129-
if !can_omit(&self.right.percentage, &self.left.percentage, false) {
130-
dest.write_char(' ')?;
131-
self.right.percentage.to_css(dest)?;
144+
if !omit {
145+
dest.write_char(' ')?;
146+
item.percentage.to_css(dest)?;
147+
}
132148
}
133149

134150
dest.write_char(')')
@@ -144,17 +160,15 @@ impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
144160
{
145161
use crate::color::mix;
146162

147-
let left = self.left.color.as_absolute()?;
148-
let right = self.right.color.as_absolute()?;
149-
150-
Some(mix::mix_many(
151-
self.interpolation,
152-
[
153-
mix::ColorMixItem::new(*left, self.left.percentage.to_percentage()),
154-
mix::ColorMixItem::new(*right, self.right.percentage.to_percentage()),
155-
],
156-
self.flags,
157-
))
163+
let mut items = ColorMixItemList::with_capacity(self.items.len());
164+
for item in self.items.iter() {
165+
items.push(mix::ColorMixItem::new(
166+
*item.color.as_absolute()?,
167+
item.percentage.to_percentage(),
168+
))
169+
}
170+
171+
Some(mix::mix_many(self.interpolation, items, self.flags))
158172
}
159173
}
160174

0 commit comments

Comments
 (0)