Bug 1329878 - Rewrite interpolate() in terms of a more general add_weighted() function draft
authorBrian Birtles <birtles@gmail.com>
Mon, 15 May 2017 09:02:52 +0900
changeset 577583 507b216ffd116becf97c38751bc242b6b9640899
parent 577554 e66dedabe582ba7b394aee4f89ed70fe389b3c46
child 577584 1bd8e35533e41129f10b214f70929d2ab16f7468
push id58723
push userbbirtles@mozilla.com
push dateMon, 15 May 2017 02:05:49 +0000
bugs1329878
milestone55.0a1
Bug 1329878 - Rewrite interpolate() in terms of a more general add_weighted() function Generalizing the procedure like this will allow us to re-use it for addition of most types. MozReview-Commit-ID: Gp9SCcmbhOa
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/values/mod.rs
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -108,18 +108,19 @@
                 /// The computed value, effectively a list of single values.
                 #[derive(Debug, Clone, PartialEq)]
                 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
                 pub struct T(pub SmallVec<[single_value::T; 1]>);
 
                 % 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)
                         }
 
                         #[inline]
                         fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
                             self.0.compute_distance(&other.0)
                         }
 
                         #[inline]
@@ -971,26 +972,27 @@
     %>
 </%def>
 
 /// Macro for defining Animatable trait for tuple struct which has Option<T>,
 /// e.g. struct T(pub Option<Au>).
 <%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))
                 },
             }
         }
 
         #[inline]
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -576,37 +576,38 @@ impl AnimationValue {
                 % endif
             % endfor
             ref other => panic!("Can't use TransitionProperty::{:?} here.", other),
         }
     }
 }
 
 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 compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (self, other) {
             % for prop in data.longhands:
@@ -630,20 +631,27 @@ 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)
+    }
 
     /// Compute distance between a value and another for a given property.
     fn compute_distance(&self, _other: &Self) -> Result<f64, ()>  { Err(()) }
 
     /// In order to compute the Euclidean distance of a list or property value with multiple
     /// components, we need to compute squared distance for each element, so the vector can sum it
     /// and then get its squared root as the distance.
     fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
@@ -653,21 +661,22 @@ pub trait Animatable: Sized {
 
 /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
 pub trait RepeatableListAnimatable: Animatable {}
 
 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()
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sd| sd.sqrt())
     }
 
@@ -679,34 +688,34 @@ impl<T: RepeatableListAnimatable> Animat
             me.compute_squared_distance(you)
         }).collect::<Result<Vec<_>, _>>().map(|d| d.iter().sum())
     }
 }
 
 /// 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.0.compute_distance(&other.0)
     }
 }
 
 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(()),
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (self, other) {
@@ -726,71 +735,72 @@ impl <T> Animatable for Option<T>
             _ => Err(()),
         }
     }
 }
 
 /// 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         Ok((*self - *other).abs() as f64)
     }
 }
 
 /// 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         Ok((*self - *other).abs())
     }
 }
 
 /// 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         Ok((*self - *other).abs() as f64)
     }
 }
 
 /// 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 {
+                Ok(if self_portion >= 0.0 && self_portion <= 1.0 &&
+                      other_portion >= 0.0 && other_portion <= 1.0 {
                     Visibility::visible
-                } else if progress < 0.0 {
+                } else if self_portion > other_portion {
                     *self
                 } else {
                     *other
                 })
             }
             _ => Err(()),
         }
     }
@@ -802,38 +812,38 @@ impl Animatable for Visibility {
         } else {
             Ok(1.0)
         }
     }
 }
 
 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))
     }
 }
 
 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))
     }
 }
 
 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sd| sd.sqrt())
     }
 
     #[inline]
@@ -841,21 +851,21 @@ impl Animatable for BorderRadiusSize {
         Ok(try!(self.0.width.compute_squared_distance(&other.0.width)) +
            try!(self.0.height.compute_squared_distance(&other.0.height)))
     }
 }
 
 /// 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))
                 })
             }
             _ => Err(()),
         }
     }
 
     #[inline]
@@ -867,52 +877,56 @@ impl Animatable for VerticalAlign {
             },
             _ => Err(()),
         }
     }
 }
 
 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]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.0.compute_distance(&other.0)
     }
 
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
         self.0.compute_squared_distance(&other.0)
     }
 }
 
 /// 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))
         }
     }
 
     /// https://www.w3.org/TR/smil-animation/#animateColorElement says we should use Euclidean
     /// RGB-cube distance.
     #[inline]
