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,11 +991,25 @@ 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;
}
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; -> *mut Keyframe;
} }
extern "C" { extern "C" {
@ -1669,14 +1683,14 @@ 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,
computed_values: computed_values:
ServoComputedValuesBorrowed, ServoComputedValuesBorrowed,
keyframe_list: keyframe_list:
RawGeckoKeyframeListBorrowedMut) RawGeckoKeyframeListBorrowedMut)
-> bool; -> bool;
} }
extern "C" { extern "C" {

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,73 +2188,154 @@ 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, style: &ComputedValues,
name: *const nsACString, property: &TransitionProperty,
timing_function: nsTimingFunctionBorrowed, shared_lock: &SharedRwLock) {
style: ServoComputedValuesBorrowed, let block = style.to_declaration_block(property.clone().into());
keyframes: RawGeckoKeyframeListBorrowedMut) -> bool { unsafe {
use style::gecko_bindings::structs::Keyframe; let index = (*keyframe).mPropertyValues.len();
use style::properties::LonghandIdSet; (*keyframe).mPropertyValues.set_len((index + 1) as u32);
(*keyframe).mPropertyValues[index].mProperty = property.into();
// FIXME. Bug 1360398: Do not set computed values once we handles
// missing keyframes with additive composition.
(*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky(
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 data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) }; 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 style = ComputedValues::as_arc(&style);
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
if let Some(ref animation) = data.stylist.animations().get(&name) { let mut properties_set_at_current_offset = LonghandIdSet::new();
let global_style_data = &*GLOBAL_STYLE_DATA; let mut properties_set_at_start = LonghandIdSet::new();
let guard = global_style_data.shared_lock.read(); let mut properties_set_at_end = LonghandIdSet::new();
for step in &animation.steps { let mut has_complete_initial_keyframe = false;
// Override timing_function if the keyframe has animation-timing-function. let mut has_complete_final_keyframe = false;
let timing_function = if let Some(val) = step.get_animation_timing_function(&guard) { let mut current_offset = -1.;
val.into()
} else {
*timing_function
};
let keyframe = unsafe { // Iterate over the keyframe rules backwards so we can drop overridden
Gecko_AnimationAppendKeyframe(keyframes, // properties (since declarations in later rules override those in earlier
step.start_percentage.0 as f32, // ones).
&timing_function) 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;
}
fn add_computed_property_value(keyframe: *mut Keyframe, // Override timing_function if the keyframe has an animation-timing-function.
index: usize, let timing_function = match step.get_animation_timing_function(&guard) {
style: &ComputedValues, Some(val) => val.into(),
property: &TransitionProperty, None => *inherited_timing_function,
shared_lock: &SharedRwLock) { };
let block = style.to_declaration_block(property.clone().into());
unsafe { // Look for an existing keyframe with the same offset and timing
(*keyframe).mPropertyValues.set_len((index + 1) as u32); // function or else add a new keyframe at the beginning of the keyframe
(*keyframe).mPropertyValues[index].mProperty = property.into(); // array.
// FIXME. Do not set computed values once we handles missing keyframes let keyframe = unsafe {
// with additive composition. Gecko_GetOrCreateKeyframeAtStart(keyframes,
(*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( step.start_percentage.0 as f32,
Arc::new(shared_lock.wrap(block))); &timing_function)
};
match step.value {
KeyframesStepValue::ComputedValues => {
// In KeyframesAnimation::from_keyframes if there is no 0% or
// 100% keyframe at all, we will create a 'ComputedValues' step
// 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 } => {
let guard = block.read_with(&guard);
// Filter out non-animatable properties.
let animatable =
guard.declarations()
.iter()
.filter(|&&(ref declaration, _)| {
declaration.is_animatable()
});
match step.value { let mut index = unsafe { (*keyframe).mPropertyValues.len() };
KeyframesStepValue::ComputedValues => { for &(ref declaration, _) in animatable {
for (index, property) in animation.properties_changed.iter().enumerate() { let property = TransitionProperty::from_declaration(declaration).unwrap();
add_computed_property_value( if !properties_set_at_current_offset.has_transition_property_bit(&property) {
keyframe, index, style, property, &global_style_data.shared_lock); properties_set_at_current_offset.set_transition_property_bit(&property);
} if current_offset == 0.0 {
}, properties_set_at_start.set_transition_property_bit(&property);
KeyframesStepValue::Declarations { ref block } => { } else if current_offset == 1.0 {
let guard = block.read_with(&guard); properties_set_at_end.set_transition_property_bit(&property);
// Filter out non-animatable properties. }
let animatable =
guard.declarations()
.iter()
.filter(|&&(ref declaration, _)| {
declaration.is_animatable()
});
let mut seen = LonghandIdSet::new();
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);
}
} }
index += 1;
} }
}
// 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;
}
}
}
},
}
} }
return true
} }
false
// 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);
}
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]