From 52c81a0a84f2b54b90c231df8794c678760f068c Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 27 Oct 2017 21:08:22 +0200 Subject: [PATCH] Add Servo_ComposeAnimationSegment which composes an animation segment. --- components/style/gecko/generated/bindings.rs | 15 +- ports/geckolib/glue.rs | 284 +++++++++++-------- 2 files changed, 185 insertions(+), 114 deletions(-) diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index d80a8f1eae6..3eb3c522913 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -2776,9 +2776,22 @@ extern "C" { RawGeckoAnimationPropertySegmentBorrowed, computed_timing: RawGeckoComputedTimingBorrowed, - iteration_composite: + iter_composite: IterationCompositeOperation); } +extern "C" { + pub fn Servo_ComposeAnimationSegment(animation_segment: + RawGeckoAnimationPropertySegmentBorrowed, + underlying_value: + RawServoAnimationValueBorrowedOrNull, + last_value: + RawServoAnimationValueBorrowedOrNull, + iter_composite: + IterationCompositeOperation, + progress: f64, + current_iteration: u64) + -> RawServoAnimationValueStrong; +} extern "C" { pub fn Servo_DeclarationBlock_PropertyIsSet(declarations: RawServoDeclarationBlockBorrowed, diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 31ea90e21f2..4c841c71daa 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -67,6 +67,7 @@ use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowed; use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowedMut; use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut; use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed; +use style::gecko_bindings::bindings::RawServoAnimationValueBorrowedOrNull; use style::gecko_bindings::bindings::RawServoAnimationValueMapBorrowedMut; use style::gecko_bindings::bindings::RawServoAnimationValueStrong; use style::gecko_bindings::bindings::RawServoAnimationValueTableBorrowed; @@ -411,6 +412,157 @@ pub extern "C" fn Servo_AnimationValues_ComputeDistance(from: RawServoAnimationV from_value.compute_squared_distance(to_value).map(|d| d.sqrt()).unwrap_or(-1.0) } +/// Compute one of the endpoints for the interpolation interval, compositing it with the +/// underlying value if needed. +/// An None returned value means, "Just use endpoint_value as-is." +/// It is the responsibility of the caller to ensure that |underlying_value| is provided +/// when it will be used. +fn composite_endpoint( + endpoint_value: Option<&RawOffsetArc>, + composite: CompositeOperation, + underlying_value: Option<&AnimationValue>, +) -> Option { + match endpoint_value { + Some(endpoint_value) => { + match composite { + CompositeOperation::Add => { + underlying_value + .expect("We should have an underlying_value") + .animate(endpoint_value, Procedure::Add).ok() + }, + CompositeOperation::Accumulate => { + underlying_value + .expect("We should have an underlying value") + .animate(endpoint_value, Procedure::Accumulate { count: 1 }) + .ok() + }, + _ => None, + } + }, + None => underlying_value.map(|v| v.clone()), + } +} + +/// Accumulate one of the endpoints of the animation interval. +/// A returned value of None means, "Just use endpoint_value as-is." +fn accumulate_endpoint( + endpoint_value: Option<&RawOffsetArc>, + composited_value: Option, + last_value: &AnimationValue, + current_iteration: u64 +) -> Option { + debug_assert!(endpoint_value.is_some() || composited_value.is_some(), + "Should have a suitable value to use"); + + let count = current_iteration; + match composited_value { + Some(endpoint) => { + last_value + .animate(&endpoint, Procedure::Accumulate { count }) + .ok() + .or(Some(endpoint)) + }, + None => { + last_value + .animate(endpoint_value.unwrap(), Procedure::Accumulate { count }) + .ok() + }, + } +} + +/// Compose the animation segment. We composite it with the underlying_value and last_value if +/// needed. +/// The caller is responsible for providing an underlying value and last value +/// in all situations where there are needed. +fn compose_animation_segment( + segment: RawGeckoAnimationPropertySegmentBorrowed, + underlying_value: Option<&AnimationValue>, + last_value: Option<&AnimationValue>, + iteration_composite: IterationCompositeOperation, + current_iteration: u64, + total_progress: f64, + segment_progress: f64, +) -> AnimationValue { + // Extract keyframe values. + let raw_from_value; + let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() { + raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr }; + Some(AnimationValue::as_arc(&raw_from_value)) + } else { + None + }; + + let raw_to_value; + let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() { + raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr }; + Some(AnimationValue::as_arc(&raw_to_value)) + } else { + None + }; + + let mut composited_from_value = composite_endpoint(keyframe_from_value, + segment.mFromComposite, + underlying_value); + let mut composited_to_value = composite_endpoint(keyframe_to_value, + segment.mToComposite, + underlying_value); + + 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 && current_iteration > 0 { + let last_value = last_value.unwrap_or_else(|| { + underlying_value.expect("Should have a valid underlying value") + }); + + composited_from_value = accumulate_endpoint(keyframe_from_value, + composited_from_value, + last_value, + current_iteration); + composited_to_value = accumulate_endpoint(keyframe_to_value, + composited_to_value, + last_value, + current_iteration); + } + + // Use the composited value if there is one, otherwise, use the original keyframe value. + let from = composited_from_value.as_ref().unwrap_or_else(|| keyframe_from_value.unwrap()); + let to = composited_to_value.as_ref().unwrap_or_else(|| keyframe_to_value.unwrap()); + + if segment.mToKey == segment.mFromKey { + return if total_progress < 0. { from.clone() } else { to.clone() }; + } + + match from.animate(to, Procedure::Interpolate { progress: segment_progress }) { + Ok(value) => value, + _ => if segment_progress < 0.5 { from.clone() } else { to.clone() }, + } +} + +#[no_mangle] +pub extern "C" fn Servo_ComposeAnimationSegment( + segment: RawGeckoAnimationPropertySegmentBorrowed, + underlying_value: RawServoAnimationValueBorrowedOrNull, + last_value: RawServoAnimationValueBorrowedOrNull, + iteration_composite: IterationCompositeOperation, + progress: f64, + current_iteration: u64 +) -> RawServoAnimationValueStrong { + let underlying_value = AnimationValue::arc_from_borrowed(&underlying_value).map(|v| &**v); + let last_value = AnimationValue::arc_from_borrowed(&last_value).map(|v| &**v); + let result = compose_animation_segment(segment, + underlying_value, + last_value, + iteration_composite, + current_iteration, + progress, + progress); + Arc::new(result).into_strong() +} + #[no_mangle] pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut, base_values: RawServoAnimationValueTableBorrowed, @@ -461,125 +613,31 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa return; } - // Extract keyframe values. - let raw_from_value; - let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() { - raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr }; - Some(AnimationValue::as_arc(&raw_from_value)) + let raw_last_value; + let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() { + raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr }; + Some(&**AnimationValue::as_arc(&raw_last_value)) } else { None }; - let raw_to_value; - let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() { - raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr }; - Some(AnimationValue::as_arc(&raw_to_value)) - } else { - None - }; - - // Composite with underlying value. - // A return value of None means, "Just use keyframe_value as-is." - let composite_endpoint = |keyframe_value: Option<&RawOffsetArc>, - composite_op: CompositeOperation| -> Option { - 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().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() - .animate(keyframe_value, Procedure::Accumulate { count: 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) - } 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<&RawOffsetArc>, - composited_value: Option| - -> Option { - let count = computed_timing.mCurrentIteration; - match composited_value { - Some(endpoint) => { - last_value - .animate(&endpoint, Procedure::Accumulate { count }) - .ok() - .or(Some(endpoint)) - }, - None => { - last_value - .animate(keyframe_value.unwrap(), Procedure::Accumulate { 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. { - value_map.insert(property, from_value.clone()); - } else { - value_map.insert(property, to_value.clone()); - } - return; - } - - let pos = unsafe { - Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) - }; - if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress: pos }) { - value_map.insert(property, value); - } else if pos < 0.5 { - value_map.insert(property, from_value.clone()); + let position = if segment.mToKey == segment.mFromKey { + // Note: compose_animation_segment doesn't use this value + // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine. + progress } else { - value_map.insert(property, to_value.clone()); - } + unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) } + }; + + let result = compose_animation_segment(segment, + underlying_value.as_ref(), + last_value, + iteration_composite, + computed_timing.mCurrentIteration, + progress, + position); + value_map.insert(property, result); } macro_rules! get_property_id_from_nscsspropertyid {