Skip to content

Commit

Permalink
Add Servo_ComposeAnimationSegment which composes an animation segment.
Browse files Browse the repository at this point in the history
  • Loading branch information
BorisChiou committed Oct 27, 2017
1 parent 1f5551c commit 52c81a0
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 113 deletions.
15 changes: 14 additions & 1 deletion components/style/gecko/generated/bindings.rs
Expand Up @@ -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,
Expand Down
282 changes: 170 additions & 112 deletions ports/geckolib/glue.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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<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]
pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut,
base_values: RawServoAnimationValueTableBorrowed,
Expand Down Expand Up @@ -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))
let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
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 {
None
unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
};

// 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) };
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());
} else {
value_map.insert(property, to_value.clone());
}
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 {
Expand Down

0 comments on commit 52c81a0

Please sign in to comment.