Auto merge of #16850 - hiikezoe:merge-keyframe, r=birtles,SimonSapin

Merge keyframes at the same offset

This is a PR for https://bugzilla.mozilla.org/show_bug.cgi?id=1354947 .
---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors

- [X] These changes do not require tests because it's for stylo

<!-- 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/16850)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-05-13 14:05:40 -05:00 committed by GitHub
commit c5e31def29
5 changed files with 1069 additions and 1066 deletions

View file

@ -991,13 +991,27 @@ extern "C" {
src: *mut nsStyleDisplay); src: *mut nsStyleDisplay);
} }
extern "C" { extern "C" {
pub fn Gecko_AnimationAppendKeyframe(keyframes: pub fn Gecko_GetOrCreateKeyframeAtStart(keyframes:
RawGeckoKeyframeListBorrowedMut, RawGeckoKeyframeListBorrowedMut,
offset: f32, offset: f32,
timingFunction: timingFunction:
*const nsTimingFunction) *const nsTimingFunction)
-> *mut Keyframe; -> *mut Keyframe;
} }
extern "C" {
pub fn Gecko_GetOrCreateInitialKeyframe(keyframes:
RawGeckoKeyframeListBorrowedMut,
timingFunction:
*const nsTimingFunction)
-> *mut Keyframe;
}
extern "C" {
pub fn Gecko_GetOrCreateFinalKeyframe(keyframes:
RawGeckoKeyframeListBorrowedMut,
timingFunction:
*const nsTimingFunction)
-> *mut Keyframe;
}
extern "C" { extern "C" {
pub fn Gecko_ResetStyleCoord(unit: *mut nsStyleUnit, pub fn Gecko_ResetStyleCoord(unit: *mut nsStyleUnit,
value: *mut nsStyleUnion); value: *mut nsStyleUnion);
@ -1669,7 +1683,7 @@ extern "C" {
author_style_disabled: bool); author_style_disabled: bool);
} }
extern "C" { extern "C" {
pub fn Servo_StyleSet_FillKeyframesForName(set: RawServoStyleSetBorrowed, pub fn Servo_StyleSet_GetKeyframesForName(set: RawServoStyleSetBorrowed,
property: *const nsACString, property: *const nsACString,
timing_function: timing_function:
nsTimingFunctionBorrowed, nsTimingFunctionBorrowed,

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -251,6 +251,14 @@ impl LonghandIdSet {
self.storage[bit / 32] &= !(1 << (bit % 32)); self.storage[bit / 32] &= !(1 << (bit % 32));
} }
/// Clear all bits
#[inline]
pub fn clear(&mut self) {
for cell in &mut self.storage {
*cell = 0
}
}
/// Set the corresponding bit of TransitionProperty. /// Set the corresponding bit of TransitionProperty.
/// This function will panic if TransitionProperty::All is given. /// This function will panic if TransitionProperty::All is given.
pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) { pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) {

View file

@ -40,7 +40,9 @@ use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedVal
use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed};
use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong}; use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
use style::gecko_bindings::bindings::{nsACString, nsAString}; use style::gecko_bindings::bindings::{nsACString, nsAString};
use style::gecko_bindings::bindings::Gecko_AnimationAppendKeyframe; use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed; use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed;
use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut; use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed; use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
@ -75,7 +77,7 @@ use style::media_queries::{MediaList, parse_media_query_list};
use style::parallel; use style::parallel;
use style::parser::{LengthParsingMode, ParserContext}; use style::parser::{LengthParsingMode, ParserContext};
use style::properties::{CascadeFlags, ComputedValues, Importance, ParsedDeclaration, StyleBuilder}; use style::properties::{CascadeFlags, ComputedValues, Importance, ParsedDeclaration, StyleBuilder};
use style::properties::{PropertyDeclarationBlock, PropertyId}; use style::properties::{LonghandIdSet, PropertyDeclarationBlock, PropertyId};
use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP; use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty}; use style::properties::animated_properties::{Animatable, AnimationValue, TransitionProperty};
use style::properties::parse_one_declaration; use style::properties::parse_one_declaration;
@ -2186,58 +2188,131 @@ pub extern "C" fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed) {
assert_subtree_is_clean(root); assert_subtree_is_clean(root);
} }
#[no_mangle] fn append_computed_property_value(keyframe: *mut structs::Keyframe,
pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSetBorrowed,
name: *const nsACString,
timing_function: nsTimingFunctionBorrowed,
style: ServoComputedValuesBorrowed,
keyframes: RawGeckoKeyframeListBorrowedMut) -> bool {
use style::gecko_bindings::structs::Keyframe;
use style::properties::LonghandIdSet;
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) };
let style = ComputedValues::as_arc(&style);
if let Some(ref animation) = data.stylist.animations().get(&name) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
for step in &animation.steps {
// Override timing_function if the keyframe has animation-timing-function.
let timing_function = if let Some(val) = step.get_animation_timing_function(&guard) {
val.into()
} else {
*timing_function
};
let keyframe = unsafe {
Gecko_AnimationAppendKeyframe(keyframes,
step.start_percentage.0 as f32,
&timing_function)
};
fn add_computed_property_value(keyframe: *mut Keyframe,
index: usize,
style: &ComputedValues, style: &ComputedValues,
property: &TransitionProperty, property: &TransitionProperty,
shared_lock: &SharedRwLock) { shared_lock: &SharedRwLock) {
let block = style.to_declaration_block(property.clone().into()); let block = style.to_declaration_block(property.clone().into());
unsafe { unsafe {
let index = (*keyframe).mPropertyValues.len();
(*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues.set_len((index + 1) as u32);
(*keyframe).mPropertyValues[index].mProperty = property.into(); (*keyframe).mPropertyValues[index].mProperty = property.into();
// FIXME. Do not set computed values once we handles missing keyframes // FIXME. Bug 1360398: Do not set computed values once we handles
// with additive composition. // missing keyframes with additive composition.
(*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( (*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky(
Arc::new(shared_lock.wrap(block))); Arc::new(shared_lock.wrap(block)));
} }
}
fn fill_in_missing_keyframe_values(all_properties: &[TransitionProperty],
timing_function: nsTimingFunctionBorrowed,
style: &ComputedValues,
properties_set_at_offset: &LonghandIdSet,
offset: f32,
keyframes: RawGeckoKeyframeListBorrowedMut,
shared_lock: &SharedRwLock) {
debug_assert!(offset == 0. || offset == 1.,
"offset should be 0. or 1.");
let needs_filling = all_properties.iter().any(|ref property| {
!properties_set_at_offset.has_transition_property_bit(property)
});
// Return earli if all animated properties are already set.
if !needs_filling {
return;
} }
let keyframe = match offset {
0. => unsafe {
Gecko_GetOrCreateInitialKeyframe(keyframes, timing_function)
},
1. => unsafe {
Gecko_GetOrCreateFinalKeyframe(keyframes, timing_function)
},
_ => unreachable!("offset should be 0. or 1."),
};
// Append properties that have not been set at this offset.
for ref property in all_properties.iter() {
if !properties_set_at_offset.has_transition_property_bit(property) {
append_computed_property_value(keyframe,
style,
property,
shared_lock);
}
}
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_GetKeyframesForName(raw_data: RawServoStyleSetBorrowed,
name: *const nsACString,
inherited_timing_function: nsTimingFunctionBorrowed,
style: ServoComputedValuesBorrowed,
keyframes: RawGeckoKeyframeListBorrowedMut) -> bool {
debug_assert!(keyframes.len() == 0,
"keyframes should be initially empty");
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) };
let animation = match data.stylist.animations().get(&name) {
Some(animation) => animation,
None => return false,
};
let style = ComputedValues::as_arc(&style);
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let mut properties_set_at_current_offset = LonghandIdSet::new();
let mut properties_set_at_start = LonghandIdSet::new();
let mut properties_set_at_end = LonghandIdSet::new();
let mut has_complete_initial_keyframe = false;
let mut has_complete_final_keyframe = false;
let mut current_offset = -1.;
// Iterate over the keyframe rules backwards so we can drop overridden
// properties (since declarations in later rules override those in earlier
// ones).
for step in animation.steps.iter().rev() {
if step.start_percentage.0 != current_offset {
properties_set_at_current_offset.clear();
current_offset = step.start_percentage.0;
}
// Override timing_function if the keyframe has an animation-timing-function.
let timing_function = match step.get_animation_timing_function(&guard) {
Some(val) => val.into(),
None => *inherited_timing_function,
};
// Look for an existing keyframe with the same offset and timing
// function or else add a new keyframe at the beginning of the keyframe
// array.
let keyframe = unsafe {
Gecko_GetOrCreateKeyframeAtStart(keyframes,
step.start_percentage.0 as f32,
&timing_function)
};
match step.value { match step.value {
KeyframesStepValue::ComputedValues => { KeyframesStepValue::ComputedValues => {
for (index, property) in animation.properties_changed.iter().enumerate() { // In KeyframesAnimation::from_keyframes if there is no 0% or
add_computed_property_value( // 100% keyframe at all, we will create a 'ComputedValues' step
keyframe, index, style, property, &global_style_data.shared_lock); // to represent that all properties animated by the keyframes
// animation should be set to the underlying computed value for
// that keyframe.
for property in animation.properties_changed.iter() {
append_computed_property_value(keyframe,
style,
property,
&global_style_data.shared_lock);
}
if current_offset == 0.0 {
has_complete_initial_keyframe = true;
} else if current_offset == 1.0 {
has_complete_final_keyframe = true;
} }
}, },
KeyframesStepValue::Declarations { ref block } => { KeyframesStepValue::Declarations { ref block } => {
@ -2250,9 +2325,17 @@ pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSet
declaration.is_animatable() declaration.is_animatable()
}); });
let mut seen = LonghandIdSet::new(); let mut index = unsafe { (*keyframe).mPropertyValues.len() };
for &(ref declaration, _) in animatable {
let property = TransitionProperty::from_declaration(declaration).unwrap();
if !properties_set_at_current_offset.has_transition_property_bit(&property) {
properties_set_at_current_offset.set_transition_property_bit(&property);
if current_offset == 0.0 {
properties_set_at_start.set_transition_property_bit(&property);
} else if current_offset == 1.0 {
properties_set_at_end.set_transition_property_bit(&property);
}
for (index, &(ref declaration, _)) in animatable.enumerate() {
unsafe { unsafe {
let property = TransitionProperty::from_declaration(declaration).unwrap(); let property = TransitionProperty::from_declaration(declaration).unwrap();
(*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues.set_len((index + 1) as u32);
@ -2262,31 +2345,34 @@ pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSet
PropertyDeclarationBlock::with_one( PropertyDeclarationBlock::with_one(
declaration.clone(), Importance::Normal declaration.clone(), Importance::Normal
)))); ))));
if step.start_percentage.0 == 0. ||
step.start_percentage.0 == 1. {
seen.set_transition_property_bit(&property);
} }
}
}
// Append missing property values in the initial or the finial keyframes.
if step.start_percentage.0 == 0. ||
step.start_percentage.0 == 1. {
let mut index = unsafe { (*keyframe).mPropertyValues.len() };
for property in animation.properties_changed.iter() {
if !seen.has_transition_property_bit(&property) {
add_computed_property_value(
keyframe, index, style, property, &global_style_data.shared_lock);
index += 1; index += 1;
} }
} }
}
}, },
} }
} }
return true
// Append property values that are missing in the initial or the final keyframes.
if !has_complete_initial_keyframe {
fill_in_missing_keyframe_values(&animation.properties_changed,
inherited_timing_function,
style,
&properties_set_at_start,
0.,
keyframes,
&global_style_data.shared_lock);
} }
false if !has_complete_final_keyframe {
fill_in_missing_keyframe_values(&animation.properties_changed,
inherited_timing_function,
style,
&properties_set_at_end,
1.,
keyframes,
&global_style_data.shared_lock);
}
true
} }
#[no_mangle] #[no_mangle]