@@ -943,20 +957,20 @@ impl Animatable for RGBA {
                                });
         Ok(diff)
     }
 }
 
 /// 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(()),
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sq| sq.sqrt())
@@ -971,36 +985,38 @@ impl Animatable for CSSParserColor {
             _ => Ok(0.0),
         }
     }
 }
 
 /// 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) {
                 (None, None) => Ok(None),
                 (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)),
         })
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sq| sq.sqrt())
     }
 
@@ -1010,30 +1026,32 @@ impl Animatable for CalcLengthOrPercenta
         let percentage_diff = (self.percentage() - other.percentage()) as f64;
         Ok(length_diff * length_diff + percentage_diff * percentage_diff)
     }
 }
 
 /// 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)
             }
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (*self, *other) {
@@ -1075,33 +1093,35 @@ 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)
             }
             (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(()),
                 }
             }
         }
     }
 
     #[inline]
@@ -1150,25 +1170,27 @@ impl Animatable for LengthOrPercentageOr
             }
         }
     }
 }
 
 /// 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)
             }
             _ => Err(())
         }
     }
 
@@ -1186,21 +1208,22 @@ impl Animatable for LengthOrPercentageOr
             _ => Err(())
         }
     }
 }
 
 /// 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(()),
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (*self, *other) {
@@ -1211,21 +1234,22 @@ impl Animatable for MinLength {
             _ => Err(()),
         }
     }
 }
 
 /// 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(()),
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (*self, *other) {
@@ -1237,25 +1261,25 @@ impl Animatable for MaxLength {
         }
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-number
 /// 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)
             }
             _ => Err(()),
         }
     }
 
@@ -1273,20 +1297,20 @@ impl Animatable for LineHeight {
             _ => Err(()),
         }
     }
 }
 
 /// 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. {
             FontWeight::Weight200
         } else if weight < 350. {
             FontWeight::Weight300
         } else if weight < 450. {
             FontWeight::Weight400
@@ -1309,20 +1333,22 @@ impl Animatable for FontWeight {
         let b = (*other as u32) as f64;
         a.compute_distance(&b)
     }
 }
 
 /// 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)),
         })
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sd| sd.sqrt())
     }
 
@@ -1334,22 +1360,23 @@ impl<H: Animatable, V: Animatable> Anima
 }
 
 impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
     where H: RepeatableListAnimatable, V: RepeatableListAnimatable {}
 
 /// 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)),
         })
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sd| sd.sqrt())
     }
 
@@ -1361,30 +1388,32 @@ impl Animatable for ClipRect {
                      try!(self.left.compute_distance(&other.left)) ];
         Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
     }
 }
 
 <%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 {
                 return Err(());
             }
             % 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} {
                 offset_x: x,
                 offset_y: y,
                 blur_radius: blur,
                 color: color,
                 % if "Box" in item:
@@ -1416,17 +1445,17 @@ impl Animatable for ClipRect {
                        ];
             Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
         }
     }
 
     /// 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} {
             % else:
             let zero = ${item} {
             % endif
                 offset_x: Au(0),
                 offset_y: Au(0),
@@ -1444,28 +1473,28 @@ impl Animatable for ClipRect {
                 SmallVec::from_vec(Vec::with_capacity(max_len))
             } else {
                 SmallVec::new()
             };
 
             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!(),
                 };
                 result.push(shadow);
             }
 
             Ok(${item}List(result))
         }
