mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #17001 - birtles:accumulative-animation, r=hiro
Support accumulative animation These are the Servo side changes for [Mozilla bug 1353202](https://bugzilla.mozilla.org/show_bug.cgi?id=1353202). <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17001) <!-- Reviewable:end -->
This commit is contained in:
commit
7f80d9cb45
6 changed files with 2138 additions and 1619 deletions
|
@ -2940,6 +2940,8 @@ cfg_if! {
|
|||
pub static nsGkAtoms_foreignObject: *mut nsIAtom;
|
||||
#[link_name = "_ZN9nsGkAtoms12fractalNoiseE"]
|
||||
pub static nsGkAtoms_fractalNoise: *mut nsIAtom;
|
||||
#[link_name = "_ZN9nsGkAtoms2frE"]
|
||||
pub static nsGkAtoms_fr: *mut nsIAtom;
|
||||
#[link_name = "_ZN9nsGkAtoms2fxE"]
|
||||
pub static nsGkAtoms_fx: *mut nsIAtom;
|
||||
#[link_name = "_ZN9nsGkAtoms2fyE"]
|
||||
|
@ -7957,6 +7959,8 @@ cfg_if! {
|
|||
pub static nsGkAtoms_foreignObject: *mut nsIAtom;
|
||||
#[link_name = "?fractalNoise@nsGkAtoms@@2PEAVnsIAtom@@EA"]
|
||||
pub static nsGkAtoms_fractalNoise: *mut nsIAtom;
|
||||
#[link_name = "?fr@nsGkAtoms@@2PEAVnsIAtom@@EA"]
|
||||
pub static nsGkAtoms_fr: *mut nsIAtom;
|
||||
#[link_name = "?fx@nsGkAtoms@@2PEAVnsIAtom@@EA"]
|
||||
pub static nsGkAtoms_fx: *mut nsIAtom;
|
||||
#[link_name = "?fy@nsGkAtoms@@2PEAVnsIAtom@@EA"]
|
||||
|
@ -12974,6 +12978,8 @@ cfg_if! {
|
|||
pub static nsGkAtoms_foreignObject: *mut nsIAtom;
|
||||
#[link_name = "\x01?fractalNoise@nsGkAtoms@@2PAVnsIAtom@@A"]
|
||||
pub static nsGkAtoms_fractalNoise: *mut nsIAtom;
|
||||
#[link_name = "\x01?fr@nsGkAtoms@@2PAVnsIAtom@@A"]
|
||||
pub static nsGkAtoms_fr: *mut nsIAtom;
|
||||
#[link_name = "\x01?fx@nsGkAtoms@@2PAVnsIAtom@@A"]
|
||||
pub static nsGkAtoms_fx: *mut nsIAtom;
|
||||
#[link_name = "\x01?fy@nsGkAtoms@@2PAVnsIAtom@@A"]
|
||||
|
@ -17994,6 +18000,8 @@ macro_rules! atom {
|
|||
{ unsafe { $crate::string_cache::atom_macro::atom_from_static($crate::string_cache::atom_macro::nsGkAtoms_foreignObject as *mut _) } };
|
||||
("fractalNoise") =>
|
||||
{ unsafe { $crate::string_cache::atom_macro::atom_from_static($crate::string_cache::atom_macro::nsGkAtoms_fractalNoise as *mut _) } };
|
||||
("fr") =>
|
||||
{ unsafe { $crate::string_cache::atom_macro::atom_from_static($crate::string_cache::atom_macro::nsGkAtoms_fr as *mut _) } };
|
||||
("fx") =>
|
||||
{ unsafe { $crate::string_cache::atom_macro::atom_from_static($crate::string_cache::atom_macro::nsGkAtoms_fx as *mut _) } };
|
||||
("fy") =>
|
||||
|
|
|
@ -36,6 +36,7 @@ use gecko_bindings::structs::FontFamilyList;
|
|||
use gecko_bindings::structs::FontFamilyType;
|
||||
use gecko_bindings::structs::FontSizePrefs;
|
||||
use gecko_bindings::structs::GeckoFontMetrics;
|
||||
use gecko_bindings::structs::IterationCompositeOperation;
|
||||
use gecko_bindings::structs::Keyframe;
|
||||
use gecko_bindings::structs::ServoBundledURI;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
|
@ -2261,8 +2262,12 @@ extern "C" {
|
|||
property: nsCSSPropertyID,
|
||||
animation_segment:
|
||||
RawGeckoAnimationPropertySegmentBorrowed,
|
||||
last_segment:
|
||||
RawGeckoAnimationPropertySegmentBorrowed,
|
||||
computed_timing:
|
||||
RawGeckoComputedTimingBorrowed);
|
||||
RawGeckoComputedTimingBorrowed,
|
||||
iteration_composite:
|
||||
IterationCompositeOperation);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_DeclarationBlock_PropertyIsSet(declarations:
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -680,6 +680,15 @@ pub trait Animatable: Sized {
|
|||
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, ()> {
|
||||
self.add_weighted(other, count as f64, 1.0)
|
||||
}
|
||||
|
||||
/// Compute distance between a value and another for a given property.
|
||||
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { Err(()) }
|
||||
|
||||
|
@ -1364,11 +1373,16 @@ impl Animatable for FontWeight {
|
|||
/// https://drafts.csswg.org/css-fonts/#font-stretch-prop
|
||||
impl Animatable for FontStretch {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
||||
-> Result<Self, ()>
|
||||
{
|
||||
let from = f64::from(*self);
|
||||
let to = f64::from(*other);
|
||||
let interpolated_mapped_index = ((from * self_portion + to * other_portion) + 0.5).floor();
|
||||
Ok(interpolated_mapped_index.into())
|
||||
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;
|
||||
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1401,11 +1415,11 @@ impl From<FontStretch> for f64 {
|
|||
impl Into<FontStretch> for f64 {
|
||||
fn into(self) -> FontStretch {
|
||||
use properties::longhands::font_stretch::computed_value::T::*;
|
||||
debug_assert!(self >= 1.0 && self <= 9.0);
|
||||
let index = (self + 0.5).floor().min(9.0).max(1.0);
|
||||
static FONT_STRETCH_ENUM_MAP: [FontStretch; 9] =
|
||||
[ ultra_condensed, extra_condensed, condensed, semi_condensed, normal,
|
||||
semi_expanded, expanded, extra_expanded, ultra_expanded ];
|
||||
FONT_STRETCH_ENUM_MAP[(self - 1.0) as usize]
|
||||
FONT_STRETCH_ENUM_MAP[(index - 1.0) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1659,6 +1673,21 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
|
|||
result
|
||||
}
|
||||
|
||||
/// A wrapper for calling add_weighted that interpolates the distance of the two values from
|
||||
/// an initial_value and uses that to produce an interpolated value.
|
||||
/// This is used for values such as 'scale' where the initial value is 1 and where if we interpolate
|
||||
/// the absolute values, we will produce odd results for accumulation.
|
||||
fn add_weighted_with_initial_val<T: Animatable>(a: &T,
|
||||
b: &T,
|
||||
a_portion: f64,
|
||||
b_portion: f64,
|
||||
initial_val: &T) -> Result<T, ()> {
|
||||
let a = try!(a.add_weighted(&initial_val, 1.0, -1.0));
|
||||
let b = try!(b.add_weighted(&initial_val, 1.0, -1.0));
|
||||
let result = try!(a.add_weighted(&b, a_portion, b_portion));
|
||||
result.add_weighted(&initial_val, 1.0, 1.0)
|
||||
}
|
||||
|
||||
/// Add two transform lists.
|
||||
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
|
||||
fn add_weighted_transform_lists(from_list: &[TransformOperation],
|
||||
|
@ -1696,9 +1725,12 @@ fn add_weighted_transform_lists(from_list: &[TransformOperation],
|
|||
}
|
||||
(&TransformOperation::Scale(fx, fy, fz),
|
||||
&TransformOperation::Scale(tx, ty, tz)) => {
|
||||
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
||||
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
||||
let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
|
||||
let ix = add_weighted_with_initial_val(&fx, &tx, self_portion,
|
||||
other_portion, &1.0).unwrap();
|
||||
let iy = add_weighted_with_initial_val(&fy, &ty, self_portion,
|
||||
other_portion, &1.0).unwrap();
|
||||
let iz = add_weighted_with_initial_val(&fz, &tz, self_portion,
|
||||
other_portion, &1.0).unwrap();
|
||||
result.push(TransformOperation::Scale(ix, iy, iz));
|
||||
}
|
||||
(&TransformOperation::Rotate(fx, fy, fz, fa),
|
||||
|
@ -1808,10 +1840,12 @@ pub struct MatrixDecomposed2D {
|
|||
impl Animatable for InnerMatrix2D {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(InnerMatrix2D {
|
||||
m11: try!(self.m11.add_weighted(&other.m11, self_portion, other_portion)),
|
||||
m11: try!(add_weighted_with_initial_val(&self.m11, &other.m11,
|
||||
self_portion, other_portion, &1.0)),
|
||||
m12: try!(self.m12.add_weighted(&other.m12, self_portion, other_portion)),
|
||||
m21: try!(self.m21.add_weighted(&other.m21, self_portion, other_portion)),
|
||||
m22: try!(self.m22.add_weighted(&other.m22, self_portion, other_portion)),
|
||||
m22: try!(add_weighted_with_initial_val(&self.m22, &other.m22,
|
||||
self_portion, other_portion, &1.0)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1828,8 +1862,8 @@ impl Animatable for Translate2D {
|
|||
impl Animatable for Scale2D {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Scale2D(
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion))
|
||||
try!(add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)),
|
||||
try!(add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -2229,9 +2263,9 @@ impl Animatable for Translate3D {
|
|||
impl Animatable for Scale3D {
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
Ok(Scale3D(
|
||||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion))
|
||||
try!(add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)),
|
||||
try!(add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)),
|
||||
try!(add_weighted_with_initial_val(&self.2, &other.2, self_portion, other_portion, &1.0))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -2252,7 +2286,7 @@ impl Animatable for Perspective {
|
|||
try!(self.0.add_weighted(&other.0, self_portion, other_portion)),
|
||||
try!(self.1.add_weighted(&other.1, self_portion, other_portion)),
|
||||
try!(self.2.add_weighted(&other.2, self_portion, other_portion)),
|
||||
try!(self.3.add_weighted(&other.3, self_portion, other_portion))
|
||||
try!(add_weighted_with_initial_val(&self.3, &other.3, self_portion, other_portion, &1.0))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -2261,8 +2295,9 @@ impl Animatable 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<Self, ()> {
|
||||
assert!(self_portion + other_portion == 1.0f64,
|
||||
"add_weighted should only be used for interpolating transforms");
|
||||
assert!(self_portion + other_portion == 1.0f64 ||
|
||||
other_portion == 1.0f64,
|
||||
"add_weighted should only be used for interpolating or accumulating transforms");
|
||||
|
||||
let mut sum = *self;
|
||||
|
||||
|
@ -2275,30 +2310,63 @@ impl Animatable for MatrixDecomposed3D {
|
|||
self_portion, other_portion));
|
||||
|
||||
// Add quaternions using spherical linear interpolation (Slerp).
|
||||
let mut product = self.quaternion.0 * other.quaternion.0 +
|
||||
self.quaternion.1 * other.quaternion.1 +
|
||||
self.quaternion.2 * other.quaternion.2 +
|
||||
self.quaternion.3 * other.quaternion.3;
|
||||
//
|
||||
// We take a specialized code path for accumulation (where other_portion is 1)
|
||||
if other_portion == 1.0 {
|
||||
if self_portion == 0.0 {
|
||||
return Ok(*other)
|
||||
}
|
||||
|
||||
// Clamp product to -1.0 <= product <= 1.0
|
||||
product = product.min(1.0);
|
||||
product = product.max(-1.0);
|
||||
let clamped_w = self.quaternion.3.min(1.0).max(-1.0);
|
||||
|
||||
if product == 1.0 {
|
||||
return Ok(sum);
|
||||
// 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 as f32;
|
||||
scale *= theta.sin();
|
||||
|
||||
// Scale the self matrix by self_portion.
|
||||
let mut scaled_self = *self;
|
||||
% for i in range(3):
|
||||
scaled_self.quaternion.${i} *= scale;
|
||||
% endfor
|
||||
scaled_self.quaternion.3 = theta.cos();
|
||||
|
||||
// Multiply scaled-self by other.
|
||||
let a = &scaled_self.quaternion;
|
||||
let b = &other.quaternion;
|
||||
sum.quaternion = Quaternion(
|
||||
a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
|
||||
a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
|
||||
a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
|
||||
a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
|
||||
);
|
||||
} else {
|
||||
let mut product = self.quaternion.0 * other.quaternion.0 +
|
||||
self.quaternion.1 * other.quaternion.1 +
|
||||
self.quaternion.2 * other.quaternion.2 +
|
||||
self.quaternion.3 * other.quaternion.3;
|
||||
|
||||
// Clamp product to -1.0 <= product <= 1.0
|
||||
product = product.min(1.0);
|
||||
product = product.max(-1.0);
|
||||
|
||||
if product == 1.0 {
|
||||
return Ok(sum);
|
||||
}
|
||||
|
||||
let theta = product.acos();
|
||||
let w = (other_portion as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
|
||||
let mut a = *self;
|
||||
let mut b = *other;
|
||||
% for i in range(4):
|
||||
a.quaternion.${i} *= (other_portion as f32 * theta).cos() - product * w;
|
||||
b.quaternion.${i} *= w;
|
||||
sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
|
||||
% endfor
|
||||
}
|
||||
|
||||
let theta = product.acos();
|
||||
let w = (other_portion as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
|
||||
let mut a = *self;
|
||||
let mut b = *other;
|
||||
% for i in range(4):
|
||||
a.quaternion.${i} *= (other_portion as f32 * theta).cos() - product * w;
|
||||
b.quaternion.${i} *= w;
|
||||
sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
|
||||
% endfor
|
||||
|
||||
Ok(sum)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleSheet};
|
|||
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID};
|
||||
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
|
||||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint};
|
||||
use style::gecko_bindings::structs::IterationCompositeOperation;
|
||||
use style::gecko_bindings::structs::Loader;
|
||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
|
@ -332,7 +333,9 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
|||
base_values: *mut ::std::os::raw::c_void,
|
||||
css_property: nsCSSPropertyID,
|
||||
segment: RawGeckoAnimationPropertySegmentBorrowed,
|
||||
computed_timing: RawGeckoComputedTimingBorrowed)
|
||||
last_segment: RawGeckoAnimationPropertySegmentBorrowed,
|
||||
computed_timing: RawGeckoComputedTimingBorrowed,
|
||||
iteration_composite: IterationCompositeOperation)
|
||||
{
|
||||
use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
|
||||
use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
|
||||
|
@ -342,10 +345,16 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
|||
let property: TransitionProperty = css_property.into();
|
||||
let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
|
||||
|
||||
// We will need an underlying value if either of the endpoints is null...
|
||||
let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
|
||||
segment.mToValue.mServo.mRawPtr.is_null() ||
|
||||
// ... or if they have a non-replace composite mode ...
|
||||
segment.mFromComposite != CompositeOperation::Replace ||
|
||||
segment.mToComposite != CompositeOperation::Replace;
|
||||
segment.mToComposite != CompositeOperation::Replace ||
|
||||
// ... or if we accumulate onto the last value and it is null.
|
||||
(iteration_composite == IterationCompositeOperation::Accumulate &&
|
||||
computed_timing.mCurrentIteration > 0 &&
|
||||
last_segment.mToValue.mServo.mRawPtr.is_null());
|
||||
|
||||
// If either of the segment endpoints are null, get the underlying value to
|
||||
// use from the current value in the values map (set by a lower-priority
|
||||
|
@ -366,39 +375,95 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
|||
return;
|
||||
}
|
||||
|
||||
// Temporaries used in the following if-block whose lifetimes we need to prlong.
|
||||
// Extract keyframe values.
|
||||
let raw_from_value;
|
||||
let from_composite_result;
|
||||
let from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
|
||||
let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
|
||||
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
|
||||
match segment.mFromComposite {
|
||||
CompositeOperation::Add => {
|
||||
let value_to_composite = AnimationValue::as_arc(&raw_from_value).as_ref();
|
||||
from_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite);
|
||||
from_composite_result.as_ref().unwrap_or(value_to_composite)
|
||||
}
|
||||
_ => { AnimationValue::as_arc(&raw_from_value) }
|
||||
}
|
||||
Some(AnimationValue::as_arc(&raw_from_value))
|
||||
} else {
|
||||
underlying_value.as_ref().unwrap()
|
||||
None
|
||||
};
|
||||
|
||||
let raw_to_value;
|
||||
let to_composite_result;
|
||||
let to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
|
||||
let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
|
||||
raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
|
||||
match segment.mToComposite {
|
||||
CompositeOperation::Add => {
|
||||
let value_to_composite = AnimationValue::as_arc(&raw_to_value).as_ref();
|
||||
to_composite_result = underlying_value.as_ref().unwrap().add(value_to_composite);
|
||||
to_composite_result.as_ref().unwrap_or(value_to_composite)
|
||||
}
|
||||
_ => { AnimationValue::as_arc(&raw_to_value) }
|
||||
}
|
||||
Some(AnimationValue::as_arc(&raw_to_value))
|
||||
} else {
|
||||
underlying_value.as_ref().unwrap()
|
||||
None
|
||||
};
|
||||
|
||||
// Composite with underlying value.
|
||||
// A return value of None means, "Just use keyframe_value as-is."
|
||||
let composite_endpoint = |keyframe_value: Option<&Arc<AnimationValue>>,
|
||||
composite_op: CompositeOperation| -> Option<AnimationValue> {
|
||||
match keyframe_value {
|
||||
Some(keyframe_value) => {
|
||||
match composite_op {
|
||||
CompositeOperation::Add => {
|
||||
debug_assert!(need_underlying_value,
|
||||
"Should have detected we need an underlying value");
|
||||
underlying_value.as_ref().unwrap().add(keyframe_value).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()
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
None => {
|
||||
debug_assert!(need_underlying_value,
|
||||
"Should have detected we need an underlying value");
|
||||
underlying_value.clone()
|
||||
},
|
||||
}
|
||||
};
|
||||
let mut composited_from_value = composite_endpoint(keyframe_from_value, segment.mFromComposite);
|
||||
let mut composited_to_value = composite_endpoint(keyframe_to_value, segment.mToComposite);
|
||||
|
||||
debug_assert!(keyframe_from_value.is_some() || composited_from_value.is_some(),
|
||||
"Should have a suitable from value to use");
|
||||
debug_assert!(keyframe_to_value.is_some() || composited_to_value.is_some(),
|
||||
"Should have a suitable to value to use");
|
||||
|
||||
// Apply iteration composite behavior.
|
||||
if iteration_composite == IterationCompositeOperation::Accumulate &&
|
||||
computed_timing.mCurrentIteration > 0 {
|
||||
let raw_last_value;
|
||||
let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
|
||||
raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
|
||||
AnimationValue::as_arc(&raw_last_value).as_ref()
|
||||
} else {
|
||||
debug_assert!(need_underlying_value,
|
||||
"Should have detected we need an underlying value");
|
||||
underlying_value.as_ref().unwrap()
|
||||
};
|
||||
|
||||
// As with composite_endpoint, a return value of None means, "Use keyframe_value as-is."
|
||||
let apply_iteration_composite = |keyframe_value: Option<&Arc<AnimationValue>>,
|
||||
composited_value: Option<AnimationValue>|
|
||||
-> Option<AnimationValue> {
|
||||
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(),
|
||||
}
|
||||
};
|
||||
|
||||
composited_from_value = apply_iteration_composite(keyframe_from_value,
|
||||
composited_from_value);
|
||||
composited_to_value = apply_iteration_composite(keyframe_to_value,
|
||||
composited_to_value);
|
||||
}
|
||||
|
||||
// Use the composited value if there is one, otherwise, use the original keyframe value.
|
||||
let from_value = composited_from_value.as_ref().unwrap_or_else(|| keyframe_from_value.unwrap());
|
||||
let to_value = composited_to_value.as_ref().unwrap_or_else(|| keyframe_to_value.unwrap());
|
||||
|
||||
let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
|
||||
if segment.mToKey == segment.mFromKey {
|
||||
if progress < 0. {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue