diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-05-14 23:24:48 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-14 23:24:48 -0500 |
commit | 94e977efebb93ef411222edc219bc9f30430a3f9 (patch) | |
tree | ed7820a4f07395d77139df67c1ce8910049f44b4 | |
parent | 8366b4d4f933f3efcadb6e337f2197cadca3e6ed (diff) | |
parent | 5539728b3cc435e65c61294ac2a1a5c6de1b2a27 (diff) | |
download | servo-94e977efebb93ef411222edc219bc9f30430a3f9.tar.gz servo-94e977efebb93ef411222edc219bc9f30430a3f9.zip |
Auto merge of #16863 - birtles:additive-animation, r=hiro
Additive animation
Pull request for [Mozilla bug 1329878](https://bugzilla.mozilla.org/show_bug.cgi?id=1329878).
---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix [Mozilla bug 1329878](https://bugzilla.mozilla.org/show_bug.cgi?id=1329878).
- [X] There are tests for these changes in web-platform-tests
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16863)
<!-- Reviewable:end -->
-rw-r--r-- | components/style/properties/helpers.mako.rs | 18 | ||||
-rw-r--r-- | components/style/properties/helpers/animated_properties.mako.rs | 486 | ||||
-rw-r--r-- | components/style/properties/longhand/background.mako.rs | 9 | ||||
-rw-r--r-- | components/style/properties/longhand/box.mako.rs | 11 | ||||
-rw-r--r-- | components/style/properties/longhand/font.mako.rs | 6 | ||||
-rw-r--r-- | components/style/properties/longhand/inherited_table.mako.rs | 9 | ||||
-rw-r--r-- | components/style/values/mod.rs | 3 | ||||
-rw-r--r-- | ports/geckolib/glue.rs | 37 |
8 files changed, 361 insertions, 218 deletions
diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 6adce11d9c1..16236150cd9 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -113,8 +113,13 @@ % if delegate_animate: use properties::animated_properties::Animatable; impl Animatable for T { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - self.0.interpolate(&other.0, progress).map(T) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { + self.0.add_weighted(&other.0, self_portion, other_portion).map(T) + } + + fn add(&self, other: &Self) -> Result<Self, ()> { + self.0.add(&other.0).map(T) } #[inline] @@ -976,16 +981,17 @@ <%def name="impl_animatable_for_option_tuple(value_for_none)"> impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { match (self, other) { (&T(Some(ref this)), &T(Some(ref other))) => { - Ok(T(this.interpolate(other, progress).ok())) + Ok(T(this.add_weighted(other, self_portion, other_portion).ok())) }, (&T(Some(ref this)), &T(None)) => { - Ok(T(this.interpolate(&${value_for_none}, progress).ok())) + Ok(T(this.add_weighted(&${value_for_none}, self_portion, other_portion).ok())) }, (&T(None), &T(Some(ref other))) => { - Ok(T(${value_for_none}.interpolate(other, progress).ok())) + Ok(T(${value_for_none}.add_weighted(other, self_portion, other_portion).ok())) }, (&T(None), &T(None)) => { Ok(T(None)) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 9d75c63d7eb..e23b4877e56 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -581,27 +581,52 @@ impl AnimationValue { } impl Animatable for AnimationValue { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { match (self, other) { % for prop in data.longhands: % if prop.animatable: (&AnimationValue::${prop.camel_case}(ref from), &AnimationValue::${prop.camel_case}(ref to)) => { - // https://w3c.github.io/web-animations/#discrete-animation-type % if prop.animation_value_type == "discrete": - if progress < 0.5 { + if self_portion > other_portion { Ok(AnimationValue::${prop.camel_case}(*from)) } else { Ok(AnimationValue::${prop.camel_case}(*to)) } % else: - from.interpolate(to, progress).map(AnimationValue::${prop.camel_case}) + from.add_weighted(to, self_portion, other_portion) + .map(AnimationValue::${prop.camel_case}) % endif } % endif % endfor _ => { - panic!("Expected interpolation of computed values of the same \ + panic!("Expected weighted addition of computed values of the same \ + property, got: {:?}, {:?}", self, other); + } + } + } + + fn add(&self, other: &Self) -> Result<Self, ()> { + match (self, other) { + % for prop in data.longhands: + % if prop.animatable: + % if prop.animation_value_type == "discrete": + (&AnimationValue::${prop.camel_case}(_), + &AnimationValue::${prop.camel_case}(_)) => { + Err(()) + } + % else: + (&AnimationValue::${prop.camel_case}(ref from), + &AnimationValue::${prop.camel_case}(ref to)) => { + from.add(to).map(AnimationValue::${prop.camel_case}) + } + % endif + % endif + % endfor + _ => { + panic!("Expected weighted addition of computed values of the same \ property, got: {:?}, {:?}", self, other); } } @@ -635,10 +660,24 @@ impl Animatable for AnimationValue { /// A trait used to implement various procedures used during animation. pub trait Animatable: Sized { + /// Performs a weighted sum of this value and |other|. This is used for + /// interpolation and addition of animation values. + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()>; + /// [Interpolates][interpolation] a value with another for a given property. /// /// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()>; + fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + self.add_weighted(other, 1.0 - progress, progress) + } + + /// Returns the [sum][animation-addition] of this value and |other|. + /// + /// [animation-addition]: https://w3c.github.io/web-animations/#animation-addition + fn add(&self, other: &Self) -> Result<Self, ()> { + self.add_weighted(other, 1.0, 1.0) + } /// Compute distance between a value and another for a given property. fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { Err(()) } @@ -658,11 +697,12 @@ impl RepeatableListAnimatable for LengthOrPercentage {} impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {} impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { use num_integer::lcm; let len = lcm(self.len(), other.len()); self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| { - me.interpolate(you, progress) + me.add_weighted(you, self_portion, other_portion) }).collect() } @@ -684,8 +724,8 @@ impl<T: RepeatableListAnimatable> Animatable for SmallVec<[T; 1]> { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for Au { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - Ok(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * progress).round() as i32)) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32)) } #[inline] @@ -698,10 +738,10 @@ impl <T> Animatable for Option<T> where T: Animatable, { #[inline] - fn interpolate(&self, other: &Option<T>, progress: f64) -> Result<Option<T>, ()> { + fn add_weighted(&self, other: &Option<T>, self_portion: f64, other_portion: f64) -> Result<Option<T>, ()> { match (self, other) { (&Some(ref this), &Some(ref other)) => { - Ok(this.interpolate(other, progress).ok()) + Ok(this.add_weighted(other, self_portion, other_portion).ok()) } _ => Err(()), } @@ -731,8 +771,8 @@ impl <T> Animatable for Option<T> /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for f32 { #[inline] - fn interpolate(&self, other: &f32, progress: f64) -> Result<Self, ()> { - Ok(((*self as f64) + ((*other as f64) - (*self as f64)) * progress) as f32) + fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32) } #[inline] @@ -744,8 +784,8 @@ impl Animatable for f32 { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for f64 { #[inline] - fn interpolate(&self, other: &f64, progress: f64) -> Result<Self, ()> { - Ok(*self + (*other - *self) * progress) + fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + Ok(*self * self_portion + *other * other_portion) } #[inline] @@ -757,10 +797,8 @@ impl Animatable for f64 { /// https://drafts.csswg.org/css-transitions/#animtype-integer impl Animatable for i32 { #[inline] - fn interpolate(&self, other: &i32, progress: f64) -> Result<Self, ()> { - let a = *self as f64; - let b = *other as f64; - Ok((a + (b - a) * progress).round() as i32) + fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32) } #[inline] @@ -772,25 +810,24 @@ impl Animatable for i32 { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for Angle { #[inline] - fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> { - self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians) + fn add_weighted(&self, other: &Angle, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + self.radians() + .add_weighted(&other.radians(), self_portion, other_portion) + .map(Angle::from_radians) } } /// https://drafts.csswg.org/css-transitions/#animtype-visibility impl Animatable for Visibility { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { - (Visibility::visible, _) | (_, Visibility::visible) => { - Ok(if progress >= 0.0 && progress <= 1.0 { - Visibility::visible - } else if progress < 0.0 { - *self - } else { - *other - }) - } + (Visibility::visible, _) => { + Ok(if self_portion > 0.0 { *self } else { *other }) + }, + (_, Visibility::visible) => { + Ok(if other_portion > 0.0 { *other } else { *self }) + }, _ => Err(()), } } @@ -807,9 +844,9 @@ impl Animatable for Visibility { impl<T: Animatable + Copy> Animatable for Size2D<T> { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - let width = try!(self.width.interpolate(&other.width, progress)); - let height = try!(self.height.interpolate(&other.height, progress)); + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + let width = try!(self.width.add_weighted(&other.width, self_portion, other_portion)); + let height = try!(self.height.add_weighted(&other.height, self_portion, other_portion)); Ok(Size2D::new(width, height)) } @@ -817,9 +854,9 @@ impl<T: Animatable + Copy> Animatable for Size2D<T> { impl<T: Animatable + Copy> Animatable for Point2D<T> { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - let x = try!(self.x.interpolate(&other.x, progress)); - let y = try!(self.y.interpolate(&other.y, progress)); + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + let x = try!(self.x.add_weighted(&other.x, self_portion, other_portion)); + let y = try!(self.y.add_weighted(&other.y, self_portion, other_portion)); Ok(Point2D::new(x, y)) } @@ -827,8 +864,8 @@ impl<T: Animatable + Copy> Animatable for Point2D<T> { impl Animatable for BorderRadiusSize { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - self.0.interpolate(&other.0, progress).map(generics::BorderRadiusSize) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + self.0.add_weighted(&other.0, self_portion, other_portion).map(generics::BorderRadiusSize) } #[inline] @@ -846,11 +883,11 @@ impl Animatable for BorderRadiusSize { /// https://drafts.csswg.org/css-transitions/#animtype-length impl Animatable for VerticalAlign { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)), VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => { - this.interpolate(other, progress).map(|value| { + this.add_weighted(other, self_portion, other_portion).map(|value| { VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)) }) } @@ -872,8 +909,8 @@ impl Animatable for VerticalAlign { impl Animatable for BackgroundSizeList { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - self.0.interpolate(&other.0, progress).map(BackgroundSizeList) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + self.0.add_weighted(&other.0, self_portion, other_portion).map(BackgroundSizeList) } #[inline] @@ -890,24 +927,28 @@ impl Animatable for BackgroundSizeList { /// https://drafts.csswg.org/css-transitions/#animtype-color impl Animatable for RGBA { #[inline] - fn interpolate(&self, other: &RGBA, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &RGBA, self_portion: f64, other_portion: f64) -> Result<Self, ()> { fn clamp(val: f32) -> f32 { val.max(0.).min(1.) } - let alpha = clamp(try!(self.alpha_f32().interpolate(&other.alpha_f32(), progress))); + let alpha = clamp(try!(self.alpha_f32().add_weighted(&other.alpha_f32(), + self_portion, other_portion))); if alpha == 0. { Ok(RGBA::transparent()) } else { // NB: We rely on RGBA::from_floats clamping already. let red = try!((self.red_f32() * self.alpha_f32()) - .interpolate(&(other.red_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.red_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; let green = try!((self.green_f32() * self.alpha_f32()) - .interpolate(&(other.green_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.green_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; let blue = try!((self.blue_f32() * self.alpha_f32()) - .interpolate(&(other.blue_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.blue_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; Ok(RGBA::from_floats(red, green, blue, alpha)) } @@ -948,10 +989,10 @@ impl Animatable for RGBA { /// https://drafts.csswg.org/css-transitions/#animtype-color impl Animatable for CSSParserColor { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => { - this.interpolate(other, progress).map(CSSParserColor::RGBA) + this.add_weighted(other, self_portion, other_portion).map(CSSParserColor::RGBA) } _ => Err(()), } @@ -976,11 +1017,12 @@ impl Animatable for CSSParserColor { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for CalcLengthOrPercentage { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - fn interpolate_half<T>(this: Option<T>, - other: Option<T>, - progress: f64) - -> Result<Option<T>, ()> + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + fn add_weighted_half<T>(this: Option<T>, + other: Option<T>, + self_portion: f64, + other_portion: f64) + -> Result<Option<T>, ()> where T: Default + Animatable, { match (this, other) { @@ -988,14 +1030,15 @@ impl Animatable for CalcLengthOrPercentage { (this, other) => { let this = this.unwrap_or(T::default()); let other = other.unwrap_or(T::default()); - this.interpolate(&other, progress).map(Some) + this.add_weighted(&other, self_portion, other_portion).map(Some) } } } Ok(CalcLengthOrPercentage { - length: try!(self.length.interpolate(&other.length, progress)), - percentage: try!(interpolate_half(self.percentage, other.percentage, progress)), + length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)), + percentage: try!(add_weighted_half(self.percentage, other.percentage, + self_portion, other_portion)), }) } @@ -1015,20 +1058,22 @@ impl Animatable for CalcLengthOrPercentage { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentage { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (LengthOrPercentage::Length(ref this), LengthOrPercentage::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentage::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentage::Length) } (LengthOrPercentage::Percentage(ref this), LengthOrPercentage::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentage::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentage::Percentage) } (this, other) => { let this: CalcLengthOrPercentage = From::from(this); let other: CalcLengthOrPercentage = From::from(other); - this.interpolate(&other, progress) + this.add_weighted(&other, self_portion, other_portion) .map(LengthOrPercentage::Calc) } } @@ -1080,15 +1125,17 @@ impl Animatable for LengthOrPercentage { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentageOrAuto { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (LengthOrPercentageOrAuto::Length(ref this), LengthOrPercentageOrAuto::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrAuto::Length) } (LengthOrPercentageOrAuto::Percentage(ref this), LengthOrPercentageOrAuto::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrAuto::Percentage) } (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => { Ok(LengthOrPercentageOrAuto::Auto) @@ -1096,7 +1143,7 @@ impl Animatable for LengthOrPercentageOrAuto { (this, other) => { let this: Option<CalcLengthOrPercentage> = From::from(this); let other: Option<CalcLengthOrPercentage> = From::from(other); - match this.interpolate(&other, progress) { + match this.add_weighted(&other, self_portion, other_portion) { Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)), _ => Err(()), } @@ -1155,15 +1202,17 @@ impl Animatable for LengthOrPercentageOrAuto { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentageOrNone { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (LengthOrPercentageOrNone::Length(ref this), LengthOrPercentageOrNone::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrNone::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrNone::Length) } (LengthOrPercentageOrNone::Percentage(ref this), LengthOrPercentageOrNone::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrNone::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrNone::Percentage) } (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => { Ok(LengthOrPercentageOrNone::None) @@ -1191,11 +1240,12 @@ impl Animatable for LengthOrPercentageOrNone { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for MinLength { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (MinLength::LengthOrPercentage(ref this), MinLength::LengthOrPercentage(ref other)) => { - this.interpolate(other, progress).map(MinLength::LengthOrPercentage) + this.add_weighted(other, self_portion, other_portion) + .map(MinLength::LengthOrPercentage) } _ => Err(()), } @@ -1216,11 +1266,12 @@ impl Animatable for MinLength { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for MaxLength { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (MaxLength::LengthOrPercentage(ref this), MaxLength::LengthOrPercentage(ref other)) => { - this.interpolate(other, progress).map(MaxLength::LengthOrPercentage) + this.add_weighted(other, self_portion, other_portion) + .map(MaxLength::LengthOrPercentage) } _ => Err(()), } @@ -1242,15 +1293,15 @@ impl Animatable for MaxLength { /// https://drafts.csswg.org/css-transitions/#animtype-length impl Animatable for LineHeight { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (LineHeight::Length(ref this), LineHeight::Length(ref other)) => { - this.interpolate(other, progress).map(LineHeight::Length) + this.add_weighted(other, self_portion, other_portion).map(LineHeight::Length) } (LineHeight::Number(ref this), LineHeight::Number(ref other)) => { - this.interpolate(other, progress).map(LineHeight::Number) + this.add_weighted(other, self_portion, other_portion).map(LineHeight::Number) } (LineHeight::Normal, LineHeight::Normal) => { Ok(LineHeight::Normal) @@ -1278,10 +1329,10 @@ impl Animatable for LineHeight { /// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight impl Animatable for FontWeight { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { let a = (*self as u32) as f64; let b = (*other as u32) as f64; - let weight = a + (b - a) * progress; + let weight = a * self_portion + b * other_portion; Ok(if weight < 150. { FontWeight::Weight100 } else if weight < 250. { @@ -1314,10 +1365,12 @@ impl Animatable for FontWeight { /// https://drafts.csswg.org/css-transitions/#animtype-simple-list impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(generic_position::Position { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, progress)), - vertical: try!(self.vertical.interpolate(&other.vertical, progress)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), }) } @@ -1339,12 +1392,13 @@ impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V> /// https://drafts.csswg.org/css-transitions/#animtype-rect impl Animatable for ClipRect { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { Ok(ClipRect { - top: try!(self.top.interpolate(&other.top, time)), - right: try!(self.right.interpolate(&other.right, time)), - bottom: try!(self.bottom.interpolate(&other.bottom, time)), - left: try!(self.left.interpolate(&other.left, time)), + top: try!(self.top.add_weighted(&other.top, self_portion, other_portion)), + right: try!(self.right.add_weighted(&other.right, self_portion, other_portion)), + bottom: try!(self.bottom.add_weighted(&other.bottom, self_portion, other_portion)), + left: try!(self.left.add_weighted(&other.left, self_portion, other_portion)), }) } @@ -1366,7 +1420,7 @@ impl Animatable for ClipRect { <%def name="impl_animatable_for_shadow(item, transparent_color)"> impl Animatable for ${item} { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { % if "Box" in item: // It can't be interpolated if inset does not match. if self.inset != other.inset { @@ -1374,12 +1428,14 @@ impl Animatable for ClipRect { } % endif - let x = try!(self.offset_x.interpolate(&other.offset_x, progress)); - let y = try!(self.offset_y.interpolate(&other.offset_y, progress)); - let color = try!(self.color.interpolate(&other.color, progress)); - let blur = try!(self.blur_radius.interpolate(&other.blur_radius, progress)); + let x = try!(self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion)); + let y = try!(self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion)); + let color = try!(self.color.add_weighted(&other.color, self_portion, other_portion)); + let blur = try!(self.blur_radius.add_weighted(&other.blur_radius, + self_portion, other_portion)); % if "Box" in item: - let spread = try!(self.spread_radius.interpolate(&other.spread_radius, progress)); + let spread = try!(self.spread_radius.add_weighted(&other.spread_radius, + self_portion, other_portion)); % endif Ok(${item} { @@ -1421,7 +1477,7 @@ impl Animatable for ClipRect { /// https://drafts.csswg.org/css-transitions/#animtype-shadow-list impl Animatable for ${item}List { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { // The inset value must change % if "Box" in item: let mut zero = ${item} { @@ -1449,18 +1505,18 @@ impl Animatable for ClipRect { for i in 0..max_len { let shadow = match (self.0.get(i), other.0.get(i)) { (Some(shadow), Some(other)) - => try!(shadow.interpolate(other, progress)), + => try!(shadow.add_weighted(other, self_portion, other_portion)), (Some(shadow), None) => { % if "Box" in item: zero.inset = shadow.inset; % endif - shadow.interpolate(&zero, progress).unwrap() + shadow.add_weighted(&zero, self_portion, other_portion).unwrap() } (None, Some(shadow)) => { % if "Box" in item: zero.inset = shadow.inset; % endif - zero.interpolate(&shadow, progress).unwrap() + zero.add_weighted(&shadow, self_portion, other_portion).unwrap() } (None, None) => unreachable!(), }; @@ -1469,6 +1525,21 @@ impl Animatable for ClipRect { Ok(${item}List(result)) } + + fn add(&self, other: &Self) -> Result<Self, ()> { + let len = self.0.len() + other.0.len(); + + let mut result = if len > 1 { + SmallVec::from_vec(Vec::with_capacity(len)) + } else { + SmallVec::new() + }; + + result.extend(self.0.iter().cloned()); + result.extend(other.0.iter().cloned()); + + Ok(${item}List(result)) + } } </%def> @@ -1541,11 +1612,12 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp result } -/// Interpolate two transform lists. +/// Add two transform lists. /// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms -fn interpolate_transform_list(from_list: &[TransformOperation], - to_list: &[TransformOperation], - progress: f64) -> TransformList { +fn add_weighted_transform_lists(from_list: &[TransformOperation], + to_list: &[TransformOperation], + self_portion: f64, + other_portion: f64) -> TransformList { let mut result = vec![]; if can_interpolate_list(from_list, to_list) { @@ -1553,33 +1625,33 @@ fn interpolate_transform_list(from_list: &[TransformOperation], match (from, to) { (&TransformOperation::Matrix(from), &TransformOperation::Matrix(_to)) => { - let interpolated = from.interpolate(&_to, progress).unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap(); + result.push(TransformOperation::Matrix(sum)); } (&TransformOperation::MatrixWithPercents(_), &TransformOperation::MatrixWithPercents(_)) => { - // We don't interpolate `-moz-transform` matrices yet. + // We don't add_weighted `-moz-transform` matrices yet. // They contain percentage values. {} } (&TransformOperation::Skew(fx, fy), &TransformOperation::Skew(tx, ty)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); result.push(TransformOperation::Skew(ix, iy)); } (&TransformOperation::Translate(fx, fy, fz), &TransformOperation::Translate(tx, ty, tz)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); - let iz = fz.interpolate(&tz, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); + let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap(); result.push(TransformOperation::Translate(ix, iy, iz)); } (&TransformOperation::Scale(fx, fy, fz), &TransformOperation::Scale(tx, ty, tz)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); - let iz = fz.interpolate(&tz, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); + let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap(); result.push(TransformOperation::Scale(ix, iy, iz)); } (&TransformOperation::Rotate(fx, fy, fz, fa), @@ -1589,14 +1661,15 @@ fn interpolate_transform_list(from_list: &[TransformOperation], let (fx, fy, fz) = (fx / norm_f, fy / norm_f, fz / norm_f); let (tx, ty, tz) = (tx / norm_t, ty / norm_t, tz / norm_t); if fx == tx && fy == ty && fz == tz { - let ia = fa.interpolate(&ta, progress).unwrap(); + let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap(); result.push(TransformOperation::Rotate(fx, fy, fz, ia)); } else { let matrix_f = rotate_to_matrix(fx, fy, fz, fa); let matrix_t = rotate_to_matrix(tx, ty, tz, ta); - let interpolated = matrix_f.interpolate(&matrix_t, progress).unwrap(); + let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion) + .unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + result.push(TransformOperation::Matrix(sum)); } } (&TransformOperation::Perspective(fd), @@ -1605,8 +1678,9 @@ fn interpolate_transform_list(from_list: &[TransformOperation], let mut td_matrix = ComputedMatrix::identity(); fd_matrix.m43 = -1. / fd.to_f32_px(); td_matrix.m43 = -1. / _td.to_f32_px(); - let interpolated = fd_matrix.interpolate(&td_matrix, progress).unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion) + .unwrap(); + result.push(TransformOperation::Matrix(sum)); } _ => { // This should be unreachable due to the can_interpolate_list() call. @@ -1685,37 +1759,37 @@ pub struct MatrixDecomposed2D { } impl Animatable for InnerMatrix2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(InnerMatrix2D { - m11: try!(self.m11.interpolate(&other.m11, progress)), - m12: try!(self.m12.interpolate(&other.m12, progress)), - m21: try!(self.m21.interpolate(&other.m21, progress)), - m22: try!(self.m22.interpolate(&other.m22, progress)), + m11: try!(self.m11.add_weighted(&other.m11, self_portion, other_portion)), + m12: try!(self.m12.add_weighted(&other.m12, self_portion, other_portion)), + m21: try!(self.m21.add_weighted(&other.m21, self_portion, other_portion)), + m22: try!(self.m22.add_weighted(&other.m22, self_portion, other_portion)), }) } } impl Animatable for Translate2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Translate2D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)) )) } } impl Animatable for Scale2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Scale2D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)) )) } } impl Animatable for MatrixDecomposed2D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { // If x-axis of one is flipped, and y-axis of the other, // convert to an unflipped rotation. let mut scale = self.scale; @@ -1745,10 +1819,11 @@ impl Animatable for MatrixDecomposed2D { } // Interpolate all values. - let translate = try!(self.translate.interpolate(&other.translate, progress)); - let scale = try!(scale.interpolate(&other.scale, progress)); - let angle = try!(angle.interpolate(&other_angle, progress)); - let matrix = try!(self.matrix.interpolate(&other.matrix, progress)); + let translate = try!(self.translate.add_weighted(&other.translate, + self_portion, other_portion)); + let scale = try!(scale.add_weighted(&other.scale, self_portion, other_portion)); + let angle = try!(angle.add_weighted(&other_angle, self_portion, other_portion)); + let matrix = try!(self.matrix.add_weighted(&other.matrix, self_portion, other_portion)); Ok(MatrixDecomposed2D { translate: translate, @@ -1760,25 +1835,26 @@ impl Animatable for MatrixDecomposed2D { } impl Animatable for ComputedMatrix { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { if self.is_3d() || other.is_3d() { let decomposed_from = decompose_3d_matrix(*self); let decomposed_to = decompose_3d_matrix(*other); match (decomposed_from, decomposed_to) { (Ok(from), Ok(to)) => { - let interpolated = try!(from.interpolate(&to, progress)); - Ok(ComputedMatrix::from(interpolated)) + let sum = try!(from.add_weighted(&to, self_portion, other_portion)); + Ok(ComputedMatrix::from(sum)) }, _ => { - let interpolated = if progress < 0.5 {*self} else {*other}; - Ok(interpolated) + let result = if self_portion > other_portion {*self} else {*other}; + Ok(result) } } } else { let decomposed_from = MatrixDecomposed2D::from(*self); let decomposed_to = MatrixDecomposed2D::from(*other); - let interpolated = try!(decomposed_from.interpolate(&decomposed_to, progress)); - Ok(ComputedMatrix::from(interpolated)) + let sum = try!(decomposed_from.add_weighted(&decomposed_to, + self_portion, other_portion)); + Ok(ComputedMatrix::from(sum)) } } } @@ -2094,58 +2170,64 @@ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] { } impl Animatable for Translate3D { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Translate3D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Scale3D { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Scale3D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Skew { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Skew( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Perspective { - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Perspective( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)), - try!(self.3.interpolate(&other.3, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)), + try!(self.3.add_weighted(&other.3, self_portion, other_portion)) )) } } impl Animatable for MatrixDecomposed3D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { - let mut interpolated = *self; - - // Interpolate translate, scale, skew and perspective components. - interpolated.translate = try!(self.translate.interpolate(&other.translate, progress)); - interpolated.scale = try!(self.scale.interpolate(&other.scale, progress)); - interpolated.skew = try!(self.skew.interpolate(&other.skew, progress)); - interpolated.perspective = try!(self.perspective.interpolate(&other.perspective, progress)); - - // Interpolate quaternions using spherical linear interpolation (Slerp). + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { + assert!(self_portion + other_portion == 1.0f64, + "add_weighted should only be used for interpolating transforms"); + + let mut sum = *self; + + // Add translate, scale, skew and perspective components. + sum.translate = try!(self.translate.add_weighted(&other.translate, + self_portion, other_portion)); + sum.scale = try!(self.scale.add_weighted(&other.scale, self_portion, other_portion)); + sum.skew = try!(self.skew.add_weighted(&other.skew, self_portion, other_portion)); + sum.perspective = try!(self.perspective.add_weighted(&other.perspective, + self_portion, other_portion)); + + // Add quaternions using spherical linear interpolation (Slerp). let mut product = self.quaternion.0 * other.quaternion.0 + self.quaternion.1 * other.quaternion.1 + self.quaternion.2 * other.quaternion.2 + @@ -2156,21 +2238,21 @@ impl Animatable for MatrixDecomposed3D { product = product.max(-1.0); if product == 1.0 { - return Ok(interpolated); + return Ok(sum); } let theta = product.acos(); - let w = (progress as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt(); + let w = (other_portion as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt(); let mut a = *self; let mut b = *other; % for i in range(4): - a.quaternion.${i} *= (progress as f32 * theta).cos() - product * w; + a.quaternion.${i} *= (other_portion as f32 * theta).cos() - product * w; b.quaternion.${i} *= w; - interpolated.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i}; + sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i}; % endfor - Ok(interpolated) + Ok(sum) } } @@ -2374,22 +2456,22 @@ impl ComputedMatrix { /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms impl Animatable for TransformList { #[inline] - fn interpolate(&self, other: &TransformList, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> { // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms let result = match (&self.0, &other.0) { (&Some(ref from_list), &Some(ref to_list)) => { // Two lists of transforms - interpolate_transform_list(from_list, &to_list, progress) + add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion) } (&Some(ref from_list), &None) => { // http://dev.w3.org/csswg/css-transforms/#none-transform-animation let to_list = build_identity_transform_list(from_list); - interpolate_transform_list(from_list, &to_list, progress) + add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion) } (&None, &Some(ref to_list)) => { // http://dev.w3.org/csswg/css-transforms/#none-transform-animation let from_list = build_identity_transform_list(to_list); - interpolate_transform_list(&from_list, to_list, progress) + add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion) } _ => { // http://dev.w3.org/csswg/css-transforms/#none-none-animation @@ -2399,23 +2481,40 @@ impl Animatable for TransformList { Ok(result) } + + fn add(&self, other: &Self) -> Result<Self, ()> { + match (&self.0, &other.0) { + (&Some(ref from_list), &Some(ref to_list)) => { + Ok(TransformList(Some([&from_list[..], &to_list[..]].concat()))) + } + (&Some(_), &None) => { + Ok(self.clone()) + } + (&None, &Some(_)) => { + Ok(other.clone()) + } + _ => { + Ok(TransformList(None)) + } + } + } } impl<T, U> Animatable for Either<T, U> where T: Animatable + Copy, U: Animatable + Copy, { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { (Either::First(ref this), Either::First(ref other)) => { - this.interpolate(&other, progress).map(Either::First) + this.add_weighted(&other, self_portion, other_portion).map(Either::First) }, (Either::Second(ref this), Either::Second(ref other)) => { - this.interpolate(&other, progress).map(Either::Second) + this.add_weighted(&other, self_portion, other_portion).map(Either::Second) }, _ => { - let interpolated = if progress < 0.5 { *self } else { *other }; - Ok(interpolated) + let result = if self_portion > other_portion {*self} else {*other}; + Ok(result) } } } @@ -2497,21 +2596,26 @@ impl IntermediateRGBA { /// Unlike Animatable for RGBA we don't clamp any component values. impl Animatable for IntermediateRGBA { #[inline] - fn interpolate(&self, other: &IntermediateRGBA, progress: f64) -> Result<Self, ()> { - let alpha = try!(self.alpha.interpolate(&other.alpha, progress)); - if alpha == 0. { + fn add_weighted(&self, other: &IntermediateRGBA, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { + let mut alpha = try!(self.alpha.add_weighted(&other.alpha, self_portion, other_portion)); + if alpha <= 0. { // Ideally we should return color value that only alpha component is // 0, but this is what current gecko does. Ok(IntermediateRGBA::transparent()) } else { + alpha = alpha.min(1.); let red = try!((self.red * self.alpha) - .interpolate(&(other.red * other.alpha), progress)) + .add_weighted(&(other.red * other.alpha), + self_portion, other_portion)) * 1. / alpha; let green = try!((self.green * self.alpha) - .interpolate(&(other.green * other.alpha), progress)) + .add_weighted(&(other.green * other.alpha), + self_portion, other_portion)) * 1. / alpha; let blue = try!((self.blue * self.alpha) - .interpolate(&(other.blue * other.alpha), progress)) + .add_weighted(&(other.blue * other.alpha), + self_portion, other_portion)) * 1. / alpha; Ok(IntermediateRGBA::new(red, green, blue, alpha)) } @@ -2588,10 +2692,12 @@ pub enum IntermediateColor { impl Animatable for IntermediateColor { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { match (*self, *other) { - (IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => { - this.interpolate(other, progress).map(IntermediateColor::IntermediateRGBA) + (IntermediateColor::IntermediateRGBA(ref this), + IntermediateColor::IntermediateRGBA(ref other)) => { + this.add_weighted(other, self_portion, other_portion) + .map(IntermediateColor::IntermediateRGBA) } // FIXME: Bug 1345709: Implement currentColor animations. _ => Err(()), diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index b0c998d59fd..3b50cbae01a 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -190,13 +190,16 @@ ${helpers.single_keyword("background-origin", impl RepeatableListAnimatable for T {} impl Animatable for T { - fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { use properties::longhands::background_size::single_value::computed_value::ExplicitSize; match (self, other) { (&T::Explicit(ref me), &T::Explicit(ref other)) => { Ok(T::Explicit(ExplicitSize { - width: try!(me.width.interpolate(&other.width, time)), - height: try!(me.height.interpolate(&other.height, time)), + width: try!(me.width.add_weighted(&other.width, + self_portion, other_portion)), + height: try!(me.height.add_weighted(&other.height, + self_portion, other_portion)), })) } _ => Err(()), diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index ac0ac739a5e..eeb2100b11e 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -2198,11 +2198,14 @@ ${helpers.single_keyword("transform-style", impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { Ok(T { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)), - vertical: try!(self.vertical.interpolate(&other.vertical, time)), - depth: try!(self.depth.interpolate(&other.depth, time)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), + depth: try!(self.depth.add_weighted(&other.depth, self_portion, other_portion)), }) } diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 2b5c89b958f..44e49ac1fb7 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1073,10 +1073,12 @@ ${helpers.single_keyword_system("font-variant-caps", } impl Animatable for T { - fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { match (*self, *other) { (T::Number(ref number), T::Number(ref other)) => - Ok(T::Number(try!(number.interpolate(other, time)))), + Ok(T::Number(try!(number.add_weighted(other, + self_portion, other_portion)))), _ => Err(()), } } diff --git a/components/style/properties/longhand/inherited_table.mako.rs b/components/style/properties/longhand/inherited_table.mako.rs index aba56b125a2..b6c1ad3d604 100644 --- a/components/style/properties/longhand/inherited_table.mako.rs +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -42,10 +42,13 @@ ${helpers.single_keyword("caption-side", "top bottom", /// https://drafts.csswg.org/css-transitions/#animtype-simple-list impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result<Self, ()> { Ok(T { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)), - vertical: try!(self.vertical.interpolate(&other.vertical, time)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), }) } diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index b21ab50e93b..4ce3d1f8dc1 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -129,7 +129,8 @@ macro_rules! define_keyword_type { impl Animatable for $name { #[inline] - fn interpolate(&self, _other: &Self, _progress: f64) -> Result<Self, ()> { + fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64) + -> Result<Self, ()> { Ok($name) } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 7978c367d6f..f3c6b680f41 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -62,6 +62,7 @@ use style::gecko_bindings::structs; use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet}; use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, nsCSSFontFaceRule}; +use style::gecko_bindings::structs::CompositeOperation; use style::gecko_bindings::structs::Loader; use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::ServoElementSnapshotTable; @@ -325,12 +326,16 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa let property: TransitionProperty = css_property.into(); let value_map = AnimationValueMap::from_ffi_mut(raw_value_map); + let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() || + segment.mToValue.mServo.mRawPtr.is_null() || + segment.mFromComposite != CompositeOperation::Replace || + segment.mToComposite != CompositeOperation::Replace; + // If either of the segment endpoints are null, get the underlying value to // use from the current value in the values map (set by a lower-priority // effect), or, if there is no current value, look up the cached base value // for this property. - let underlying_value = if segment.mFromValue.mServo.mRawPtr.is_null() || - segment.mToValue.mServo.mRawPtr.is_null() { + let underlying_value = if need_underlying_value { let previous_composed_value = value_map.get(&property).cloned(); previous_composed_value.or_else(|| { let raw_base_style = unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property) }; @@ -340,26 +345,40 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa None }; - if (segment.mFromValue.mServo.mRawPtr.is_null() || - segment.mToValue.mServo.mRawPtr.is_null()) && - underlying_value.is_none() { - warn!("Underlying value should be valid in the case where either 'from' value or 'to' value is null"); + if need_underlying_value && underlying_value.is_none() { + warn!("Underlying value should be valid when we expect to use it"); return; } - // Declare for making derefenced raw pointer alive outside the if block. + // Temporaries used in the following if-block whose lifetimes we need to prlong. let raw_from_value; + let from_composite_result; let from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() { raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr }; - AnimationValue::as_arc(&raw_from_value).as_ref() + match segment.mFromComposite { + CompositeOperation::Add => { + let value_to_composite = AnimationValue::as_arc(&raw_from_value).as_ref(); + from_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite); + from_composite_result.as_ref().unwrap_or(value_to_composite) + } + _ => { AnimationValue::as_arc(&raw_from_value) } + } } else { underlying_value.as_ref().unwrap() }; let raw_to_value; + let to_composite_result; let to_value = if !segment.mToValue.mServo.mRawPtr.is_null() { raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr }; - AnimationValue::as_arc(&raw_to_value).as_ref() + match segment.mToComposite { + CompositeOperation::Add => { + let value_to_composite = AnimationValue::as_arc(&raw_to_value).as_ref(); + to_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite); + to_composite_result.as_ref().unwrap_or(value_to_composite) + } + _ => { AnimationValue::as_arc(&raw_to_value) } + } } else { underlying_value.as_ref().unwrap() }; |