@@ -1536,82 +1565,85 @@ fn build_identity_transform_list(list: &
                 result.push(TransformOperation::Matrix(identity));
             }
         }
     }
 
     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) {
         for (from, to) in from_list.iter().zip(to_list) {
             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),
                  &TransformOperation::Rotate(tx, ty, tz, ta)) => {
                     let norm_f = ((fx * fx) + (fy * fy) + (fz * fz)).sqrt();
                     let norm_t = ((tx * tx) + (ty * ty) + (tz * tz)).sqrt();
                     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),
                  &TransformOperation::Perspective(_td)) => {
                     let mut fd_matrix = ComputedMatrix::identity();
                     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.
                     unreachable!();
                 }
             }
         }
     } else {
@@ -1680,47 +1712,47 @@ pub struct MatrixDecomposed2D {
     pub scale: Scale2D,
     /// The rotation angle.
     pub angle: f32,
     /// The inner matrix.
     pub matrix: InnerMatrix2D,
 }
 
 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;
         let mut angle = self.angle;
         let mut other_angle = other.angle;
         if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
             scale.0 = -scale.0;
             scale.1 = -scale.1;
@@ -1740,50 +1772,52 @@ impl Animatable for MatrixDecomposed2D {
                 angle -= 360.
             }
             else{
                 other_angle -= 360.
             }
         }
 
         // 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,
             scale: scale,
             angle: angle,
             matrix: matrix,
         })
     }
 }
 
 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))
         }
     }
 }
 
 impl From<ComputedMatrix> for MatrixDecomposed2D {
     /// Decompose a 2D matrix.
     /// https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix
     fn from(matrix: ComputedMatrix) -> MatrixDecomposed2D {
@@ -2089,93 +2123,99 @@ fn cross(row1: [f32; 3], row2: [f32; 3])
     [
         row1[1] * row2[2] - row1[2] * row2[1],
         row1[2] * row2[0] - row1[0] * row2[2],
         row1[0] * row2[1] - row1[1] * row2[0]
     ]
 }
 
 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;
+    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;
 
-        // 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));
+        // 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));
 
-        // Interpolate quaternions using spherical linear interpolation (Slerp).
+        // 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 +
                           self.quaternion.3 * other.quaternion.3;
 
         // Clamp product to -1.0 <= product <= 1.0
         product = product.min(1.0);
         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)
     }
 }
 
 impl From<MatrixDecomposed3D> for ComputedMatrix {
     /// Recompose a 3D matrix.
     /// https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix
     fn from(decomposed: MatrixDecomposed3D) -> ComputedMatrix {
         let mut matrix = ComputedMatrix::identity();
@@ -2369,58 +2409,58 @@ impl ComputedMatrix {
 
         Some(x)
     }
 }
 
 /// 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
                 TransformList(None)
             }
         };
 
         Ok(result)
     }
 }
 
 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)
             }
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         match (self, other) {
             (&Either::First(ref this), &Either::First(ref other)) => {
@@ -2492,31 +2532,36 @@ impl IntermediateRGBA {
     pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
         IntermediateRGBA { red: red, green: green, blue: blue, alpha: alpha }
     }
 }
 
 /// 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))
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         self.compute_squared_distance(other).map(|sq| sq.sqrt())
@@ -2583,20 +2628,22 @@ impl<'a> From<<&'a Either<IntermediateCo
 #[allow(missing_docs)]
 pub enum IntermediateColor {
     CurrentColor,
     IntermediateRGBA(IntermediateRGBA),
 }
 
 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(()),
         }
     }
 
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -185,23 +185,26 @@
             Explicit(ExplicitSize),
             Cover,
             Contain,
         }
 
         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(()),
                 }
             }
 
             #[inline]
             fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -2193,21 +2193,24 @@
         pub struct T {
             pub horizontal: LengthOrPercentage,
             pub vertical: LengthOrPercentage,
             pub depth: Length,
         }
 
         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)),
                 })
             }
 
             #[inline]
             fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
                 self.compute_squared_distance(other).map(|sd| sd.sqrt())
             }
 
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1068,20 +1068,22 @@
                     T::None
                 } else {
                     T::Number(gecko)
                 }
             }
         }
 
         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(()),
                 }
             }
 
             #[inline]
             fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
                 match (*self, *other) {
                     (T::Number(ref number), T::Number(ref other)) =>
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -37,20 +37,23 @@
         pub struct T {
             pub horizontal: Au,
             pub vertical: Au,
         }
 
         /// 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)),
                 })
             }
 
             #[inline]
             fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
                 self.compute_squared_distance(other).map(|sd| sd.sqrt())
             }
 
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -124,17 +124,18 @@ macro_rules! define_keyword_type {
         impl ::style_traits::ToCss for $name {
             fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result where W: ::std::fmt::Write {
                 write!(dest, $css)
             }
         }
 
         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)
             }
         }
 
         impl fmt::Debug for $name {
             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 write!(f, $css)
             }