mirror of
https://github.com/servo/servo.git
synced 2025-06-19 14:48:59 +01:00
Add Servo_ComposeAnimationSegment which composes an animation segment.
This commit is contained in:
parent
1f5551c397
commit
52c81a0a84
2 changed files with 185 additions and 114 deletions
|
@ -2776,9 +2776,22 @@ extern "C" {
|
||||||
RawGeckoAnimationPropertySegmentBorrowed,
|
RawGeckoAnimationPropertySegmentBorrowed,
|
||||||
computed_timing:
|
computed_timing:
|
||||||
RawGeckoComputedTimingBorrowed,
|
RawGeckoComputedTimingBorrowed,
|
||||||
iteration_composite:
|
iter_composite:
|
||||||
IterationCompositeOperation);
|
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" {
|
extern "C" {
|
||||||
pub fn Servo_DeclarationBlock_PropertyIsSet(declarations:
|
pub fn Servo_DeclarationBlock_PropertyIsSet(declarations:
|
||||||
RawServoDeclarationBlockBorrowed,
|
RawServoDeclarationBlockBorrowed,
|
||||||
|
|
|
@ -67,6 +67,7 @@ use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowed;
|
||||||
use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowedMut;
|
use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
|
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
|
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
|
||||||
|
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowedOrNull;
|
||||||
use style::gecko_bindings::bindings::RawServoAnimationValueMapBorrowedMut;
|
use style::gecko_bindings::bindings::RawServoAnimationValueMapBorrowedMut;
|
||||||
use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
|
use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
|
||||||
use style::gecko_bindings::bindings::RawServoAnimationValueTableBorrowed;
|
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)
|
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<AnimationValue>>,
|
||||||
|
composite: CompositeOperation,
|
||||||
|
underlying_value: Option<&AnimationValue>,
|
||||||
|
) -> Option<AnimationValue> {
|
||||||
|
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<AnimationValue>>,
|
||||||
|
composited_value: Option<AnimationValue>,
|
||||||
|
last_value: &AnimationValue,
|
||||||
|
current_iteration: u64
|
||||||
|
) -> Option<AnimationValue> {
|
||||||
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut,
|
pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut,
|
||||||
base_values: RawServoAnimationValueTableBorrowed,
|
base_values: RawServoAnimationValueTableBorrowed,
|
||||||
|
@ -461,125 +613,31 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract keyframe values.
|
let raw_last_value;
|
||||||
let raw_from_value;
|
let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
|
||||||
let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
|
raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
|
||||||
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
|
Some(&**AnimationValue::as_arc(&raw_last_value))
|
||||||
Some(AnimationValue::as_arc(&raw_from_value))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
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<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().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<AnimationValue>>,
|
|
||||||
composited_value: Option<AnimationValue>|
|
|
||||||
-> Option<AnimationValue> {
|
|
||||||
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) };
|
let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
|
||||||
if segment.mToKey == segment.mFromKey {
|
let position = if segment.mToKey == segment.mFromKey {
|
||||||
if progress < 0. {
|
// Note: compose_animation_segment doesn't use this value
|
||||||
value_map.insert(property, from_value.clone());
|
// if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
|
||||||
} else {
|
progress
|
||||||
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());
|
|
||||||
} else {
|
} 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 {
|
macro_rules! get_property_id_from_nscsspropertyid {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue