style: Hook up linear easing calculation for servo and expose it to C++

Differential Revision: https://phabricator.services.mozilla.com/D146838
This commit is contained in:
David Shin 2022-06-07 11:51:24 +00:00 committed by Martin Robinson
parent 6326a384a8
commit b31be826c4
3 changed files with 64 additions and 11 deletions

View file

@ -10,6 +10,7 @@
use crate::bezier::Bezier; use crate::bezier::Bezier;
use crate::context::{CascadeInputs, SharedStyleContext}; use crate::context::{CascadeInputs, SharedStyleContext};
use crate::dom::{OpaqueNode, TDocument, TElement, TNode}; use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
use crate::piecewise_linear::PiecewiseLinearFunction;
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; 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_direction::computed_value::single_value::T as AnimationDirection;
use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode; 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::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
use crate::stylesheets::layer_rule::LayerOrder; use crate::stylesheets::layer_rule::LayerOrder;
use crate::values::animated::{Animate, Procedure}; use crate::values::animated::{Animate, Procedure};
use crate::values::computed::easing::ComputedLinearStop;
use crate::values::computed::{Time, TimingFunction}; use crate::values::computed::{Time, TimingFunction};
use crate::values::generics::box_::AnimationIterationCount; use crate::values::generics::box_::AnimationIterationCount;
use crate::values::generics::easing::{ use crate::values::generics::easing::{
@ -127,9 +129,16 @@ impl PropertyAnimation {
(current_step as f64) / (jumps as f64) (current_step as f64) / (jumps as f64)
}, },
GenericTimingFunction::LinearFunction(_elements) => { GenericTimingFunction::LinearFunction(elements) => {
// TODO(dshin): To be implemented (bug 1764126) // TODO(dshin): For servo, which uses this code path, constructing the function
progress // 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) => { GenericTimingFunction::Keyword(keyword) => {
let bezier = match keyword { let bezier = match keyword {
@ -367,7 +376,8 @@ impl ComputedKeyframe {
let properties_changed_in_step = step.declarations.longhands().clone(); let properties_changed_in_step = step.declarations.longhands().clone();
let step_timing_function = step.timing_function.clone(); let step_timing_function = step.timing_function.clone();
let step_style = step.resolve_style(element, context, base_style, resolver); 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 = { let values = {
// If a value is not set in a property declaration we use the value from // If a value is not set in a property declaration we use the value from

View file

@ -13,7 +13,7 @@ type ValueType = CSSFloat;
/// a single entry in a piecewise linear function. /// a single entry in a piecewise linear function.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
struct Entry { struct PiecewiseLinearFunctionEntry {
x: ValueType, x: ValueType,
y: ValueType, y: ValueType,
} }
@ -22,12 +22,20 @@ struct Entry {
#[derive(Default)] #[derive(Default)]
#[repr(C)] #[repr(C)]
pub struct PiecewiseLinearFunction { pub struct PiecewiseLinearFunction {
entries: crate::OwnedSlice<Entry>, entries: crate::OwnedSlice<PiecewiseLinearFunctionEntry>,
} }
/// Parameters to define one linear stop.
pub type PiecewiseLinearFunctionBuildParameters = (CSSFloat, Option<CSSFloat>, Option<CSSFloat>);
impl PiecewiseLinearFunction { impl PiecewiseLinearFunction {
/// Interpolate y value given x and two points. The linear function will be rooted at the asymptote. /// 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 // Line is vertical, or the two points are identical. Avoid infinite slope by pretending
// the line is flat. // the line is flat.
if prev.x.approx_eq(&next.x) { if prev.x.approx_eq(&next.x) {
@ -82,6 +90,18 @@ impl PiecewiseLinearFunction {
} }
unreachable!("Input is supposed to be within the entries' min & max!"); 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: Iter) -> Self
where
Iter: Iterator<Item = PiecewiseLinearFunctionBuildParameters> + 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. /// 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() 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<ValueType>) { fn create_entry(&mut self, y: ValueType, x: Option<ValueType>) {
let x = match x { let x = match x {
Some(x) if x.is_finite() => x, Some(x) if x.is_finite() => x,
@ -148,7 +177,7 @@ impl PiecewiseLinearFunctionBuilder {
if self.entries.len() == 1 { if self.entries.len() == 1 {
// Don't bother resolving anything. // Don't bother resolving anything.
return PiecewiseLinearFunction { return PiecewiseLinearFunction {
entries: crate::OwnedSlice::from_slice(&[Entry { entries: crate::OwnedSlice::from_slice(&[PiecewiseLinearFunctionEntry {
x: 0., x: 0.,
y: self.entries[0].y, 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)); .get_or_insert(self.largest_x.filter(|x| x > &1.0).unwrap_or(1.0));
let mut result = Vec::with_capacity(self.entries.len()); let mut result = Vec::with_capacity(self.entries.len());
result.push(Entry { result.push(PiecewiseLinearFunctionEntry {
x: self.entries[0].x.unwrap(), x: self.entries[0].x.unwrap(),
y: self.entries[0].y, y: self.entries[0].y,
}); });
@ -199,14 +228,14 @@ impl PiecewiseLinearFunctionBuilder {
.enumerate() .enumerate()
.map(|(j, e)| { .map(|(j, e)| {
debug_assert!(e.x.is_none(), "Expected an entry with x undefined!"); debug_assert!(e.x.is_none(), "Expected an entry with x undefined!");
Entry { PiecewiseLinearFunctionEntry {
x: increment * (j + 1) as ValueType + start_x, x: increment * (j + 1) as ValueType + start_x,
y: e.y, y: e.y,
} }
}), }),
); );
} }
result.push(Entry { result.push(PiecewiseLinearFunctionEntry {
x: e.x.unwrap(), x: e.x.unwrap(),
y: e.y, y: e.y,
}); });

View file

@ -4,6 +4,7 @@
//! Computed types for CSS Easing functions. //! Computed types for CSS Easing functions.
use crate::piecewise_linear::PiecewiseLinearFunctionBuildParameters;
use crate::values::computed::{Integer, Number, Percentage}; use crate::values::computed::{Integer, Number, Percentage};
use crate::values::generics::easing; use crate::values::generics::easing;
@ -15,3 +16,16 @@ pub type TimingFunction = ComputedTimingFunction;
/// A computed linear easing entry. /// A computed linear easing entry.
pub type ComputedLinearStop = easing::LinearStop<Number, Percentage>; pub type ComputedLinearStop = easing::LinearStop<Number, Percentage>;
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),
)
}
}