mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
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:
commit
c5e31def29
5 changed files with 1069 additions and 1066 deletions
|
@ -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
|
@ -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) {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue