aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-05-14 23:24:48 -0500
committerGitHub <noreply@github.com>2017-05-14 23:24:48 -0500
commit94e977efebb93ef411222edc219bc9f30430a3f9 (patch)
treeed7820a4f07395d77139df67c1ce8910049f44b4
parent8366b4d4f933f3efcadb6e337f2197cadca3e6ed (diff)
parent5539728b3cc435e65c61294ac2a1a5c6de1b2a27 (diff)
downloadservo-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.rs18
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs486
-rw-r--r--components/style/properties/longhand/background.mako.rs9
-rw-r--r--components/style/properties/longhand/box.mako.rs11
-rw-r--r--components/style/properties/longhand/font.mako.rs6
-rw-r--r--components/style/properties/longhand/inherited_table.mako.rs9
-rw-r--r--components/style/values/mod.rs3
-rw-r--r--ports/geckolib/glue.rs37
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()
};