diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index ee2210fa2f0..a66b2fb7aeb 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1401,15 +1401,15 @@ impl<'le> TElement for GeckoElement<'le> { }) } - fn needs_transitions_update_per_property(&self, - property: &TransitionProperty, - combined_duration: f32, - before_change_style: &ComputedValues, - after_change_style: &ComputedValues, - existing_transitions: &HashMap>) - -> bool { - use properties::animated_properties::Animatable; + fn needs_transitions_update_per_property( + &self, + property: &TransitionProperty, + combined_duration: f32, + before_change_style: &ComputedValues, + after_change_style: &ComputedValues, + existing_transitions: &HashMap>, + ) -> bool { + use values::animated::{Animate, Procedure}; // |property| should be an animatable longhand let animatable_longhand = AnimatableLonghand::from_transition_property(property).unwrap(); @@ -1431,7 +1431,7 @@ impl<'le> TElement for GeckoElement<'le> { combined_duration > 0.0f32 && from != to && - from.interpolate(&to, 0.5).is_ok() + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).is_ok() } #[inline] diff --git a/components/style/macros.rs b/components/style/macros.rs index 8af642f80da..b4a27415162 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -87,10 +87,13 @@ macro_rules! define_keyword_type { #[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)] pub struct $name; - impl $crate::properties::animated_properties::Animatable for $name { + impl $crate::values::animated::Animate for $name { #[inline] - fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64) - -> Result { + fn animate( + &self, + _other: &Self, + _procedure: $crate::values::animated::Procedure, + ) -> Result { Ok($name) } } diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 5d126e91f50..44190948ccf 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -211,7 +211,7 @@ class Longhand(object): self.animatable = False self.transitionable = False self.animation_type = None - # NB: Animatable implies clone because a property animation requires a + # NB: Animate implies clone because a property animation requires a # copy of the computed value. # # See components/style/helpers/animated_properties.mako.rs. diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index e82ee073c87..b7667e64421 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -133,17 +133,12 @@ ); % if need_animatable or animation_value_type == "ComputedValue": - use properties::animated_properties::Animatable; - use values::animated::ToAnimatedZero; + use values::animated::{Animate, Procedure, ToAnimatedZero}; - impl Animatable for T { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { - self.0.add_weighted(&other.0, self_portion, other_portion).map(T) - } - - fn add(&self, other: &Self) -> Result { - self.0.add(&other.0).map(T) + impl Animate for T { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(T(self.0.animate(&other.0, procedure)?)) } } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 1df5dc19d6a..b30bb25623d 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -8,7 +8,7 @@ use app_units::Au; use cssparser::Parser; -use euclid::{Point2D, Point3D, Size2D}; +use euclid::Point3D; #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap; #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; @@ -39,7 +39,7 @@ use super::ComputedValues; #[cfg(feature = "gecko")] use values::Auto; use values::{CSSFloat, CustomIdent, Either}; -use values::animated::{ToAnimatedValue, ToAnimatedZero}; +use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA}; use values::animated::effects::BoxShadowList as AnimatedBoxShadowList; use values::animated::effects::Filter as AnimatedFilter; @@ -60,39 +60,8 @@ use values::generics::position as generic_position; use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint}; use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity}; -/// 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; - - /// [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.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.add_weighted(other, 1.0, 1.0) - } - - /// [Accumulates][animation-accumulation] this value onto itself (|count| - 1) times then - /// accumulates |other| onto the result. - /// If |count| is zero, the result will be |other|. - /// - /// [animation-accumulation]: https://w3c.github.io/web-animations/#animation-accumulation - fn accumulate(&self, other: &Self, count: u64) -> Result { - self.add_weighted(other, count as f64, 1.0) - } -} - /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list -pub trait RepeatableListAnimatable: Animatable {} +pub trait RepeatableListAnimatable: Animate {} /// A longhand property whose animation type is not "none". /// @@ -451,7 +420,7 @@ impl AnimatedProperty { % if prop.animation_value_type == "discrete": let value = if progress < 0.5 { from.clone() } else { to.clone() }; % else: - let value = match from.interpolate(to, progress) { + let value = match from.animate(to, Procedure::Interpolate { progress }) { Ok(value) => value, Err(()) => return, }; @@ -662,78 +631,38 @@ impl AnimationValue { } } -impl Animatable for AnimationValue { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { +impl Animate for AnimationValue { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { % for prop in data.longhands: - % if prop.animatable: - (&AnimationValue::${prop.camel_case}(ref from), - &AnimationValue::${prop.camel_case}(ref to)) => { - % if prop.animation_value_type == "discrete": - if self_portion > other_portion { - Ok(AnimationValue::${prop.camel_case}(from.clone())) - } else { - Ok(AnimationValue::${prop.camel_case}(to.clone())) - } - % else: - from.add_weighted(to, self_portion, other_portion) - .map(AnimationValue::${prop.camel_case}) - % endif - } - % endif + % if prop.animatable: + % if prop.animation_value_type != "discrete": + ( + &AnimationValue::${prop.camel_case}(ref this), + &AnimationValue::${prop.camel_case}(ref other), + ) => { + Ok(AnimationValue::${prop.camel_case}( + this.animate(other, procedure)?, + )) + }, + % else: + ( + &AnimationValue::${prop.camel_case}(ref this), + &AnimationValue::${prop.camel_case}(ref other), + ) => { + if let Procedure::Interpolate { progress } = procedure { + Ok(AnimationValue::${prop.camel_case}( + if progress < 0.5 { this.clone() } else { other.clone() }, + )) + } else { + Err(()) + } + }, + % endif + % endif % endfor _ => { - panic!("Expected weighted addition of computed values of the same \ - property, got: {:?}, {:?}", self, other); - } - } - } - - fn add(&self, other: &Self) -> Result { - 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 addition of computed values of the same \ - property, got: {:?}, {:?}", self, other); - } - } - } - - fn accumulate(&self, other: &Self, count: u64) -> Result { - 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.accumulate(to, count).map(AnimationValue::${prop.camel_case}) - } - % endif - % endif - % endfor - _ => { - panic!("Expected accumulation of computed values of the same \ - property, got: {:?}, {:?}", self, other); + panic!("Unexpected AnimationValue::animate call, got: {:?}, {:?}", self, other); } } } @@ -789,17 +718,19 @@ impl RepeatableListAnimatable for SvgLengthOrPercentageOrNumber { - $(impl Animatable for $ty { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { + $(impl Animate for $ty + where + T: RepeatableListAnimatable, + { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { // If the length of either list is zero, the least common multiple is undefined. if self.is_empty() || other.is_empty() { return Err(()); } use num_integer::lcm; let len = lcm(self.len(), other.len()); - self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| { - me.add_weighted(you, self_portion, other_portion) + self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| { + this.animate(other, procedure) }).collect() } } @@ -825,63 +756,17 @@ macro_rules! repeated_vec_impl { repeated_vec_impl!(SmallVec<[T; 1]>, Vec); -/// https://drafts.csswg.org/css-transitions/#animtype-number -impl Animatable for Au { - #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32)) - } -} - -impl Animatable for Option - where T: Animatable, -{ - #[inline] - fn add_weighted(&self, other: &Option, self_portion: f64, other_portion: f64) -> Result, ()> { - match (self, other) { - (&Some(ref this), &Some(ref other)) => { - Ok(this.add_weighted(other, self_portion, other_portion).ok()) - } - (&None, &None) => Ok(None), - _ => Err(()), - } - } -} - -/// https://drafts.csswg.org/css-transitions/#animtype-number -impl Animatable for f32 { - #[inline] - fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result { - Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32) - } -} - -/// https://drafts.csswg.org/css-transitions/#animtype-number -impl Animatable for f64 { - #[inline] - fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result { - Ok(*self * self_portion + *other * other_portion) - } -} - -/// https://drafts.csswg.org/css-transitions/#animtype-integer -impl Animatable for i32 { - #[inline] - fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result { - Ok((*self as f64 * self_portion + *other as f64 * other_portion + 0.5).floor() as i32) - } -} - /// https://drafts.csswg.org/css-transitions/#animtype-visibility -impl Animatable for Visibility { +impl Animate for Visibility { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + let (this_weight, other_weight) = procedure.weights(); match (*self, *other) { (Visibility::visible, _) => { - Ok(if self_portion > 0.0 { *self } else { *other }) + Ok(if this_weight > 0.0 { *self } else { *other }) }, (_, Visibility::visible) => { - Ok(if other_portion > 0.0 { *other } else { *self }) + Ok(if other_weight > 0.0 { *other } else { *self }) }, _ => Err(()), } @@ -902,37 +787,19 @@ impl ToAnimatedZero for Visibility { } } -impl Animatable for Size2D { - #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - let width = self.width.add_weighted(&other.width, self_portion, other_portion)?; - let height = self.height.add_weighted(&other.height, self_portion, other_portion)?; - - Ok(Size2D::new(width, height)) - } -} - -impl Animatable for Point2D { - #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - let x = self.x.add_weighted(&other.x, self_portion, other_portion)?; - let y = self.y.add_weighted(&other.y, self_portion, other_portion)?; - - Ok(Point2D::new(x, y)) - } -} - /// https://drafts.csswg.org/css-transitions/#animtype-length -impl Animatable for VerticalAlign { +impl Animate for VerticalAlign { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (VerticalAlign::LengthOrPercentage(ref this), - VerticalAlign::LengthOrPercentage(ref other)) => { - this.add_weighted(other, self_portion, other_portion).map(|value| { - VerticalAlign::LengthOrPercentage(value) - }) - } + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &VerticalAlign::LengthOrPercentage(ref this), + &VerticalAlign::LengthOrPercentage(ref other), + ) => { + Ok(VerticalAlign::LengthOrPercentage( + this.animate(other, procedure)? + )) + }, _ => Err(()), } } @@ -960,59 +827,53 @@ impl ToAnimatedZero for VerticalAlign { } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for CalcLengthOrPercentage { +impl Animate for CalcLengthOrPercentage { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - fn add_weighted_half(this: Option, - other: Option, - self_portion: f64, - other_portion: f64) - -> Result, ()> - 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.add_weighted(&other, self_portion, other_portion).map(Some) - } + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + let animate_percentage_half = |this: Option, other: Option| { + if this.is_none() && other.is_none() { + return Ok(None); } - } + let this = this.unwrap_or_default(); + let other = other.unwrap_or_default(); + Ok(Some(this.animate(&other, procedure)?)) + }; - let length = self.unclamped_length().add_weighted(&other.unclamped_length(), self_portion, other_portion)?; - let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?; + let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?; + let percentage = animate_percentage_half(self.percentage, other.percentage)?; Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode)) } } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for LengthOrPercentage { +impl Animate for LengthOrPercentage { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (LengthOrPercentage::Length(ref this), - LengthOrPercentage::Length(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentage::Length) - } - (LengthOrPercentage::Percentage(ref this), - LengthOrPercentage::Percentage(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentage::Percentage) - } + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &LengthOrPercentage::Length(ref this), + &LengthOrPercentage::Length(ref other), + ) => { + Ok(LengthOrPercentage::Length(this.animate(other, procedure)?)) + }, + ( + &LengthOrPercentage::Percentage(ref this), + &LengthOrPercentage::Percentage(ref other), + ) => { + Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?)) + }, (this, other) => { // Special handling for zero values since these should not require calc(). if this.is_definitely_zero() { - return other.add_weighted(&other, 0., other_portion) - } else if other.is_definitely_zero() { - return this.add_weighted(self, self_portion, 0.) + return other.to_animated_zero()?.animate(other, procedure); + } + if other.is_definitely_zero() { + return this.animate(&this.to_animated_zero()?, procedure); } - let this: CalcLengthOrPercentage = From::from(this); - let other: CalcLengthOrPercentage = From::from(other); - this.add_weighted(&other, self_portion, other_portion) - .map(LengthOrPercentage::Calc) + let this = CalcLengthOrPercentage::from(*this); + let other = CalcLengthOrPercentage::from(*other); + Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?)) } } } @@ -1021,41 +882,49 @@ impl Animatable for LengthOrPercentage { impl ToAnimatedZero for LengthOrPercentage { #[inline] fn to_animated_zero(&self) -> Result { - match self { - &LengthOrPercentage::Length(_) | &LengthOrPercentage::Calc(_) => - Ok(LengthOrPercentage::zero()), - &LengthOrPercentage::Percentage(_) => - Ok(LengthOrPercentage::Percentage(Percentage::zero())), - } + match *self { + LengthOrPercentage::Length(ref length) => { + Ok(LengthOrPercentage::Length(length.to_animated_zero()?)) + }, + LengthOrPercentage::Percentage(ref percentage) => { + Ok(LengthOrPercentage::Percentage(percentage.to_animated_zero()?)) + }, + LengthOrPercentage::Calc(ref calc) => { + Ok(LengthOrPercentage::Calc(calc.to_animated_zero()?)) + }, + } } } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for LengthOrPercentageOrAuto { +impl Animate for LengthOrPercentageOrAuto { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (LengthOrPercentageOrAuto::Length(ref this), - LengthOrPercentageOrAuto::Length(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentageOrAuto::Length) - } - (LengthOrPercentageOrAuto::Percentage(ref this), - LengthOrPercentageOrAuto::Percentage(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentageOrAuto::Percentage) - } - (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &LengthOrPercentageOrAuto::Length(ref this), + &LengthOrPercentageOrAuto::Length(ref other), + ) => { + Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?)) + }, + ( + &LengthOrPercentageOrAuto::Percentage(ref this), + &LengthOrPercentageOrAuto::Percentage(ref other), + ) => { + Ok(LengthOrPercentageOrAuto::Percentage( + this.animate(other, procedure)?, + )) + }, + (&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => { Ok(LengthOrPercentageOrAuto::Auto) - } + }, (this, other) => { - let this: Option = From::from(this); - let other: Option = From::from(other); - match this.add_weighted(&other, self_portion, other_portion) { - Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)), - _ => Err(()), - } - } + let this: Option = From::from(*this); + let other: Option = From::from(*other); + Ok(LengthOrPercentageOrAuto::Calc( + this.animate(&other, procedure)?.ok_or(())?, + )) + }, } } } @@ -1075,30 +944,33 @@ impl ToAnimatedZero for LengthOrPercentageOrAuto { } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for LengthOrPercentageOrNone { +impl Animate for LengthOrPercentageOrNone { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (LengthOrPercentageOrNone::Length(ref this), - LengthOrPercentageOrNone::Length(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentageOrNone::Length) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &LengthOrPercentageOrNone::Length(ref this), + &LengthOrPercentageOrNone::Length(ref other), + ) => { + Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?)) + }, + ( + &LengthOrPercentageOrNone::Percentage(ref this), + &LengthOrPercentageOrNone::Percentage(ref other), + ) => { + Ok(LengthOrPercentageOrNone::Percentage( + this.animate(other, procedure)?, + )) } - (LengthOrPercentageOrNone::Percentage(ref this), - LengthOrPercentageOrNone::Percentage(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(LengthOrPercentageOrNone::Percentage) - } - (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => { + (&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => { Ok(LengthOrPercentageOrNone::None) - } + }, (this, other) => { - let this = >::from(this); - let other = >::from(other); - match this.add_weighted(&other, self_portion, other_portion) { - Ok(Some(result)) => Ok(LengthOrPercentageOrNone::Calc(result)), - _ => Err(()), - } + let this = >::from(*this); + let other = >::from(*other); + Ok(LengthOrPercentageOrNone::Calc( + this.animate(&other, procedure)?.ok_or(())?, + )) }, } } @@ -1119,14 +991,17 @@ impl ToAnimatedZero for LengthOrPercentageOrNone { } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for MozLength { +impl Animate for MozLength { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (MozLength::LengthOrPercentageOrAuto(ref this), - MozLength::LengthOrPercentageOrAuto(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(MozLength::LengthOrPercentageOrAuto) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &MozLength::LengthOrPercentageOrAuto(ref this), + &MozLength::LengthOrPercentageOrAuto(ref other), + ) => { + Ok(MozLength::LengthOrPercentageOrAuto( + this.animate(other, procedure)?, + )) } _ => Err(()), } @@ -1146,15 +1021,18 @@ impl ToAnimatedZero for MozLength { } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animatable for MaxLength { +impl Animate for MaxLength { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (MaxLength::LengthOrPercentageOrNone(ref this), - MaxLength::LengthOrPercentageOrNone(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(MaxLength::LengthOrPercentageOrNone) - } + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + ( + &MaxLength::LengthOrPercentageOrNone(ref this), + &MaxLength::LengthOrPercentageOrNone(ref other), + ) => { + Ok(MaxLength::LengthOrPercentageOrNone( + this.animate(other, procedure)?, + )) + }, _ => Err(()), } } @@ -1166,13 +1044,14 @@ impl ToAnimatedZero for MaxLength { } /// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight -impl Animatable for FontWeight { +impl Animate for FontWeight { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { let a = self.0 as f64; let b = other.0 as f64; const NORMAL: f64 = 400.; - let weight = (a - NORMAL) * self_portion + (b - NORMAL) * other_portion + NORMAL; + let (this_weight, other_weight) = procedure.weights(); + let weight = (a - NORMAL) * this_weight + (b - NORMAL) * other_weight + NORMAL; let weight = (weight.max(100.).min(900.) / 100.).round() * 100.; Ok(FontWeight(weight as u16)) } @@ -1186,17 +1065,15 @@ impl ToAnimatedZero for FontWeight { } /// https://drafts.csswg.org/css-fonts/#font-stretch-prop -impl Animatable for FontStretch { +impl Animate for FontStretch { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result + fn animate(&self, other: &Self, procedure: Procedure) -> Result { let from = f64::from(*self); let to = f64::from(*other); - // FIXME: When `const fn` is available in release rust, make |normal|, below, const. let normal = f64::from(FontStretch::normal); - let result = (from - normal) * self_portion + (to - normal) * other_portion + normal; - + let (this_weight, other_weight) = procedure.weights(); + let result = (from - normal) * this_weight + (to - normal) * other_weight + normal; Ok(result.into()) } } @@ -1243,13 +1120,16 @@ impl Into for f64 { } } -/// https://drafts.csswg.org/css-transitions/#animtype-simple-list -impl Animatable for generic_position::Position { +impl Animate for generic_position::Position +where + H: Animate, + V: Animate, +{ #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(generic_position::Position { - horizontal: self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?, - vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?, + horizontal: self.horizontal.animate(&other.horizontal, procedure)?, + vertical: self.vertical.animate(&other.vertical, procedure)?, }) } } @@ -1272,15 +1152,14 @@ impl RepeatableListAnimatable for generic_position::Position where H: RepeatableListAnimatable, V: RepeatableListAnimatable {} /// https://drafts.csswg.org/css-transitions/#animtype-rect -impl Animatable for ClipRect { +impl Animate for ClipRect { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(ClipRect { - top: self.top.add_weighted(&other.top, self_portion, other_portion)?, - right: self.right.add_weighted(&other.right, self_portion, other_portion)?, - bottom: self.bottom.add_weighted(&other.bottom, self_portion, other_portion)?, - left: self.left.add_weighted(&other.left, self_portion, other_portion)?, + top: self.top.animate(&other.top, procedure)?, + right: self.right.animate(&other.right, procedure)?, + bottom: self.bottom.animate(&other.bottom, procedure)?, + left: self.left.animate(&other.left, procedure)?, }) } } @@ -1334,36 +1213,34 @@ impl ToAnimatedZero for TransformOperation { // // Therefore, we use an identity matrix to represent the identity transform list. // http://dev.w3.org/csswg/css-transforms/#identity-transform-function + // + // FIXME(nox): This does not actually work, given the impl of + // Animate for TransformOperation bails out if the two given + // values are dissimilar. Ok(TransformOperation::Matrix(ComputedMatrix::identity())) }, } } } -fn add_weighted_multiplicative_factor( +fn animate_multiplicative_factor( this: CSSFloat, other: CSSFloat, - self_portion: f64, - other_portion: f64, + procedure: Procedure, ) -> Result { - Ok((this - 1.).add_weighted(&(other - 1.), self_portion, other_portion)? + 1.) + Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.) } /// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms -impl Animatable for TransformOperation { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { +impl Animate for TransformOperation { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { ( &TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other), ) => { Ok(TransformOperation::Matrix( - this.add_weighted(other, self_portion, other_portion)?, + this.animate(other, procedure)?, )) }, ( @@ -1371,8 +1248,8 @@ impl Animatable for TransformOperation { &TransformOperation::Skew(ref tx, ref ty), ) => { Ok(TransformOperation::Skew( - fx.add_weighted(tx, self_portion, other_portion)?, - fy.add_weighted(ty, self_portion, other_portion)?, + fx.animate(tx, procedure)?, + fy.animate(ty, procedure)?, )) }, ( @@ -1380,9 +1257,9 @@ impl Animatable for TransformOperation { &TransformOperation::Translate(ref tx, ref ty, ref tz), ) => { Ok(TransformOperation::Translate( - fx.add_weighted(tx, self_portion, other_portion)?, - fy.add_weighted(ty, self_portion, other_portion)?, - fz.add_weighted(tz, self_portion, other_portion)?, + fx.animate(tx, procedure)?, + fy.animate(ty, procedure)?, + fz.animate(tz, procedure)?, )) }, ( @@ -1390,9 +1267,9 @@ impl Animatable for TransformOperation { &TransformOperation::Scale(ref tx, ref ty, ref tz), ) => { Ok(TransformOperation::Scale( - add_weighted_multiplicative_factor(*fx, *tx, self_portion, other_portion)?, - add_weighted_multiplicative_factor(*fy, *ty, self_portion, other_portion)?, - add_weighted_multiplicative_factor(*fz, *tz, self_portion, other_portion)?, + animate_multiplicative_factor(*fx, *tx, procedure)?, + animate_multiplicative_factor(*fy, *ty, procedure)?, + animate_multiplicative_factor(*fz, *tz, procedure)?, )) }, ( @@ -1402,13 +1279,13 @@ impl Animatable for TransformOperation { let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa); let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta); if (fx, fy, fz) == (tx, ty, tz) { - let ia = fa.add_weighted(&ta, self_portion, other_portion)?; + let ia = fa.animate(&ta, procedure)?; Ok(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); Ok(TransformOperation::Matrix( - matrix_f.add_weighted(&matrix_t, self_portion, other_portion)? + matrix_f.animate(&matrix_t, procedure)?, )) } }, @@ -1425,7 +1302,7 @@ impl Animatable for TransformOperation { td_matrix.m34 = -1. / td.to_f32_px(); } Ok(TransformOperation::Matrix( - fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)?, + fd_matrix.animate(&td_matrix, procedure)?, )) }, _ => Err(()), @@ -1499,38 +1376,38 @@ pub struct MatrixDecomposed2D { pub matrix: InnerMatrix2D, } -impl Animatable for InnerMatrix2D { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for InnerMatrix2D { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(InnerMatrix2D { - m11: add_weighted_multiplicative_factor(self.m11, other.m11, self_portion, other_portion)?, - m12: self.m12.add_weighted(&other.m12, self_portion, other_portion)?, - m21: self.m21.add_weighted(&other.m21, self_portion, other_portion)?, - m22: add_weighted_multiplicative_factor(self.m22, other.m22, self_portion, other_portion)?, + m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?, + m12: self.m12.animate(&other.m12, procedure)?, + m21: self.m21.animate(&other.m21, procedure)?, + m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?, }) } } -impl Animatable for Translate2D { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Translate2D { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Translate2D( - self.0.add_weighted(&other.0, self_portion, other_portion)?, - self.1.add_weighted(&other.1, self_portion, other_portion)?, + self.0.animate(&other.0, procedure)?, + self.1.animate(&other.1, procedure)?, )) } } -impl Animatable for Scale2D { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Scale2D { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Scale2D( - add_weighted_multiplicative_factor(self.0, other.0, self_portion, other_portion)?, - add_weighted_multiplicative_factor(self.1, other.1, self_portion, other_portion)?, + animate_multiplicative_factor(self.0, other.0, procedure)?, + animate_multiplicative_factor(self.1, other.1, procedure)?, )) } } -impl Animatable for MatrixDecomposed2D { +impl Animate for MatrixDecomposed2D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { // If x-axis of one is flipped, and y-axis of the other, // convert to an unflipped rotation. let mut scale = self.scale; @@ -1560,10 +1437,10 @@ impl Animatable for MatrixDecomposed2D { } // Interpolate all values. - let translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?; - let scale = scale.add_weighted(&other.scale, self_portion, other_portion)?; - let angle = angle.add_weighted(&other_angle, self_portion, other_portion)?; - let matrix = self.matrix.add_weighted(&other.matrix, self_portion, other_portion)?; + let translate = self.translate.animate(&other.translate, procedure)?; + let scale = scale.animate(&other.scale, procedure)?; + let angle = angle.animate(&other_angle, procedure)?; + let matrix = self.matrix.animate(&other.matrix, procedure)?; Ok(MatrixDecomposed2D { translate: translate, @@ -1588,26 +1465,25 @@ impl ComputeSquaredDistance for MatrixDecomposed2D { } } -impl Animatable for ComputedMatrix { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for ComputedMatrix { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { 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 sum = from.add_weighted(&to, self_portion, other_portion)?; - Ok(ComputedMatrix::from(sum)) + (Ok(this), Ok(other)) => { + Ok(ComputedMatrix::from(this.animate(&other, procedure)?)) }, _ => { - let result = if self_portion > other_portion {*self} else {*other}; + let (this_weight, other_weight) = procedure.weights(); + let result = if this_weight > other_weight { *self } else { *other }; Ok(result) - } + }, } } else { - let decomposed_from = MatrixDecomposed2D::from(*self); - let decomposed_to = MatrixDecomposed2D::from(*other); - let sum = decomposed_from.add_weighted(&decomposed_to, self_portion, other_portion)?; - Ok(ComputedMatrix::from(sum)) + let this = MatrixDecomposed2D::from(*self); + let other = MatrixDecomposed2D::from(*other); + Ok(ComputedMatrix::from(this.animate(&other, procedure)?)) } } } @@ -2044,32 +1920,32 @@ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] { ] } -impl Animatable for Translate3D { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Translate3D { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Translate3D( - self.0.add_weighted(&other.0, self_portion, other_portion)?, - self.1.add_weighted(&other.1, self_portion, other_portion)?, - self.2.add_weighted(&other.2, self_portion, other_portion)?, + self.0.animate(&other.0, procedure)?, + self.1.animate(&other.1, procedure)?, + self.2.animate(&other.2, procedure)?, )) } } -impl Animatable for Scale3D { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Scale3D { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Scale3D( - add_weighted_multiplicative_factor(self.0, other.0, self_portion, other_portion)?, - add_weighted_multiplicative_factor(self.1, other.1, self_portion, other_portion)?, - add_weighted_multiplicative_factor(self.2, other.2, self_portion, other_portion)?, + animate_multiplicative_factor(self.0, other.0, procedure)?, + animate_multiplicative_factor(self.1, other.1, procedure)?, + animate_multiplicative_factor(self.2, other.2, procedure)?, )) } } -impl Animatable for Skew { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Skew { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Skew( - self.0.add_weighted(&other.0, self_portion, other_portion)?, - self.1.add_weighted(&other.1, self_portion, other_portion)?, - self.2.add_weighted(&other.2, self_portion, other_portion)?, + self.0.animate(&other.0, procedure)?, + self.1.animate(&other.1, procedure)?, + self.2.animate(&other.2, procedure)?, )) } } @@ -2085,40 +1961,41 @@ impl ComputeSquaredDistance for Skew { } } -impl Animatable for Perspective { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for Perspective { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Perspective( - self.0.add_weighted(&other.0, self_portion, other_portion)?, - self.1.add_weighted(&other.1, self_portion, other_portion)?, - self.2.add_weighted(&other.2, self_portion, other_portion)?, - add_weighted_multiplicative_factor(self.3, other.3, self_portion, other_portion)?, + self.0.animate(&other.0, procedure)?, + self.1.animate(&other.1, procedure)?, + self.2.animate(&other.2, procedure)?, + animate_multiplicative_factor(self.3, other.3, procedure)?, )) } } -impl Animatable for MatrixDecomposed3D { +impl Animate for MatrixDecomposed3D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { use std::f64; - debug_assert!((self_portion + other_portion - 1.0f64).abs() <= f64::EPSILON || - other_portion == 1.0f64 || other_portion == 0.0f64, - "add_weighted should only be used for interpolating or accumulating transforms"); + let (this_weight, other_weight) = procedure.weights(); + + debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON || + other_weight == 1.0f64 || other_weight == 0.0f64, + "animate should only be used for interpolating or accumulating transforms"); let mut sum = *self; // Add translate, scale, skew and perspective components. - sum.translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?; - sum.scale = self.scale.add_weighted(&other.scale, self_portion, other_portion)?; - sum.skew = self.skew.add_weighted(&other.skew, self_portion, other_portion)?; - sum.perspective = self.perspective.add_weighted(&other.perspective, self_portion, other_portion)?; + sum.translate = self.translate.animate(&other.translate, procedure)?; + sum.scale = self.scale.animate(&other.scale, procedure)?; + sum.skew = self.skew.animate(&other.skew, procedure)?; + sum.perspective = self.perspective.animate(&other.perspective, procedure)?; // Add quaternions using spherical linear interpolation (Slerp). // - // We take a specialized code path for accumulation (where other_portion is 1) - if other_portion == 1.0 { - if self_portion == 0.0 { + // We take a specialized code path for accumulation (where other_weight is 1) + if other_weight == 1.0 { + if this_weight == 0.0 { return Ok(*other) } @@ -2127,10 +2004,10 @@ impl Animatable for MatrixDecomposed3D { // Determine the scale factor. let mut theta = clamped_w.acos(); let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() }; - theta *= self_portion; + theta *= this_weight; scale *= theta.sin(); - // Scale the self matrix by self_portion. + // Scale the self matrix by this_weight. let mut scaled_self = *self; % for i in range(3): scaled_self.quaternion.${i} *= scale; @@ -2161,12 +2038,12 @@ impl Animatable for MatrixDecomposed3D { } let theta = product.acos(); - let w = (other_portion * theta).sin() * 1.0 / (1.0 - product * product).sqrt(); + let w = (other_weight * 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} *= (other_portion * theta).cos() - product * w; + a.quaternion.${i} *= (other_weight * theta).cos() - product * w; b.quaternion.${i} *= w; sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i}; % endfor @@ -2374,77 +2251,37 @@ impl ComputedMatrix { } /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms -impl Animatable for TransformList { +impl Animate for TransformList { #[inline] - fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result { - let result = self.animate_with_similar_list( - other, - |this, other| this.add_weighted(other, self_portion, other_portion), - ); - let (this, other) = match result { - Ok(list) => return Ok(list), - Err(None) => return Err(()), - Err(Some(pair)) => pair, - }; - Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix { - from_list: this, - to_list: other, - progress: Percentage(other_portion as f32), - }]))) - } - - fn add(&self, other: &Self) -> Result { - let this = self.0.as_ref().map_or(&[][..], |l| l); - let other = other.0.as_ref().map_or(&[][..], |l| l); - let result = this.iter().chain(other).cloned().collect::>(); - Ok(TransformList(if result.is_empty() { - None - } else { - Some(result) - })) - } - - #[inline] - fn accumulate(&self, other: &Self, count: u64) -> Result { - let result = self.animate_with_similar_list( - other, - |this, other| this.accumulate(other, count), - ); - let (this, other) = match result { - Ok(list) => return Ok(list), - Err(None) => return Err(()), - Err(Some(pair)) => pair, - }; - - Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix { - from_list: this, - to_list: other, - count: cmp::min(count, i32::max_value() as u64) as i32, - }]))) - } -} - -impl TransformList { - fn animate_with_similar_list( + fn animate( &self, other: &Self, - animate: F, - ) -> Result> - where - F: Fn(&TransformOperation, &TransformOperation) -> Result, - { + procedure: Procedure, + ) -> Result { if self.0.is_none() && other.0.is_none() { return Ok(TransformList(None)); } + + if procedure == Procedure::Add { + let this = self.0.as_ref().map_or(&[][..], |l| l); + let other = other.0.as_ref().map_or(&[][..], |l| l); + let result = this.iter().chain(other).cloned().collect::>(); + return Ok(TransformList(if result.is_empty() { + None + } else { + Some(result) + })); + } + let this = if self.0.is_some() { Cow::Borrowed(self) } else { - Cow::Owned(other.to_animated_zero().map_err(|_| None)?) + Cow::Owned(other.to_animated_zero()?) }; let other = if other.0.is_some() { Cow::Borrowed(other) } else { - Cow::Owned(self.to_animated_zero().map_err(|_| None)?) + Cow::Owned(self.to_animated_zero()?) }; { @@ -2452,7 +2289,7 @@ impl TransformList { let other = (*other).0.as_ref().map_or(&[][..], |l| l); if this.len() == other.len() { let result = this.iter().zip(other).map(|(this, other)| { - animate(this, other) + this.animate(other, procedure) }).collect::, _>>(); if let Ok(list) = result { return Ok(TransformList(if list.is_empty() { @@ -2464,7 +2301,23 @@ impl TransformList { } } - Err(Some((this.into_owned(), other.into_owned()))) + match procedure { + Procedure::Add => Err(()), + Procedure::Interpolate { progress } => { + Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix { + from_list: this.into_owned(), + to_list: other.into_owned(), + progress: Percentage(progress as f32), + }]))) + }, + Procedure::Accumulate { count } => { + Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix { + from_list: this.into_owned(), + to_list: other.into_owned(), + count: cmp::min(count, i32::max_value() as u64) as i32, + }]))) + }, + } } } @@ -2611,17 +2464,19 @@ impl ToAnimatedZero for TransformList { } } -impl Animatable for Either - where T: Animatable + Copy, U: Animatable + Copy, +impl Animate for Either +where + T: Animate, + U: Animate, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (Either::First(ref this), Either::First(ref other)) => { - this.add_weighted(&other, self_portion, other_portion).map(Either::First) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + (&Either::First(ref this), &Either::First(ref other)) => { + Ok(Either::First(this.animate(other, procedure)?)) }, - (Either::Second(ref this), Either::Second(ref other)) => { - this.add_weighted(&other, self_portion, other_portion).map(Either::Second) + (&Either::Second(ref this), &Either::Second(ref other)) => { + Ok(Either::Second(this.animate(other, procedure)?)) }, _ => Err(()), } @@ -2646,18 +2501,18 @@ where } } -/// Animatable SVGPaint +/// Animated SVGPaint pub type IntermediateSVGPaint = SVGPaint; -/// Animatable SVGPaintKind +/// Animated SVGPaintKind pub type IntermediateSVGPaintKind = SVGPaintKind; -impl Animatable for IntermediateSVGPaint { +impl Animate for IntermediateSVGPaint { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(IntermediateSVGPaint { - kind: self.kind.add_weighted(&other.kind, self_portion, other_portion)?, - fallback: self.fallback.add_weighted(&other.fallback, self_portion, other_portion)?, + kind: self.kind.animate(&other.kind, procedure)?, + fallback: self.fallback.animate(&other.fallback, procedure)?, }) } } @@ -2665,6 +2520,7 @@ impl Animatable for IntermediateSVGPaint { impl ComputeSquaredDistance for IntermediateSVGPaint { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { + // FIXME(nox): This should be derived. Ok( self.kind.compute_squared_distance(&other.kind)? + self.fallback.compute_squared_distance(&other.fallback)?, @@ -2682,18 +2538,20 @@ impl ToAnimatedZero for IntermediateSVGPaint { } } -impl Animatable for IntermediateSVGPaintKind { +impl Animate for IntermediateSVGPaintKind { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { - (&SVGPaintKind::Color(ref self_color), &SVGPaintKind::Color(ref other_color)) => { - Ok(SVGPaintKind::Color(self_color.add_weighted(other_color, self_portion, other_portion)?)) - } - // FIXME context values should be interpolable with colors - // Gecko doesn't implement this behavior either. + (&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => { + Ok(SVGPaintKind::Color(this.animate(other, procedure)?)) + }, (&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) => Ok(SVGPaintKind::ContextFill), (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => Ok(SVGPaintKind::ContextStroke), - _ => Err(()) + _ => { + // FIXME: Context values should be interpolable with colors, + // Gecko doesn't implement this behavior either. + Err(()) + } } } } @@ -2791,142 +2649,161 @@ fn convert_from_number_or_percentage( } } -impl Animatable for - SvgLengthOrPercentageOrNumber - where LengthOrPercentageType: Animatable + Into + From + Copy, - NumberType: Animatable + Into + From, - SvgLengthOrPercentageOrNumber: Copy, - LengthOrPercentage: From +impl Animate for SvgLengthOrPercentageOrNumber +where + L: Animate + From + Into + Copy, + N: Animate + From + Into, + LengthOrPercentage: From, + Self: Copy, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { if self.has_calc() || other.has_calc() { // TODO: We need to treat calc value. // https://bugzilla.mozilla.org/show_bug.cgi?id=1386967 return Err(()); } - let from_value = convert_to_number_or_percentage(*self); - let to_value = convert_to_number_or_percentage(*other); + let this = convert_to_number_or_percentage(*self); + let other = convert_to_number_or_percentage(*other); - match (from_value, to_value) { - (NumberOrPercentage::Number(from), - NumberOrPercentage::Number(to)) => { - from.add_weighted(&to, self_portion, other_portion) - .map(|num| NumberOrPercentage::Number(num)) - .map(|nop| convert_from_number_or_percentage(nop)) + match (this, other) { + ( + NumberOrPercentage::Number(ref this), + NumberOrPercentage::Number(ref other), + ) => { + Ok(convert_from_number_or_percentage( + NumberOrPercentage::Number(this.animate(other, procedure)?) + )) }, - (NumberOrPercentage::Percentage(from), - NumberOrPercentage::Percentage(to)) => { - from.add_weighted(&to, self_portion, other_portion) - .map(|p| NumberOrPercentage::Percentage(p)) - .map(|nop| convert_from_number_or_percentage(nop)) + ( + NumberOrPercentage::Percentage(ref this), + NumberOrPercentage::Percentage(ref other), + ) => { + Ok(convert_from_number_or_percentage( + NumberOrPercentage::Percentage(this.animate(other, procedure)?) + )) }, _ => Err(()), } } } -impl ToAnimatedZero for - SvgLengthOrPercentageOrNumber - where LengthOrPercentageType: ToAnimatedZero, NumberType: ToAnimatedZero +impl ToAnimatedZero for SvgLengthOrPercentageOrNumber +where + L: ToAnimatedZero, + N: ToAnimatedZero, { #[inline] fn to_animated_zero(&self) -> Result { - match self { - &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref lop) => - lop.to_animated_zero() - .map(SvgLengthOrPercentageOrNumber::LengthOrPercentage), - &SvgLengthOrPercentageOrNumber::Number(ref num) => - num.to_animated_zero() - .map(SvgLengthOrPercentageOrNumber::Number), + match *self { + SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref lop) => { + Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage( + lop.to_animated_zero()?, + )) + }, + SvgLengthOrPercentageOrNumber::Number(ref num) => { + Ok(SvgLengthOrPercentageOrNumber::Number( + num.to_animated_zero()?, + )) + }, } } } -impl Animatable for SVGLength - where LengthType: Animatable + Clone +impl Animate for SVGLength +where + L: Animate + Clone, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => { - this.add_weighted(&other, self_portion, other_portion).map(SVGLength::Length) - } + Ok(SVGLength::Length(this.animate(other, procedure)?)) + }, _ => { - Ok(if self_portion > other_portion { self.clone() } else { other.clone() }) - } + // FIXME(nox): Is this correct for addition and accumulation? + // I think an error should be returned if it's not + // an interpolation. + let (this_weight, other_weight) = procedure.weights(); + Ok(if this_weight > other_weight { self.clone() } else { other.clone() }) + }, } } } -impl ToAnimatedZero for SVGLength where LengthType : ToAnimatedZero { +impl ToAnimatedZero for SVGLength +where + L: ToAnimatedZero, +{ #[inline] fn to_animated_zero(&self) -> Result { - match self { - &SVGLength::Length(ref length) => length.to_animated_zero().map(SVGLength::Length), - &SVGLength::ContextValue => Ok(SVGLength::ContextValue), + match *self { + SVGLength::Length(ref length) => { + Ok(SVGLength::Length(length.to_animated_zero()?)) + }, + SVGLength::ContextValue => Ok(SVGLength::ContextValue), } } } /// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty -impl Animatable for SVGStrokeDashArray - where LengthType : RepeatableListAnimatable + Clone +impl Animate for SVGStrokeDashArray +where + L: Clone + RepeatableListAnimatable, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (self, other) { - (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other))=> { - this.add_weighted(other, self_portion, other_portion) - .map(SVGStrokeDashArray::Values) - } - _ => { - Ok(if self_portion > other_portion { self.clone() } else { other.clone() }) - } + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) { + // Non-additive. + return Err(()); + } + match (self, other) { + (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => { + Ok(SVGStrokeDashArray::Values(this.animate(other, procedure)?)) + }, + _ => { + let (this_weight, other_weight) = procedure.weights(); + Ok(if this_weight > other_weight { self.clone() } else { other.clone() }) + }, } - } - - /// stroke-dasharray is non-additive - #[inline] - fn add(&self, _other: &Self) -> Result { - Err(()) - } - - /// stroke-dasharray is non-additive - #[inline] - fn accumulate(&self, _other: &Self, _count: u64) -> Result { - Err(()) } } -impl ToAnimatedZero for SVGStrokeDashArray - where LengthType : ToAnimatedZero + Clone +impl ToAnimatedZero for SVGStrokeDashArray +where + L: Clone + ToAnimatedZero { #[inline] fn to_animated_zero(&self) -> Result { - match self { - &SVGStrokeDashArray::Values(ref values) => { - values.iter().map(ToAnimatedZero::to_animated_zero) - .collect::, ()>>().map(SVGStrokeDashArray::Values) + match *self { + SVGStrokeDashArray::Values(ref values) => { + Ok(SVGStrokeDashArray::Values( + values.iter().map(ToAnimatedZero::to_animated_zero).collect::, _>>()?, + )) } - &SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue), + SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue), } } } -impl Animatable for SVGOpacity - where OpacityType: Animatable + Clone +impl Animate for SVGOpacity +where + O: Animate + Clone, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => { - this.add_weighted(other, self_portion, other_portion).map(SVGOpacity::Opacity) - } + Ok(SVGOpacity::Opacity(this.animate(other, procedure)?)) + }, _ => { - Ok(if self_portion > other_portion { self.clone() } else { other.clone() }) - } + // FIXME(nox): Is this correct for addition and accumulation? + // I think an error should be returned if it's not + // an interpolation. + let (this_weight, other_weight) = procedure.weights(); + Ok(if this_weight > other_weight { self.clone() } else { other.clone() }) + }, } } } @@ -2951,40 +2828,30 @@ impl ToAnimatedZero for SVGOpacity %> /// https://drafts.fxtf.org/filters/#animation-of-filters -impl Animatable for AnimatedFilter { - fn add_weighted( +impl Animate for AnimatedFilter { + fn animate( &self, other: &Self, - self_portion: f64, - other_portion: f64, + procedure: Procedure, ) -> Result { match (self, other) { % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']: (&Filter::${func}(ref this), &Filter::${func}(ref other)) => { - Ok(Filter::${func}(this.add_weighted( - other, - self_portion, - other_portion, - )?)) + Ok(Filter::${func}(this.animate(other, procedure)?)) }, % endfor % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']: (&Filter::${func}(ref this), &Filter::${func}(ref other)) => { - Ok(Filter::${func}(NonNegative(add_weighted_multiplicative_factor( + Ok(Filter::${func}(NonNegative(animate_multiplicative_factor( this.0, other.0, - self_portion, - other_portion, + procedure, )?))) }, % endfor % if product == "gecko": (&Filter::DropShadow(ref this), &Filter::DropShadow(ref other)) => { - Ok(Filter::DropShadow(this.add_weighted( - other, - self_portion, - other_portion, - )?)) + Ok(Filter::DropShadow(this.animate(other, procedure)?)) }, % endif _ => Err(()), @@ -2992,7 +2859,7 @@ impl Animatable for AnimatedFilter { } } - +/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation impl ToAnimatedZero for AnimatedFilter { fn to_animated_zero(&self) -> Result { match *self { @@ -3030,32 +2897,32 @@ impl ComputeSquaredDistance for AnimatedFilter { } } -impl Animatable for AnimatedFilterList { +impl Animate for AnimatedFilterList { #[inline] - fn add_weighted( + fn animate( &self, other: &Self, - self_portion: f64, - other_portion: f64, + procedure: Procedure, ) -> Result { + if procedure == Procedure::Add { + return Ok(AnimatedFilterList( + self.0.iter().chain(other.0.iter()).cloned().collect(), + )); + } Ok(AnimatedFilterList(self.0.iter().zip_longest(other.0.iter()).map(|it| { match it { EitherOrBoth::Both(this, other) => { - this.add_weighted(other, self_portion, other_portion) + this.animate(other, procedure) }, EitherOrBoth::Left(this) => { - this.add_weighted(&this.to_animated_zero()?, self_portion, other_portion) + this.animate(&this.to_animated_zero()?, procedure) }, EitherOrBoth::Right(other) => { - other.to_animated_zero()?.add_weighted(other, self_portion, other_portion) + other.to_animated_zero()?.animate(other, procedure) }, } }).collect::, _>>()?)) } - - fn add(&self, other: &Self) -> Result { - Ok(AnimatedFilterList(self.0.iter().chain(other.0.iter()).cloned().collect())) - } } impl ComputeSquaredDistance for AnimatedFilterList { @@ -3127,12 +2994,13 @@ sorted_shorthands = [(p, position) for position, p in enumerate(sorted_shorthand } } -impl Animatable for NonNegative - where T: Animatable + Clone +impl Animate for NonNegative +where + T: Animate, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegative::) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(NonNegative(self.0.animate(&other.0, procedure)?)) } } @@ -3145,12 +3013,13 @@ impl ToAnimatedZero for NonNegative } } -impl Animatable for GreaterThanOrEqualToOne - where T: Animatable + Clone +impl Animate for GreaterThanOrEqualToOne +where + T: Animate, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - self.0.add_weighted(&other.0, self_portion, other_portion).map(GreaterThanOrEqualToOne::) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(GreaterThanOrEqualToOne(self.0.animate(&other.0, procedure)?)) } } diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index f2e90545e5c..70fa55e2a14 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1115,9 +1115,8 @@ ${helpers.single_keyword_system("font-variant-caps", } pub mod computed_value { - use properties::animated_properties::Animatable; use values::CSSFloat; - use values::animated::{ToAnimatedValue, ToAnimatedZero}; + use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -1137,12 +1136,12 @@ ${helpers.single_keyword_system("font-variant-caps", } } - impl Animatable for T { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { - match (*self, *other) { - (T::Number(ref number), T::Number(ref other)) => - Ok(T::Number(number.add_weighted(other, self_portion, other_portion)?)), + impl Animate for T { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + (&T::Number(ref number), &T::Number(ref other)) => { + Ok(T::Number(number.animate(other, procedure)?)) + }, _ => Err(()), } } diff --git a/components/style/properties/longhand/inherited_table.mako.rs b/components/style/properties/longhand/inherited_table.mako.rs index 1d646cc9410..3cee357a99a 100644 --- a/components/style/properties/longhand/inherited_table.mako.rs +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -26,8 +26,7 @@ ${helpers.single_keyword("caption-side", "top bottom", use values::specified::length::NonNegativeLength; pub mod computed_value { - use properties::animated_properties::Animatable; - use values::animated::{ToAnimatedValue, ToAnimatedZero}; + use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::computed::NonNegativeAu; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -38,15 +37,12 @@ ${helpers.single_keyword("caption-side", "top bottom", } /// https://drafts.csswg.org/css-transitions/#animtype-simple-list - impl Animatable for T { + impl Animate for T { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(T { - horizontal: self.horizontal.add_weighted(&other.horizontal, - self_portion, other_portion)?, - vertical: self.vertical.add_weighted(&other.vertical, - self_portion, other_portion)?, + horizontal: self.horizontal.animate(&other.horizontal, procedure)?, + vertical: self.vertical.animate(&other.vertical, procedure)?, }) } } diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 77575ec2d9d..ad77feb10dd 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -4,8 +4,7 @@ //! Animated types for CSS colors. -use properties::animated_properties::Animatable; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// An animated RGBA color. @@ -39,13 +38,13 @@ impl RGBA { } } -/// Unlike Animatable for computed colors, we don't clamp any component values. +/// Unlike Animate for computed colors, we don't clamp any component values. /// -/// FIXME(nox): Why do computed colors even implement Animatable? -impl Animatable for RGBA { +/// FIXME(nox): Why do computed colors even implement Animate? +impl Animate for RGBA { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?; + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + let mut alpha = self.alpha.animate(&other.alpha, procedure)?; if alpha <= 0. { // Ideally we should return color value that only alpha component is // 0, but this is what current gecko does. @@ -53,15 +52,9 @@ impl Animatable for RGBA { } alpha = alpha.min(1.); - let red = (self.red * self.alpha).add_weighted( - &(other.red * other.alpha), self_portion, other_portion - )? * 1. / alpha; - let green = (self.green * self.alpha).add_weighted( - &(other.green * other.alpha), self_portion, other_portion - )? * 1. / alpha; - let blue = (self.blue * self.alpha).add_weighted( - &(other.blue * other.alpha), self_portion, other_portion - )? * 1. / alpha; + let red = (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha; + let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? * 1. / alpha; + let blue = (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha; Ok(RGBA::new(red, green, blue, alpha)) } @@ -123,9 +116,9 @@ impl Color { } } -impl Animatable for Color { +impl Animate for Color { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { // Common cases are interpolating between two numeric colors, // two currentcolors, and a numeric color and a currentcolor. // @@ -133,35 +126,35 @@ impl Animatable for Color { // equals to one, so it may be broken for additive operation. // To properly support additive color interpolation, we would // need two ratio fields in computed color types. + let (this_weight, other_weight) = procedure.weights(); if self.foreground_ratio == other.foreground_ratio { if self.is_currentcolor() { Ok(Color::currentcolor()) } else { Ok(Color { - color: self.color.add_weighted(&other.color, self_portion, other_portion)?, + color: self.color.animate(&other.color, procedure)?, foreground_ratio: self.foreground_ratio, }) } } else if self.is_currentcolor() && other.is_numeric() { Ok(Color { color: other.color, - foreground_ratio: self_portion as f32, + foreground_ratio: this_weight as f32, }) } else if self.is_numeric() && other.is_currentcolor() { Ok(Color { color: self.color, - foreground_ratio: other_portion as f32, + foreground_ratio: other_weight as f32, }) } else { // For interpolating between two complex colors, we need to // generate colors with effective alpha value. let self_color = self.effective_intermediate_rgba(); let other_color = other.effective_intermediate_rgba(); - let color = self_color.add_weighted(&other_color, self_portion, other_portion)?; + let color = self_color.animate(&other_color, procedure)?; // Then we compute the final foreground ratio, and derive // the final alpha value from the effective alpha value. - let foreground_ratio = self.foreground_ratio - .add_weighted(&other.foreground_ratio, self_portion, other_portion)?; + let foreground_ratio = self.foreground_ratio.animate(&other.foreground_ratio, procedure)?; let alpha = color.alpha / (1. - foreground_ratio); Ok(Color { color: RGBA { @@ -177,7 +170,7 @@ impl Animatable for Color { impl ComputeSquaredDistance for Color { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { - // All comments in add_weighted also applies here. + // All comments from the Animate impl also applies here. if self.foreground_ratio == other.foreground_ratio { if self.is_currentcolor() { Ok(SquaredDistance::Value(0.)) diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index b94a336e16a..e4419ec9304 100644 --- a/components/style/values/animated/effects.rs +++ b/components/style/values/animated/effects.rs @@ -4,14 +4,13 @@ //! Animated types for CSS values related to effects. -use properties::animated_properties::Animatable; use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList; use properties::longhands::filter::computed_value::T as ComputedFilterList; use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList; use std::cmp; #[cfg(not(feature = "gecko"))] use values::Impossible; -use values::animated::{ToAnimatedValue, ToAnimatedZero}; +use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::animated::color::RGBA; use values::computed::{Angle, NonNegativeNumber}; use values::computed::length::{Length, NonNegativeLength}; @@ -66,42 +65,36 @@ impl ToAnimatedValue for ComputedBoxShadowList { } } -impl Animatable for ShadowList +impl Animate for ShadowList where - S: Animatable + Clone + ToAnimatedZero, + S: Animate + Clone + ToAnimatedZero, { #[inline] - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + if procedure == Procedure::Add { + return Ok(ShadowList( + self.0.iter().chain(&other.0).cloned().collect(), + )); + } + // FIXME(nox): Use itertools here, to avoid the need for `unreachable!`. let max_len = cmp::max(self.0.len(), other.0.len()); let mut shadows = Vec::with_capacity(max_len); for i in 0..max_len { shadows.push(match (self.0.get(i), other.0.get(i)) { (Some(shadow), Some(other)) => { - shadow.add_weighted(other, self_portion, other_portion)? + shadow.animate(other, procedure)? }, (Some(shadow), None) => { - shadow.add_weighted(&shadow.to_animated_zero()?, self_portion, other_portion)? + shadow.animate(&shadow.to_animated_zero()?, procedure)? }, (None, Some(shadow)) => { - shadow.to_animated_zero()?.add_weighted(&shadow, self_portion, other_portion)? + shadow.to_animated_zero()?.animate(shadow, procedure)? }, (None, None) => unreachable!(), }); } Ok(ShadowList(shadows)) } - - #[inline] - fn add(&self, other: &Self) -> Result { - Ok(ShadowList( - self.0.iter().cloned().chain(other.0.iter().cloned()).collect(), - )) - } } impl ComputeSquaredDistance for ShadowList @@ -146,20 +139,15 @@ impl ToAnimatedValue for ComputedTextShadowList { } } -impl Animatable for BoxShadow { +impl Animate for BoxShadow { #[inline] - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { if self.inset != other.inset { return Err(()); } Ok(BoxShadow { - base: self.base.add_weighted(&other.base, self_portion, other_portion)?, - spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?, + base: self.base.animate(&other.base, procedure)?, + spread: self.spread.animate(&other.spread, procedure)?, inset: self.inset, }) } @@ -224,19 +212,14 @@ impl ToAnimatedZero for FilterList { } } -impl Animatable for SimpleShadow { +impl Animate for SimpleShadow { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - let color = self.color.add_weighted(&other.color, self_portion, other_portion)?; - let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?; - let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?; - let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?; - + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(SimpleShadow { - color: color, - horizontal: horizontal, - vertical: vertical, - blur: blur, + color: self.color.animate(&other.color, procedure)?, + horizontal: self.horizontal.animate(&other.horizontal, procedure)?, + vertical: self.vertical.animate(&other.vertical, procedure)?, + blur: self.blur.animate(&other.blur, procedure)?, }) } } @@ -245,11 +228,7 @@ impl ToAnimatedZero for SimpleShadow { #[inline] fn to_animated_zero(&self) -> Result { Ok(SimpleShadow { - color: if let Some(color) = self.color.as_ref() { - Some(color.to_animated_zero()?) - } else { - None - }, + color: self.color.to_animated_zero()?, horizontal: self.horizontal.to_animated_zero()?, vertical: self.vertical.to_animated_zero()?, blur: self.blur.to_animated_zero()?, diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index 6883f98ac09..71f6e2bd4db 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -9,6 +9,7 @@ //! module's raison d'ĂȘtre is to ultimately contain all these types. use app_units::Au; +use euclid::{Point2D, Size2D}; use smallvec::SmallVec; use std::cmp::max; use values::computed::Angle as ComputedAngle; @@ -27,6 +28,26 @@ use values::specified::url::SpecifiedUrl; pub mod color; pub mod effects; +/// Animating from one value to another. +pub trait Animate: Sized { + /// Animate a value towards another one, given an animation procedure. + fn animate(&self, other: &Self, procedure: Procedure) -> Result; +} + +/// An animation procedure. +/// +/// https://w3c.github.io/web-animations/#procedures-for-animating-properties +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Procedure { + /// https://w3c.github.io/web-animations/#animation-interpolation + Interpolate { progress: f64 }, + /// https://w3c.github.io/web-animations/#animation-addition + Add, + /// https://w3c.github.io/web-animations/#animation-accumulation + Accumulate { count: u64 }, +} + /// Conversion between computed values and intermediate values for animations. /// /// Notably, colors are represented as four floats during animations. @@ -41,6 +62,108 @@ pub trait ToAnimatedValue { fn from_animated_value(animated: Self::AnimatedValue) -> Self; } +/// Marker trait for computed values with the same representation during animations. +pub trait AnimatedValueAsComputed {} + +/// Returns a value similar to `self` that represents zero. +pub trait ToAnimatedZero: Sized { + /// Returns a value that, when added with an underlying value, will produce the underlying + /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from + /// the zero value to the 'by' value, and then adds the result to the underlying value. + /// + /// This is not the necessarily the same as the initial value of a property. For example, the + /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the + /// underlying value will not produce the underlying value. + fn to_animated_zero(&self) -> Result; +} + +impl Procedure { + /// Returns this procedure as a pair of weights. + /// + /// This is useful for animations that don't animate differently + /// depending on the used procedure. + #[inline] + pub fn weights(self) -> (f64, f64) { + match self { + Procedure::Interpolate { progress } => (1. - progress, progress), + Procedure::Add => (1., 1.), + Procedure::Accumulate { count } => (count as f64, 1.), + } + } +} + +/// https://drafts.csswg.org/css-transitions/#animtype-number +impl Animate for i32 { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32) + } +} + +/// https://drafts.csswg.org/css-transitions/#animtype-number +impl Animate for f32 { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok((*self as f64).animate(&(*other as f64), procedure)? as f32) + } +} + +/// https://drafts.csswg.org/css-transitions/#animtype-number +impl Animate for f64 { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + let (self_weight, other_weight) = procedure.weights(); + Ok(*self * self_weight + *other * other_weight) + } +} + +impl Animate for Option +where + T: Animate, +{ + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self.as_ref(), other.as_ref()) { + (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)), + (None, None) => Ok(None), + _ => Err(()), + } + } +} + +impl Animate for Au { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Au(self.0.animate(&other.0, procedure)?)) + } +} + +impl Animate for Size2D +where + T: Animate + Copy, +{ + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Size2D::new( + self.width.animate(&other.width, procedure)?, + self.height.animate(&other.height, procedure)?, + )) + } +} + +impl Animate for Point2D +where + T: Animate + Copy, +{ + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Point2D::new( + self.x.animate(&other.x, procedure)?, + self.y.animate(&other.y, procedure)?, + )) + } +} + impl ToAnimatedValue for Option where T: ToAnimatedValue, @@ -92,9 +215,6 @@ where } } -/// Marker trait for computed values with the same representation during animations. -pub trait AnimatedValueAsComputed {} - impl AnimatedValueAsComputed for Au {} impl AnimatedValueAsComputed for ComputedAngle {} impl AnimatedValueAsComputed for SpecifiedUrl {} @@ -263,18 +383,6 @@ impl ToAnimatedValue for ComputedMozLength { } } -/// Returns a value similar to `self` that represents zero. -pub trait ToAnimatedZero: Sized { - /// Returns a value that, when added with an underlying value, will produce the underlying - /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from - /// the zero value to the 'by' value, and then adds the result to the underlying value. - /// - /// This is not the necessarily the same as the initial value of a property. For example, the - /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the - /// underlying value will not produce the underlying value. - fn to_animated_zero(&self) -> Result; -} - impl ToAnimatedZero for Au { #[inline] fn to_animated_zero(&self) -> Result { Ok(Au(0)) } @@ -294,3 +402,16 @@ impl ToAnimatedZero for i32 { #[inline] fn to_animated_zero(&self) -> Result { Ok(0) } } + +impl ToAnimatedZero for Option +where + T: ToAnimatedZero, +{ + #[inline] + fn to_animated_zero(&self) -> Result { + match *self { + Some(ref value) => Ok(Some(value.to_animated_zero()?)), + None => Ok(None), + } + } +} diff --git a/components/style/values/computed/angle.rs b/components/style/values/computed/angle.rs index 183230c4aea..3478553666f 100644 --- a/components/style/values/computed/angle.rs +++ b/components/style/values/computed/angle.rs @@ -4,12 +4,11 @@ //! Computed angles. -use properties::animated_properties::Animatable; use std::{f32, f64, fmt}; use std::f64::consts::PI; use style_traits::ToCss; use values::CSSFloat; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// A computed angle. @@ -66,36 +65,34 @@ impl Angle { } /// https://drafts.csswg.org/css-transitions/#animtype-number -impl Animatable for Angle { +impl Animate for Angle { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { (&Angle::Degree(ref this), &Angle::Degree(ref other)) => { - Ok(Angle::Degree(this.add_weighted(other, self_portion, other_portion)?)) + Ok(Angle::Degree(this.animate(other, procedure)?)) }, (&Angle::Gradian(ref this), &Angle::Gradian(ref other)) => { - Ok(Angle::Gradian(this.add_weighted(other, self_portion, other_portion)?)) + Ok(Angle::Gradian(this.animate(other, procedure)?)) }, (&Angle::Turn(ref this), &Angle::Turn(ref other)) => { - Ok(Angle::Turn(this.add_weighted(other, self_portion, other_portion)?)) + Ok(Angle::Turn(this.animate(other, procedure)?)) }, _ => { - self.radians() - .add_weighted(&other.radians(), self_portion, other_portion) - .map(Angle::from_radians) - } + Ok(Angle::from_radians(self.radians().animate(&other.radians(), procedure)?)) + }, } } } impl ToAnimatedZero for Angle { #[inline] - fn to_animated_zero(&self) -> Result { + fn to_animated_zero(&self) -> Result { match *self { - Angle::Degree(value) => Ok(Angle::Degree(value.to_animated_zero()?)), - Angle::Gradian(value) => Ok(Angle::Gradian(value.to_animated_zero()?)), - Angle::Radian(value) => Ok(Angle::Radian(value.to_animated_zero()?)), - Angle::Turn(value) => Ok(Angle::Turn(value.to_animated_zero()?)), + Angle::Degree(ref this) => Ok(Angle::Degree(this.to_animated_zero()?)), + Angle::Gradian(ref this) => Ok(Angle::Gradian(this.to_animated_zero()?)), + Angle::Radian(ref this) => Ok(Angle::Radian(this.to_animated_zero()?)), + Angle::Turn(ref this) => Ok(Angle::Turn(this.to_animated_zero()?)), } } } diff --git a/components/style/values/computed/background.rs b/components/style/values/computed/background.rs index a2111a046e4..82b6ba74f28 100644 --- a/components/style/values/computed/background.rs +++ b/components/style/values/computed/background.rs @@ -4,9 +4,9 @@ //! Computed types for CSS values related to backgrounds. -use properties::animated_properties::{Animatable, RepeatableListAnimatable}; +use properties::animated_properties::RepeatableListAnimatable; use properties::longhands::background_size::computed_value::T as BackgroundSizeList; -use values::animated::{ToAnimatedValue, ToAnimatedZero}; +use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::computed::length::LengthOrPercentageOrAuto; use values::generics::background::BackgroundSize as GenericBackgroundSize; @@ -15,16 +15,16 @@ pub type BackgroundSize = GenericBackgroundSize; impl RepeatableListAnimatable for BackgroundSize {} -impl Animatable for BackgroundSize { - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { +impl Animate for BackgroundSize { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { ( &GenericBackgroundSize::Explicit { width: self_width, height: self_height }, &GenericBackgroundSize::Explicit { width: other_width, height: other_height }, ) => { Ok(GenericBackgroundSize::Explicit { - width: self_width.add_weighted(&other_width, self_portion, other_portion)?, - height: self_height.add_weighted(&other_height, self_portion, other_portion)?, + width: self_width.animate(&other_width, procedure)?, + height: self_height.animate(&other_height, procedure)?, }) } _ => Err(()), diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 2f897f95174..365e377614a 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -11,6 +11,7 @@ use style_traits::ToCss; use style_traits::values::specified::AllowedLengthType; use super::{Number, ToComputedValue, Context, Percentage}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; +use values::animated::ToAnimatedZero; use values::computed::{NonNegativeAu, NonNegativeNumber}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::NonNegative; @@ -72,6 +73,17 @@ pub struct CalcLengthOrPercentage { pub percentage: Option, } +impl ToAnimatedZero for CalcLengthOrPercentage { + #[inline] + fn to_animated_zero(&self) -> Result { + Ok(CalcLengthOrPercentage { + clamping_mode: self.clamping_mode, + length: self.length.to_animated_zero()?, + percentage: self.percentage.to_animated_zero()?, + }) + } +} + impl ComputeSquaredDistance for CalcLengthOrPercentage { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { diff --git a/components/style/values/computed/percentage.rs b/components/style/values/computed/percentage.rs index 6e5e8e0a907..01f8195456c 100644 --- a/components/style/values/computed/percentage.rs +++ b/components/style/values/computed/percentage.rs @@ -4,11 +4,10 @@ //! Computed percentages. -use properties::animated_properties::Animatable; use std::fmt; use style_traits::ToCss; use values::{CSSFloat, serialize_percentage}; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; /// A computed percentage. #[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq, PartialOrd)] @@ -36,10 +35,10 @@ impl Percentage { } /// https://drafts.csswg.org/css-transitions/#animtype-percentage -impl Animatable for Percentage { +impl Animate for Percentage { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32)) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Percentage(self.0.animate(&other.0, procedure)?)) } } diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index fd39b4dd647..30d56278765 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -4,9 +4,8 @@ //! Computed types for text properties. -use properties::animated_properties::Animatable; use values::{CSSInteger, CSSFloat}; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::computed::{NonNegativeAu, NonNegativeNumber}; use values::computed::length::{Length, LengthOrPercentage}; use values::generics::text::InitialLetter as GenericInitialLetter; @@ -25,21 +24,21 @@ pub type WordSpacing = Spacing; /// A computed value for the `line-height` property. pub type LineHeight = GenericLineHeight; -impl Animatable for LineHeight { +impl Animate for LineHeight { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { - match (*self, *other) { - (GenericLineHeight::Length(ref this), GenericLineHeight::Length(ref other)) => { - this.add_weighted(other, self_portion, other_portion).map(GenericLineHeight::Length) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + match (self, other) { + (&GenericLineHeight::Length(ref this), &GenericLineHeight::Length(ref other)) => { + Ok(GenericLineHeight::Length(this.animate(other, procedure)?)) }, - (GenericLineHeight::Number(ref this), GenericLineHeight::Number(ref other)) => { - this.add_weighted(other, self_portion, other_portion).map(GenericLineHeight::Number) + (&GenericLineHeight::Number(ref this), &GenericLineHeight::Number(ref other)) => { + Ok(GenericLineHeight::Number(this.animate(other, procedure)?)) }, - (GenericLineHeight::Normal, GenericLineHeight::Normal) => { + (&GenericLineHeight::Normal, &GenericLineHeight::Normal) => { Ok(GenericLineHeight::Normal) }, #[cfg(feature = "gecko")] - (GenericLineHeight::MozBlockHeight, GenericLineHeight::MozBlockHeight) => { + (&GenericLineHeight::MozBlockHeight, &GenericLineHeight::MozBlockHeight) => { Ok(GenericLineHeight::MozBlockHeight) }, _ => Err(()), diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index 70c543b9a7f..1d4495bf94e 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -4,8 +4,7 @@ //! Computed types for CSS values that are related to transformations. -use properties::animated_properties::Animatable; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::computed::{Length, LengthOrPercentage, Number, Percentage}; use values::generics::transform::TimingFunction as GenericTimingFunction; use values::generics::transform::TransformOrigin as GenericTransformOrigin; @@ -28,13 +27,13 @@ impl TransformOrigin { } } -impl Animatable for TransformOrigin { +impl Animate for TransformOrigin { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { Ok(Self::new( - self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?, - self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?, - self.depth.add_weighted(&other.depth, self_portion, other_portion)?, + self.horizontal.animate(&other.horizontal, procedure)?, + self.vertical.animate(&other.vertical, procedure)?, + self.depth.animate(&other.depth, procedure)?, )) } } diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index cad038df0f1..517062b593b 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -5,10 +5,9 @@ //! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape) //! types that are generic over their `ToCss` implementations. -use properties::animated_properties::Animatable; use std::fmt; use style_traits::{HasViewportPercentage, ToCss}; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::computed::ComputedValueAsSpecified; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; @@ -123,24 +122,21 @@ define_css_keyword_enum!(FillRule: ); add_impls_for_keyword_enum!(FillRule); -impl Animatable for ShapeSource +impl Animate for ShapeSource where - B: Animatable, + B: Animate, T: Clone + PartialEq, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { ( &ShapeSource::Shape(ref this, ref this_box), &ShapeSource::Shape(ref other, ref other_box), ) if this_box == other_box => { - let shape = this.add_weighted(other, self_portion, other_portion)?; - Ok(ShapeSource::Shape(shape, this_box.clone())) + Ok(ShapeSource::Shape( + this.animate(other, procedure)?, + this_box.clone(), + )) }, _ => Err(()), } @@ -178,49 +174,40 @@ impl HasViewportPercentage for ShapeSource { fn has_viewport_percentage(&self) -> bool { false } } -impl Animatable for BasicShape +impl Animate for BasicShape where - H: Animatable, - V: Animatable, - L: Animatable + Copy, + H: Animate, + V: Animate, + L: Animate + Copy, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { (&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => { - Ok(BasicShape::Circle(this.add_weighted(other, self_portion, other_portion)?)) + Ok(BasicShape::Circle(this.animate(other, procedure)?)) }, (&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => { - Ok(BasicShape::Ellipse(this.add_weighted(other, self_portion, other_portion)?)) + Ok(BasicShape::Ellipse(this.animate(other, procedure)?)) }, (&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => { - Ok(BasicShape::Inset(this.add_weighted(other, self_portion, other_portion)?)) + Ok(BasicShape::Inset(this.animate(other, procedure)?)) }, (&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => { - Ok(BasicShape::Polygon(this.add_weighted(other, self_portion, other_portion)?)) + Ok(BasicShape::Polygon(this.animate(other, procedure)?)) }, _ => Err(()), } } } -impl Animatable for InsetRect +impl Animate for InsetRect where - L: Animatable + Copy, + L: Animate + Copy, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - let rect = self.rect.add_weighted(&other.rect, self_portion, other_portion)?; - let round = self.round.add_weighted(&other.round, self_portion, other_portion)?; - Ok(InsetRect { rect, round }) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(InsetRect { + rect: self.rect.animate(&other.rect, procedure)?, + round: self.round.animate(&other.round, procedure)?, + }) } } @@ -238,56 +225,43 @@ impl ToCss for InsetRect } } -impl Animatable for Circle +impl Animate for Circle where - H: Animatable, - V: Animatable, - L: Animatable, + H: Animate, + V: Animate, + L: Animate, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - let position = self.position.add_weighted(&other.position, self_portion, other_portion)?; - let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?; - Ok(Circle { position, radius }) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Circle { + position: self.position.animate(&other.position, procedure)?, + radius: self.radius.animate(&other.radius, procedure)?, + }) } } -impl Animatable for Ellipse +impl Animate for Ellipse where - H: Animatable, - V: Animatable, - L: Animatable, + H: Animate, + V: Animate, + L: Animate, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - let position = self.position.add_weighted(&other.position, self_portion, other_portion)?; - let semiaxis_x = self.semiaxis_x.add_weighted(&other.semiaxis_x, self_portion, other_portion)?; - let semiaxis_y = self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)?; - Ok(Ellipse { position, semiaxis_x, semiaxis_y }) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Ellipse { + position: self.position.animate(&other.position, procedure)?, + semiaxis_x: self.semiaxis_x.animate(&other.semiaxis_x, procedure)?, + semiaxis_y: self.semiaxis_y.animate(&other.semiaxis_y, procedure)?, + }) } } -impl Animatable for ShapeRadius +impl Animate for ShapeRadius where - L: Animatable, + L: Animate, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { match (self, other) { (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { - Ok(ShapeRadius::Length(this.add_weighted(other, self_portion, other_portion)?)) + Ok(ShapeRadius::Length(this.animate(other, procedure)?)) }, _ => Err(()), } @@ -299,16 +273,11 @@ impl Default for ShapeRadius { fn default() -> Self { ShapeRadius::ClosestSide } } -impl Animatable for Polygon +impl Animate for Polygon where - L: Animatable, + L: Animate, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { if self.fill != other.fill { return Err(()); } @@ -316,9 +285,10 @@ where return Err(()); } let coordinates = self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| { - let x = this.0.add_weighted(&other.0, self_portion, other_portion)?; - let y = this.1.add_weighted(&other.1, self_portion, other_portion)?; - Ok((x, y)) + Ok(( + this.0.animate(&other.0, procedure)?, + this.1.animate(&other.1, procedure)?, + )) }).collect::, _>>()?; Ok(Polygon { fill: self.fill, coordinates }) } diff --git a/components/style/values/generics/border.rs b/components/style/values/generics/border.rs index 78faf1de050..1ac6f7d5433 100644 --- a/components/style/values/generics/border.rs +++ b/components/style/values/generics/border.rs @@ -5,9 +5,9 @@ //! Generic types for CSS values related to borders. use euclid::Size2D; -use properties::animated_properties::Animatable; use std::fmt; use style_traits::ToCss; +use values::animated::{Animate, Procedure}; use values::generics::rect::Rect; /// A generic value for a single side of a `border-image-width` property. @@ -113,21 +113,17 @@ impl BorderRadius } } -impl Animatable for BorderRadius +impl Animate for BorderRadius where - L: Animatable + Copy, + L: Animate + Copy, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - let tl = self.top_left.add_weighted(&other.top_left, self_portion, other_portion)?; - let tr = self.top_right.add_weighted(&other.top_right, self_portion, other_portion)?; - let br = self.bottom_right.add_weighted(&other.bottom_right, self_portion, other_portion)?; - let bl = self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion)?; - Ok(BorderRadius::new(tl, tr, br, bl)) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(BorderRadius::new( + self.top_left.animate(&other.top_left, procedure)?, + self.top_right.animate(&other.top_right, procedure)?, + self.bottom_right.animate(&other.bottom_right, procedure)?, + self.bottom_left.animate(&other.bottom_left, procedure)?, + )) } } @@ -163,18 +159,13 @@ impl From for BorderCornerRadius { } } -impl Animatable for BorderCornerRadius +impl Animate for BorderCornerRadius where - L: Animatable + Copy, + L: Animate + Copy, { #[inline] - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - Ok(BorderCornerRadius(self.0.add_weighted(&other.0, self_portion, other_portion)?)) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(BorderCornerRadius(self.0.animate(&other.0, procedure)?)) } } diff --git a/components/style/values/generics/rect.rs b/components/style/values/generics/rect.rs index 89ce196267a..6eaaa5a48c4 100644 --- a/components/style/values/generics/rect.rs +++ b/components/style/values/generics/rect.rs @@ -6,9 +6,9 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use properties::animated_properties::Animatable; use std::fmt; use style_traits::{ToCss, ParseError}; +use values::animated::{Animate, Procedure}; /// A CSS value made of four components, where its `ToCss` impl will try to /// serialize as few components as possible, like for example in `border-width`. @@ -52,21 +52,17 @@ impl Rect } } -impl Animatable for Rect +impl Animate for Rect where - L: Animatable, + L: Animate, { - fn add_weighted( - &self, - other: &Self, - self_portion: f64, - other_portion: f64, - ) -> Result { - let first = self.0.add_weighted(&other.0, self_portion, other_portion)?; - let second = self.1.add_weighted(&other.1, self_portion, other_portion)?; - let third = self.2.add_weighted(&other.2, self_portion, other_portion)?; - let fourth = self.3.add_weighted(&other.3, self_portion, other_portion)?; - Ok(Rect(first, second, third, fourth)) + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + Ok(Rect( + self.0.animate(&other.0, procedure)?, + self.1.animate(&other.1, procedure)?, + self.2.animate(&other.2, procedure)?, + self.3.animate(&other.3, procedure)?, + )) } } diff --git a/components/style/values/generics/text.rs b/components/style/values/generics/text.rs index a3879602bdc..f78ab594c5e 100644 --- a/components/style/values/generics/text.rs +++ b/components/style/values/generics/text.rs @@ -7,9 +7,8 @@ use app_units::Au; use cssparser::Parser; use parser::ParserContext; -use properties::animated_properties::Animatable; use style_traits::ParseError; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// A generic value for the `initial-letter` property. @@ -72,18 +71,19 @@ impl Spacing { } } -impl Animatable for Spacing - where Value: Animatable + From, +impl Animate for Spacing +where + Value: Animate + From, { #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn animate(&self, other: &Self, procedure: Procedure) -> Result { if let (&Spacing::Normal, &Spacing::Normal) = (self, other) { return Ok(Spacing::Normal); } let zero = Value::from(Au(0)); let this = self.value().unwrap_or(&zero); let other = other.value().unwrap_or(&zero); - this.add_weighted(other, self_portion, other_portion).map(Spacing::Value) + Ok(Spacing::Value(this.animate(other, procedure)?)) } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index c0b77855565..c2763e85bba 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -105,7 +105,7 @@ use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandI use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId}; use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder}; use style::properties::PROHIBIT_DISPLAY_CONTENTS; -use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue}; +use style::properties::animated_properties::{AnimatableLonghand, AnimationValue}; use style::properties::animated_properties::compare_property_priority; use style::properties::parse_one_declaration_into; use style::rule_tree::StyleSource; @@ -128,7 +128,7 @@ use style::traversal::{DomTraversal, TraversalDriver}; use style::traversal::resolve_style; use style::traversal_flags::{TraversalFlags, self}; use style::values::{CustomIdent, KeyframesName}; -use style::values::animated::ToAnimatedZero; +use style::values::animated::{Animate, Procedure, ToAnimatedZero}; use style::values::computed::Context; use style::values::distance::ComputeSquaredDistance; use style_traits::{PARSING_MODE_DEFAULT, ToCss}; @@ -319,7 +319,7 @@ pub extern "C" fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValue { let from_value = AnimationValue::as_arc(&from); let to_value = AnimationValue::as_arc(&to); - if let Ok(value) = from_value.interpolate(to_value, progress) { + if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress }) { Arc::new(value).into_strong() } else { RawServoAnimationValueStrong::null() @@ -332,7 +332,7 @@ pub extern "C" fn Servo_AnimationValues_IsInterpolable(from: RawServoAnimationVa -> bool { let from_value = AnimationValue::as_arc(&from); let to_value = AnimationValue::as_arc(&to); - from_value.interpolate(to_value, 0.5).is_ok() + from_value.animate(to_value, Procedure::Interpolate { progress: 0.5 }).is_ok() } #[no_mangle] @@ -342,7 +342,7 @@ pub extern "C" fn Servo_AnimationValues_Add(a: RawServoAnimationValueBorrowed, { let a_value = AnimationValue::as_arc(&a); let b_value = AnimationValue::as_arc(&b); - if let Ok(value) = a_value.add(b_value) { + if let Ok(value) = a_value.animate(b_value, Procedure::Add) { Arc::new(value).into_strong() } else { RawServoAnimationValueStrong::null() @@ -357,7 +357,7 @@ pub extern "C" fn Servo_AnimationValues_Accumulate(a: RawServoAnimationValueBorr { let a_value = AnimationValue::as_arc(&a); let b_value = AnimationValue::as_arc(&b); - if let Ok(value) = a_value.accumulate(b_value, count) { + if let Ok(value) = a_value.animate(b_value, Procedure::Accumulate { count }) { Arc::new(value).into_strong() } else { RawServoAnimationValueStrong::null() @@ -463,12 +463,16 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa CompositeOperation::Add => { debug_assert!(need_underlying_value, "Should have detected we need an underlying value"); - underlying_value.as_ref().unwrap().add(keyframe_value).ok() + underlying_value.as_ref().unwrap().animate(keyframe_value, Procedure::Add).ok() }, CompositeOperation::Accumulate => { debug_assert!(need_underlying_value, "Should have detected we need an underlying value"); - underlying_value.as_ref().unwrap().accumulate(keyframe_value, 1).ok() + underlying_value + .as_ref() + .unwrap() + .animate(keyframe_value, Procedure::Accumulate { count: 1 }) + .ok() }, _ => None, } @@ -507,11 +511,17 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa -> Option { let count = computed_timing.mCurrentIteration; match composited_value { - Some(endpoint) => last_value.accumulate(&endpoint, count) - .ok() - .or(Some(endpoint)), - None => last_value.accumulate(keyframe_value.unwrap(), count) - .ok(), + Some(endpoint) => { + last_value + .animate(&endpoint, Procedure::Accumulate { count }) + .ok() + .or(Some(endpoint)) + }, + None => { + last_value + .animate(keyframe_value.unwrap(), Procedure::Accumulate { count }) + .ok() + }, } }; @@ -535,12 +545,12 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa return; } - let position = unsafe { + let pos = unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }; - if let Ok(value) = from_value.interpolate(to_value, position) { + if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress: pos }) { value_map.insert(property, value); - } else if position < 0.5 { + } else if pos < 0.5 { value_map.insert(property, from_value.clone()); } else { value_map.insert(property, to_value.clone()); @@ -2080,8 +2090,8 @@ pub extern "C" fn Servo_MatrixTransform_Operate(matrix_operator: MatrixTransform let from = ComputedMatrix::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix")); let to = ComputedMatrix::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix")); let result = match matrix_operator { - Interpolate => from.interpolate(&to, progress), - Accumulate => from.accumulate(&to, progress as u64), + Interpolate => from.animate(&to, Procedure::Interpolate { progress }), + Accumulate => from.animate(&to, Procedure::Accumulate { count: progress as u64 }), }; let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix"); diff --git a/tests/unit/style/animated_properties.rs b/tests/unit/style/animated_properties.rs index f68b89f12e5..7043bd0a8ca 100644 --- a/tests/unit/style/animated_properties.rs +++ b/tests/unit/style/animated_properties.rs @@ -4,16 +4,17 @@ use app_units::Au; use cssparser::RGBA; -use style::properties::animated_properties::Animatable; use style::properties::longhands::transform::computed_value::ComputedOperation as TransformOperation; use style::properties::longhands::transform::computed_value::T as TransformList; -use style::values::animated::ToAnimatedValue; +use style::values::animated::{Animate, Procedure, ToAnimatedValue}; use style::values::computed::Percentage; fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA { let from = from.to_animated_value(); let to = to.to_animated_value(); - RGBA::from_animated_value(from.interpolate(&to, progress).unwrap()) + RGBA::from_animated_value( + from.animate(&to, Procedure::Interpolate { progress }).unwrap(), + ) } // Color @@ -74,11 +75,14 @@ fn test_transform_interpolation_on_translate() { TransformOperation::Translate(LengthOrPercentage::Length(Au(100)), LengthOrPercentage::Length(Au(0)), Au(75))])); - assert_eq!(from.interpolate(&to, 0.5).unwrap(), - TransformList(Some(vec![ - TransformOperation::Translate(LengthOrPercentage::Length(Au(50)), - LengthOrPercentage::Length(Au(50)), - Au(50))]))); + assert_eq!( + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), + TransformList(Some(vec![TransformOperation::Translate( + LengthOrPercentage::Length(Au(50)), + LengthOrPercentage::Length(Au(50)), + Au(50), + )])) + ); let from = TransformList(Some(vec![TransformOperation::Translate( LengthOrPercentage::Percentage(Percentage(0.5)), @@ -90,7 +94,7 @@ fn test_transform_interpolation_on_translate() { LengthOrPercentage::Length(Au(50)), Au(75))])); assert_eq!( - from.interpolate(&to, 0.5).unwrap(), + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), TransformList(Some(vec![TransformOperation::Translate( // calc(50px + 25%) LengthOrPercentage::Calc(CalcLengthOrPercentage::new(Au(50), Some(Percentage(0.25)))), @@ -105,8 +109,10 @@ fn test_transform_interpolation_on_translate() { fn test_transform_interpolation_on_scale() { let from = TransformList(Some(vec![TransformOperation::Scale(1.0, 2.0, 1.0)])); let to = TransformList(Some(vec![TransformOperation::Scale(2.0, 4.0, 2.0)])); - assert_eq!(from.interpolate(&to, 0.5).unwrap(), - TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)]))); + assert_eq!( + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), + TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)])) + ); } #[test] @@ -117,9 +123,12 @@ fn test_transform_interpolation_on_rotate() { Angle::from_radians(0.0))])); let to = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::from_radians(100.0))])); - assert_eq!(from.interpolate(&to, 0.5).unwrap(), - TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0, - Angle::from_radians(50.0))]))); + assert_eq!( + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), + TransformList(Some(vec![ + TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::from_radians(50.0)), + ])) + ); } #[test] @@ -130,9 +139,13 @@ fn test_transform_interpolation_on_skew() { Angle::from_radians(100.0))])); let to = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(100.0), Angle::from_radians(0.0))])); - assert_eq!(from.interpolate(&to, 0.5).unwrap(), - TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(50.0), - Angle::from_radians(50.0))]))); + assert_eq!( + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), + TransformList(Some(vec![TransformOperation::Skew( + Angle::from_radians(50.0), + Angle::from_radians(50.0), + )])) + ); } #[test] @@ -146,7 +159,7 @@ fn test_transform_interpolation_on_mismatched_lists() { LengthOrPercentage::Length(Au(0)), Au(0))])); assert_eq!( - from.interpolate(&to, 0.5).unwrap(), + from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(), TransformList(Some(vec![TransformOperation::InterpolateMatrix { from_list: from.clone(), to_list: to.clone(), diff --git a/tests/unit/style/stylist.rs b/tests/unit/style/stylist.rs index 3ae3715e9c7..a2e18b4906e 100644 --- a/tests/unit/style/stylist.rs +++ b/tests/unit/style/stylist.rs @@ -5,16 +5,14 @@ use cssparser::SourceLocation; use euclid::ScaleFactor; use euclid::TypedSize2D; -use html5ever::LocalName; use selectors::parser::{AncestorHashes, Selector}; -use selectors::parser::LocalName as LocalNameSelector; use servo_arc::Arc; use servo_atoms::Atom; use style::context::QuirksMode; use style::media_queries::{Device, MediaType}; use style::properties::{PropertyDeclarationBlock, PropertyDeclaration}; use style::properties::{longhands, Importance}; -use style::selector_map::{self, SelectorMap}; +use style::selector_map::SelectorMap; use style::selector_parser::{SelectorImpl, SelectorParser}; use style::shared_lock::SharedRwLock; use style::stylesheets::StyleRule;