diff --git a/components/style/animation.rs b/components/style/animation.rs index fa5888ffeeb..7bcd5c62067 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -10,6 +10,7 @@ use crate::bezier::Bezier; use crate::context::{CascadeInputs, SharedStyleContext}; use crate::dom::{OpaqueNode, TDocument, TElement, TNode}; +use crate::piecewise_linear::PiecewiseLinearFunction; use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode; @@ -26,6 +27,7 @@ use crate::style_resolver::StyleResolverForElement; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use crate::stylesheets::layer_rule::LayerOrder; use crate::values::animated::{Animate, Procedure}; +use crate::values::computed::easing::ComputedLinearStop; use crate::values::computed::{Time, TimingFunction}; use crate::values::generics::box_::AnimationIterationCount; use crate::values::generics::easing::{ @@ -127,9 +129,16 @@ impl PropertyAnimation { (current_step as f64) / (jumps as f64) }, - GenericTimingFunction::LinearFunction(_elements) => { - // TODO(dshin): To be implemented (bug 1764126) - progress + GenericTimingFunction::LinearFunction(elements) => { + // TODO(dshin): For servo, which uses this code path, constructing the function + // every time the animation advances seem... expensive. + PiecewiseLinearFunction::from_iter( + elements + .iter() + .map(ComputedLinearStop::to_piecewise_linear_build_parameters), + ) + .at(progress as f32) + .into() }, GenericTimingFunction::Keyword(keyword) => { let bezier = match keyword { @@ -367,7 +376,8 @@ impl ComputedKeyframe { let properties_changed_in_step = step.declarations.longhands().clone(); let step_timing_function = step.timing_function.clone(); let step_style = step.resolve_style(element, context, base_style, resolver); - let timing_function = step_timing_function.unwrap_or_else(|| default_timing_function.clone()); + let timing_function = + step_timing_function.unwrap_or_else(|| default_timing_function.clone()); let values = { // If a value is not set in a property declaration we use the value from diff --git a/components/style/piecewise_linear.rs b/components/style/piecewise_linear.rs index 0ac0220e44a..61bb6e31837 100644 --- a/components/style/piecewise_linear.rs +++ b/components/style/piecewise_linear.rs @@ -13,7 +13,7 @@ type ValueType = CSSFloat; /// a single entry in a piecewise linear function. #[derive(Clone, Copy)] #[repr(C)] -struct Entry { +struct PiecewiseLinearFunctionEntry { x: ValueType, y: ValueType, } @@ -22,12 +22,20 @@ struct Entry { #[derive(Default)] #[repr(C)] pub struct PiecewiseLinearFunction { - entries: crate::OwnedSlice, + entries: crate::OwnedSlice, } +/// Parameters to define one linear stop. +pub type PiecewiseLinearFunctionBuildParameters = (CSSFloat, Option, Option); + impl PiecewiseLinearFunction { /// Interpolate y value given x and two points. The linear function will be rooted at the asymptote. - fn interpolate(x: ValueType, prev: Entry, next: Entry, asymptote: &Entry) -> ValueType { + fn interpolate( + x: ValueType, + prev: PiecewiseLinearFunctionEntry, + next: PiecewiseLinearFunctionEntry, + asymptote: &PiecewiseLinearFunctionEntry, + ) -> ValueType { // Line is vertical, or the two points are identical. Avoid infinite slope by pretending // the line is flat. if prev.x.approx_eq(&next.x) { @@ -82,6 +90,18 @@ impl PiecewiseLinearFunction { } unreachable!("Input is supposed to be within the entries' min & max!"); } + + /// Create the piecewise linear function from an iterator that generates the parameter tuple. + pub fn from_iter(iter: Iter) -> Self + where + Iter: Iterator + ExactSizeIterator, + { + let mut builder = PiecewiseLinearFunctionBuilder::with_capacity(iter.len()); + for (y, x_start, x_end) in iter { + builder = builder.push(y, x_start, x_end); + } + builder.build() + } } /// Entry of a piecewise linear function while building, where the calculation of x value can be deferred. @@ -105,6 +125,15 @@ impl PiecewiseLinearFunctionBuilder { PiecewiseLinearFunctionBuilder::default() } + /// Create a builder for a known amount of linear stop entries. + pub fn with_capacity(len: usize) -> Self { + PiecewiseLinearFunctionBuilder { + largest_x: None, + smallest_x: None, + entries: Vec::with_capacity(len), + } + } + fn create_entry(&mut self, y: ValueType, x: Option) { let x = match x { Some(x) if x.is_finite() => x, @@ -148,7 +177,7 @@ impl PiecewiseLinearFunctionBuilder { if self.entries.len() == 1 { // Don't bother resolving anything. return PiecewiseLinearFunction { - entries: crate::OwnedSlice::from_slice(&[Entry { + entries: crate::OwnedSlice::from_slice(&[PiecewiseLinearFunctionEntry { x: 0., y: self.entries[0].y, }]), @@ -173,7 +202,7 @@ impl PiecewiseLinearFunctionBuilder { .get_or_insert(self.largest_x.filter(|x| x > &1.0).unwrap_or(1.0)); let mut result = Vec::with_capacity(self.entries.len()); - result.push(Entry { + result.push(PiecewiseLinearFunctionEntry { x: self.entries[0].x.unwrap(), y: self.entries[0].y, }); @@ -199,14 +228,14 @@ impl PiecewiseLinearFunctionBuilder { .enumerate() .map(|(j, e)| { debug_assert!(e.x.is_none(), "Expected an entry with x undefined!"); - Entry { + PiecewiseLinearFunctionEntry { x: increment * (j + 1) as ValueType + start_x, y: e.y, } }), ); } - result.push(Entry { + result.push(PiecewiseLinearFunctionEntry { x: e.x.unwrap(), y: e.y, }); diff --git a/components/style/values/computed/easing.rs b/components/style/values/computed/easing.rs index 3db7261e2ea..7bee63f0346 100644 --- a/components/style/values/computed/easing.rs +++ b/components/style/values/computed/easing.rs @@ -4,6 +4,7 @@ //! Computed types for CSS Easing functions. +use crate::piecewise_linear::PiecewiseLinearFunctionBuildParameters; use crate::values::computed::{Integer, Number, Percentage}; use crate::values::generics::easing; @@ -15,3 +16,16 @@ pub type TimingFunction = ComputedTimingFunction; /// A computed linear easing entry. pub type ComputedLinearStop = easing::LinearStop; + +impl ComputedLinearStop { + /// Convert this type to entries that can be used to build PiecewiseLinearFunction. + pub fn to_piecewise_linear_build_parameters( + x: &ComputedLinearStop, + ) -> PiecewiseLinearFunctionBuildParameters { + ( + x.output, + x.input_start.into_rust().map(|x| x.0), + x.input_end.into_rust().map(|x| x.0), + ) + } +}