mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #17202 - BorisChiou:stylo/animation/mismatched_transform, r=Manishearth,birtles
stylo: Bug 1335998 - Handle interpolation and accumulation of mismatched transform lists These are the interdependent patches of Bug 1335998. We want to do interpolation and accumulation for mismatched transform lists, so introduce ComputedOperation::InterpolateMatrix and ComputedOperation::Accumulation. Both arms store the from_list and to_list, and resolve them until we have the layout information. For the Servo part, we haven't implemented how to read the transform lists in layout/fragment.rs, but I think it would be easy. (related issue #13267) --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix [Bug 1335998](https://bugzilla.mozilla.org/show_bug.cgi?id=1335998) - [X] There are tests for these changes <!-- 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/17202) <!-- Reviewable:end -->
This commit is contained in:
commit
f4a720483d
10 changed files with 4840 additions and 4085 deletions
|
@ -2896,6 +2896,12 @@ impl Fragment {
|
|||
Matrix4D::create_skew(Radians::new(theta_x.radians()),
|
||||
Radians::new(theta_y.radians()))
|
||||
}
|
||||
transform::ComputedOperation::InterpolateMatrix { .. } |
|
||||
transform::ComputedOperation::AccumulateMatrix { .. } => {
|
||||
// TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Matrix4D by
|
||||
// the reference box.
|
||||
Matrix4D::identity()
|
||||
}
|
||||
};
|
||||
|
||||
transform = transform.pre_mul(&matrix);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* automatically generated by rust-bindgen */
|
||||
|
||||
pub use nsstring::{nsACString, nsAString, nsString, nsStringRepr};
|
||||
use gecko_bindings::structs::nsStyleTransformMatrix;
|
||||
use gecko_bindings::structs::nsTArray;
|
||||
type nsACString_internal = nsACString;
|
||||
type nsAString_internal = nsAString;
|
||||
|
@ -209,6 +210,10 @@ use gecko_bindings::structs::ParsingMode;
|
|||
use gecko_bindings::structs::InheritTarget;
|
||||
use gecko_bindings::structs::URLMatchingFunction;
|
||||
use gecko_bindings::structs::StyleRuleInclusion;
|
||||
use gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
|
||||
unsafe impl Send for nsStyleTransformMatrix::MatrixTransformOperator {}
|
||||
unsafe impl Sync for nsStyleTransformMatrix::MatrixTransformOperator {}
|
||||
use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
||||
pub type nsTArrayBorrowed_uintptr_t<'a> = &'a mut ::gecko_bindings::structs::nsTArray<usize>;
|
||||
pub type RawServoStyleSetOwned = ::gecko_bindings::sugar::ownership::Owned<RawServoStyleSet>;
|
||||
pub type RawServoStyleSetOwnedOrNull = ::gecko_bindings::sugar::ownership::OwnedOrNull<RawServoStyleSet>;
|
||||
|
@ -1310,6 +1315,10 @@ extern "C" {
|
|||
pub fn Gecko_CSSValue_SetPairList(css_value: nsCSSValueBorrowedMut,
|
||||
len: u32);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_CSSValue_InitSharedList(css_value: nsCSSValueBorrowedMut,
|
||||
len: u32);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Gecko_CSSValue_Drop(css_value: nsCSSValueBorrowedMut);
|
||||
}
|
||||
|
@ -2179,6 +2188,15 @@ extern "C" {
|
|||
arg3:
|
||||
nsCSSPropertyIDSetBorrowedMut);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_MatrixTransform_Operate(matrix_operator:
|
||||
MatrixTransformOperator,
|
||||
from: *const RawGeckoGfxMatrix4x4,
|
||||
to: *const RawGeckoGfxMatrix4x4,
|
||||
progress: f64,
|
||||
result:
|
||||
*mut RawGeckoGfxMatrix4x4);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_AnimationValues_Interpolate(from:
|
||||
RawServoAnimationValueBorrowed,
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -8,8 +8,9 @@ use app_units::Au;
|
|||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::structs;
|
||||
use gecko_bindings::structs::{nsCSSValue, nsCSSUnit};
|
||||
use gecko_bindings::structs::{nsCSSValue_Array, nscolor};
|
||||
use gecko_bindings::structs::{nsCSSValue_Array, nsCSSValueList, nscolor};
|
||||
use gecko_string_cache::Atom;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::slice;
|
||||
|
@ -225,19 +226,17 @@ impl nsCSSValue {
|
|||
/// Set to a list value
|
||||
///
|
||||
/// This is only supported on the main thread.
|
||||
pub fn set_list<I>(&mut self, mut values: I) where I: ExactSizeIterator<Item=nsCSSValue> {
|
||||
pub fn set_list<I>(&mut self, values: I) where I: ExactSizeIterator<Item=nsCSSValue> {
|
||||
debug_assert!(values.len() > 0, "Empty list is not supported");
|
||||
unsafe { bindings::Gecko_CSSValue_SetList(self, values.len() as u32); }
|
||||
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
|
||||
let mut item_ptr = &mut unsafe {
|
||||
let list: &mut structs::nsCSSValueList = &mut unsafe {
|
||||
self.mValue.mList.as_ref() // &*nsCSSValueList_heap
|
||||
.as_mut().expect("List pointer should be non-null")
|
||||
}._base as *mut structs::nsCSSValueList;
|
||||
while let Some(item) = unsafe { item_ptr.as_mut() } {
|
||||
item.mValue = values.next().expect("Values shouldn't have been exhausted");
|
||||
item_ptr = item.mNext;
|
||||
}._base;
|
||||
for (item, new_value) in list.into_iter().zip(values) {
|
||||
*item = new_value;
|
||||
}
|
||||
debug_assert!(values.next().is_none(), "Values should have been exhausted");
|
||||
}
|
||||
|
||||
/// Set to a pair list value
|
||||
|
@ -260,6 +259,21 @@ impl nsCSSValue {
|
|||
}
|
||||
debug_assert!(values.next().is_none(), "Values should have been exhausted");
|
||||
}
|
||||
|
||||
/// Set a shared list
|
||||
pub fn set_shared_list<I>(&mut self, values: I) where I: ExactSizeIterator<Item=nsCSSValue> {
|
||||
debug_assert!(values.len() > 0, "Empty list is not supported");
|
||||
unsafe { bindings::Gecko_CSSValue_InitSharedList(self, values.len() as u32) };
|
||||
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_SharedList);
|
||||
let list = unsafe {
|
||||
self.mValue.mSharedList.as_ref()
|
||||
.as_mut().expect("List pointer should be non-null").mHead.as_mut()
|
||||
};
|
||||
debug_assert!(list.is_some(), "New created shared list shouldn't be null");
|
||||
for (item, new_value) in list.unwrap().into_iter().zip(values) {
|
||||
*item = new_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for nsCSSValue {
|
||||
|
@ -268,6 +282,64 @@ impl Drop for nsCSSValue {
|
|||
}
|
||||
}
|
||||
|
||||
/// Iterator of nsCSSValueList.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct nsCSSValueListIterator<'a> {
|
||||
current: Option<&'a nsCSSValueList>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for nsCSSValueListIterator<'a> {
|
||||
type Item = &'a nsCSSValue;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.current {
|
||||
Some(item) => {
|
||||
self.current = unsafe { item.mNext.as_ref() };
|
||||
Some(&item.mValue)
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a nsCSSValueList {
|
||||
type Item = &'a nsCSSValue;
|
||||
type IntoIter = nsCSSValueListIterator<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
nsCSSValueListIterator { current: Some(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutable Iterator of nsCSSValueList.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct nsCSSValueListMutIterator<'a> {
|
||||
current: *mut nsCSSValueList,
|
||||
phantom: PhantomData<&'a mut nsCSSValue>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
|
||||
type Item = &'a mut nsCSSValue;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match unsafe { self.current.as_mut() } {
|
||||
Some(item) => {
|
||||
self.current = item.mNext;
|
||||
Some(&mut item.mValue)
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut nsCSSValueList {
|
||||
type Item = &'a mut nsCSSValue;
|
||||
type IntoIter = nsCSSValueListMutIterator<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
nsCSSValueListMutIterator { current: self as *mut nsCSSValueList,
|
||||
phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl nsCSSValue_Array {
|
||||
/// Return the length of this `nsCSSValue::Array`
|
||||
#[inline]
|
||||
|
|
|
@ -2286,30 +2286,44 @@ fn static_assert() {
|
|||
single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b)
|
||||
in enumerate(items)]
|
||||
if name == "Matrix":
|
||||
pattern = "ComputedMatrix { %s }" % ", ".join(single_patterns)
|
||||
pattern = "(ComputedMatrix { %s })" % ", ".join(single_patterns)
|
||||
else:
|
||||
pattern = "ComputedMatrixWithPercents { %s }" % ", ".join(single_patterns)
|
||||
pattern = "(ComputedMatrixWithPercents { %s })" % ", ".join(single_patterns)
|
||||
elif keyword == "interpolatematrix":
|
||||
pattern = " { from_list: ref list1, to_list: ref list2, progress: percentage3 }"
|
||||
elif keyword == "accumulatematrix":
|
||||
pattern = " { from_list: ref list1, to_list: ref list2, count: integer_to_percentage3 }"
|
||||
else:
|
||||
# Generate contents of pattern from items
|
||||
pattern = ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
|
||||
pattern = "(%s)" % ", ".join([b + str(a+1) for (a,b) in enumerate(items)])
|
||||
|
||||
# First %s substituted with the call to GetArrayItem, the second
|
||||
# %s substituted with the corresponding variable
|
||||
css_value_setters = {
|
||||
"length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)",
|
||||
"percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)",
|
||||
"percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)",
|
||||
# Note: This is an integer type, but we use it as a percentage value in Gecko, so
|
||||
# need to cast it to f32.
|
||||
"integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)",
|
||||
"lop" : "%s.set_lop(%s)",
|
||||
"angle" : "%s.set_angle(%s)",
|
||||
"number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
|
||||
# Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap
|
||||
# because this function is not called on the main thread and
|
||||
# nsCSSValueList_heap is not thread safe.
|
||||
"list" : "%s.set_shared_list(%s.0.as_ref().unwrap().into_iter().map(&convert_to_ns_css_value));",
|
||||
}
|
||||
%>
|
||||
longhands::transform::computed_value::ComputedOperation::${name}(${pattern}) => {
|
||||
longhands::transform::computed_value::ComputedOperation::${name}${pattern} => {
|
||||
bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
|
||||
bindings::Gecko_CSSValue_SetKeyword(
|
||||
bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
|
||||
eCSSKeyword_${keyword}
|
||||
structs::nsCSSKeyword::eCSSKeyword_${keyword}
|
||||
);
|
||||
% for index, item in enumerate(items):
|
||||
% if item == "list":
|
||||
debug_assert!(${item}${index + 1}.0.is_some());
|
||||
% endif
|
||||
${css_value_setters[item] % (
|
||||
"bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
|
||||
item + str(index + 1)
|
||||
|
@ -2317,40 +2331,50 @@ fn static_assert() {
|
|||
% endfor
|
||||
}
|
||||
</%def>
|
||||
pub fn convert_transform(input: &[longhands::transform::computed_value::ComputedOperation],
|
||||
output: &mut structs::root::RefPtr<structs::root::nsCSSValueSharedList>) {
|
||||
use gecko_bindings::structs::nsCSSKeyword::*;
|
||||
use gecko_bindings::sugar::refptr::RefPtr;
|
||||
fn set_single_transform_function(servo_value: &longhands::transform::computed_value::ComputedOperation,
|
||||
gecko_value: &mut structs::nsCSSValue /* output */) {
|
||||
use properties::longhands::transform::computed_value::ComputedMatrix;
|
||||
use properties::longhands::transform::computed_value::ComputedMatrixWithPercents;
|
||||
use properties::longhands::transform::computed_value::ComputedOperation;
|
||||
|
||||
let convert_to_ns_css_value = |item: &ComputedOperation| -> structs::nsCSSValue {
|
||||
let mut value = structs::nsCSSValue::null();
|
||||
Self::set_single_transform_function(item, &mut value);
|
||||
value
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match *servo_value {
|
||||
${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)}
|
||||
${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2
|
||||
+ ["length"] + ["number"])}
|
||||
${transform_function_arm("Skew", "skew", ["angle"] * 2)}
|
||||
${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])}
|
||||
${transform_function_arm("Scale", "scale3d", ["number"] * 3)}
|
||||
${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
|
||||
${transform_function_arm("Perspective", "perspective", ["length"])}
|
||||
${transform_function_arm("InterpolateMatrix", "interpolatematrix",
|
||||
["list"] * 2 + ["percentage"])}
|
||||
${transform_function_arm("AccumulateMatrix", "accumulatematrix",
|
||||
["list"] * 2 + ["integer_to_percentage"])}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn convert_transform(input: &[longhands::transform::computed_value::ComputedOperation],
|
||||
output: &mut structs::root::RefPtr<structs::root::nsCSSValueSharedList>) {
|
||||
use gecko_bindings::sugar::refptr::RefPtr;
|
||||
|
||||
unsafe { output.clear() };
|
||||
|
||||
let list = unsafe {
|
||||
RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32))
|
||||
};
|
||||
|
||||
let mut cur = list.mHead;
|
||||
let mut iter = input.into_iter();
|
||||
while !cur.is_null() {
|
||||
let gecko_value = unsafe { &mut (*cur).mValue };
|
||||
let servo = iter.next().expect("Gecko_NewCSSValueSharedList should create a shared \
|
||||
value list of the same length as the transform vector");
|
||||
unsafe {
|
||||
match *servo {
|
||||
${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)}
|
||||
${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2
|
||||
+ ["length"] + ["number"])}
|
||||
${transform_function_arm("Skew", "skew", ["angle"] * 2)}
|
||||
${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])}
|
||||
${transform_function_arm("Scale", "scale3d", ["number"] * 3)}
|
||||
${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
|
||||
${transform_function_arm("Perspective", "perspective", ["length"])}
|
||||
}
|
||||
cur = (*cur).mNext;
|
||||
let value_list = unsafe { list.mHead.as_mut() };
|
||||
if let Some(value_list) = value_list {
|
||||
for (gecko, servo) in value_list.into_iter().zip(input.into_iter()) {
|
||||
Self::set_single_transform_function(servo, gecko);
|
||||
}
|
||||
}
|
||||
debug_assert!(iter.next().is_none());
|
||||
unsafe { output.set_move(list) };
|
||||
}
|
||||
|
||||
|
@ -2378,60 +2402,94 @@ fn static_assert() {
|
|||
"lop" : "%s.get_lop()",
|
||||
"angle" : "%s.get_angle()",
|
||||
"number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
|
||||
"percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))",
|
||||
"percentage_to_integer" : "bindings::Gecko_CSSValue_GetPercentage(%s) as i32",
|
||||
"list" : "TransformList(Some(convert_shared_list_to_operations(%s)))",
|
||||
}
|
||||
pre_symbols = "("
|
||||
post_symbols = ")"
|
||||
if keyword == "interpolatematrix" or keyword == "accumulatematrix":
|
||||
# We generate this like: "ComputedOperation::InterpolateMatrix {", so the space is
|
||||
# between "InterpolateMatrix"/"AccumulateMatrix" and '{'
|
||||
pre_symbols = " {"
|
||||
post_symbols = "}"
|
||||
elif keyword == "matrix3d":
|
||||
pre_symbols = "(ComputedMatrix {"
|
||||
post_symbols = "})"
|
||||
field_names = None
|
||||
if keyword == "interpolatematrix":
|
||||
field_names = ["from_list", "to_list", "progress"]
|
||||
elif keyword == "accumulatematrix":
|
||||
field_names = ["from_list", "to_list", "count"]
|
||||
%>
|
||||
eCSSKeyword_${keyword} => {
|
||||
ComputedOperation::${name}(
|
||||
% if keyword == "matrix3d":
|
||||
ComputedMatrix {
|
||||
% endif
|
||||
structs::nsCSSKeyword::eCSSKeyword_${keyword} => {
|
||||
ComputedOperation::${name}${pre_symbols}
|
||||
% for index, item in enumerate(items):
|
||||
% if keyword == "matrix3d":
|
||||
m${index / 4 + 1}${index % 4 + 1}:
|
||||
% elif keyword == "interpolatematrix" or keyword == "accumulatematrix":
|
||||
${field_names[index]}:
|
||||
% endif
|
||||
${css_value_getters[item] % (
|
||||
"bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1)
|
||||
)},
|
||||
% endfor
|
||||
% if keyword == "matrix3d":
|
||||
}
|
||||
% endif
|
||||
)
|
||||
${post_symbols}
|
||||
},
|
||||
</%def>
|
||||
pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
|
||||
use app_units::Au;
|
||||
use gecko_bindings::structs::nsCSSKeyword::*;
|
||||
use properties::longhands::transform::computed_value;
|
||||
fn clone_single_transform_function(gecko_value: &structs::nsCSSValue)
|
||||
-> longhands::transform::computed_value::ComputedOperation {
|
||||
use properties::longhands::transform::computed_value::ComputedMatrix;
|
||||
use properties::longhands::transform::computed_value::ComputedOperation;
|
||||
use properties::longhands::transform::computed_value::T as TransformList;
|
||||
use values::computed::Percentage;
|
||||
|
||||
let convert_shared_list_to_operations = |value: &structs::nsCSSValue|
|
||||
-> Vec<ComputedOperation> {
|
||||
debug_assert!(value.mUnit == structs::nsCSSUnit::eCSSUnit_SharedList);
|
||||
let value_list = unsafe {
|
||||
value.mValue.mSharedList.as_ref()
|
||||
.as_mut().expect("List pointer should be non-null").mHead.as_ref()
|
||||
};
|
||||
debug_assert!(value_list.is_some(), "An empty shared list is not allowed");
|
||||
value_list.unwrap().into_iter()
|
||||
.map(|item| Self::clone_single_transform_function(item))
|
||||
.collect()
|
||||
};
|
||||
|
||||
let transform_function = unsafe {
|
||||
bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
|
||||
};
|
||||
|
||||
unsafe {
|
||||
match transform_function {
|
||||
${computed_operation_arm("Matrix", "matrix3d", ["number"] * 16)}
|
||||
${computed_operation_arm("Skew", "skew", ["angle"] * 2)}
|
||||
${computed_operation_arm("Translate", "translate3d", ["lop", "lop", "length"])}
|
||||
${computed_operation_arm("Scale", "scale3d", ["number"] * 3)}
|
||||
${computed_operation_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
|
||||
${computed_operation_arm("Perspective", "perspective", ["length"])}
|
||||
${computed_operation_arm("InterpolateMatrix", "interpolatematrix",
|
||||
["list"] * 2 + ["percentage"])}
|
||||
${computed_operation_arm("AccumulateMatrix", "accumulatematrix",
|
||||
["list"] * 2 + ["percentage_to_integer"])}
|
||||
_ => panic!("We shouldn't set any other transform function types"),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
|
||||
use properties::longhands::transform::computed_value;
|
||||
|
||||
if self.gecko.mSpecifiedTransform.mRawPtr.is_null() {
|
||||
return computed_value::T(None);
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
let mut cur = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead };
|
||||
while !cur.is_null() {
|
||||
let gecko_value = unsafe { &(*cur).mValue };
|
||||
let transform_function = unsafe {
|
||||
bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0))
|
||||
};
|
||||
let servo = unsafe {
|
||||
match transform_function {
|
||||
${computed_operation_arm("Matrix", "matrix3d", ["number"] * 16)}
|
||||
${computed_operation_arm("Skew", "skew", ["angle"] * 2)}
|
||||
${computed_operation_arm("Translate", "translate3d", ["lop", "lop", "length"])}
|
||||
${computed_operation_arm("Scale", "scale3d", ["number"] * 3)}
|
||||
${computed_operation_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])}
|
||||
${computed_operation_arm("Perspective", "perspective", ["length"])}
|
||||
_ => panic!("We shouldn't set any other transform function types"),
|
||||
}
|
||||
};
|
||||
result.push(servo);
|
||||
unsafe { cur = (&*cur).mNext };
|
||||
}
|
||||
computed_value::T(Some(result))
|
||||
let list = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead.as_ref() };
|
||||
let result = list.map(|list| {
|
||||
list.into_iter()
|
||||
.map(|value| Self::clone_single_transform_function(value))
|
||||
.collect()
|
||||
});
|
||||
computed_value::T(result)
|
||||
}
|
||||
|
||||
${impl_transition_time_value('delay', 'Delay')}
|
||||
|
|
|
@ -10,6 +10,7 @@ use app_units::Au;
|
|||
use cssparser::{Color as CSSParserColor, Parser, RGBA, serialize_identifier};
|
||||
use euclid::{Point2D, Size2D};
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
|
||||
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
|
||||
|
@ -637,7 +638,31 @@ impl Animatable for AnimationValue {
|
|||
% endif
|
||||
% endfor
|
||||
_ => {
|
||||
panic!("Expected weighted addition of computed values of the same \
|
||||
panic!("Expected addition of computed values of the same \
|
||||
property, got: {:?}, {:?}", self, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
% if prop.animation_value_type == "discrete":
|
||||
(&AnimationValue::${prop.camel_case}(_),
|
||||
&AnimationValue::${prop.camel_case}(_)) => {
|
||||
Err(())
|
||||
}
|
||||
% else:
|
||||
(&AnimationValue::${prop.camel_case}(ref from),
|
||||
&AnimationValue::${prop.camel_case}(ref to)) => {
|
||||
from.accumulate(to, count).map(AnimationValue::${prop.camel_case})
|
||||
}
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
_ => {
|
||||
panic!("Expected accumulation of computed values of the same \
|
||||
property, got: {:?}, {:?}", self, other);
|
||||
}
|
||||
}
|
||||
|
@ -1510,11 +1535,21 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
|
|||
TransformOperation::Rotate(..) => {
|
||||
result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::zero()));
|
||||
}
|
||||
TransformOperation::Perspective(..) => {
|
||||
TransformOperation::Perspective(..) |
|
||||
TransformOperation::AccumulateMatrix { .. } => {
|
||||
// Perspective: We convert a perspective function into an equivalent
|
||||
// ComputedMatrix, and then decompose/interpolate/recompose these matrices.
|
||||
// AccumulateMatrix: We do interpolation on AccumulateMatrix by reading it as a
|
||||
// ComputedMatrix (with layout information), and then do matrix interpolation.
|
||||
//
|
||||
// Therefore, we use an identity matrix to represent the identity transform list.
|
||||
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function
|
||||
let identity = ComputedMatrix::identity();
|
||||
result.push(TransformOperation::Matrix(identity));
|
||||
}
|
||||
TransformOperation::InterpolateMatrix { .. } => {
|
||||
panic!("Building the identity matrix for InterpolateMatrix is not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1616,8 +1651,13 @@ fn add_weighted_transform_lists(from_list: &[TransformOperation],
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// TODO(gw): Implement matrix decomposition and interpolation
|
||||
result.extend_from_slice(from_list);
|
||||
use values::specified::Percentage;
|
||||
let from_transform_list = TransformList(Some(from_list.to_vec()));
|
||||
let to_transform_list = TransformList(Some(to_list.to_vec()));
|
||||
result.push(
|
||||
TransformOperation::InterpolateMatrix { from_list: from_transform_list,
|
||||
to_list: to_transform_list,
|
||||
progress: Percentage(other_portion as f32) });
|
||||
}
|
||||
|
||||
TransformList(Some(result))
|
||||
|
@ -1889,6 +1929,28 @@ impl From<MatrixDecomposed2D> for ComputedMatrix {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl<'a> From< &'a RawGeckoGfxMatrix4x4> for ComputedMatrix {
|
||||
fn from(m: &'a RawGeckoGfxMatrix4x4) -> ComputedMatrix {
|
||||
ComputedMatrix {
|
||||
m11: m[0], m12: m[1], m13: m[2], m14: m[3],
|
||||
m21: m[4], m22: m[5], m23: m[6], m24: m[7],
|
||||
m31: m[8], m32: m[9], m33: m[10], m34: m[11],
|
||||
m41: m[12], m42: m[13], m43: m[14], m44: m[15],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl From<ComputedMatrix> for RawGeckoGfxMatrix4x4 {
|
||||
fn from(matrix: ComputedMatrix) -> RawGeckoGfxMatrix4x4 {
|
||||
[ matrix.m11, matrix.m12, matrix.m13, matrix.m14,
|
||||
matrix.m21, matrix.m22, matrix.m23, matrix.m24,
|
||||
matrix.m31, matrix.m32, matrix.m33, matrix.m34,
|
||||
matrix.m41, matrix.m42, matrix.m43, matrix.m44 ]
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3d translation.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
@ -2462,6 +2524,41 @@ impl Animatable for TransformList {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
|
||||
match (&self.0, &other.0) {
|
||||
(&Some(ref from_list), &Some(ref to_list)) => {
|
||||
if can_interpolate_list(from_list, to_list) {
|
||||
Ok(add_weighted_transform_lists(from_list, &to_list, count as f64, 1.0))
|
||||
} else {
|
||||
use std::i32;
|
||||
let result = vec![TransformOperation::AccumulateMatrix {
|
||||
from_list: self.clone(),
|
||||
to_list: other.clone(),
|
||||
count: cmp::min(count, i32::MAX as u64) as i32
|
||||
}];
|
||||
Ok(TransformList(Some(result)))
|
||||
}
|
||||
}
|
||||
(&Some(ref from_list), &None) => {
|
||||
Ok(add_weighted_transform_lists(from_list, from_list, count as f64, 0.0))
|
||||
}
|
||||
(&None, &Some(_)) => {
|
||||
// If |self| is 'none' then we are calculating:
|
||||
//
|
||||
// none * |count| + |other|
|
||||
// = none + |other|
|
||||
// = |other|
|
||||
//
|
||||
// Hence the result is just |other|.
|
||||
Ok(other.clone())
|
||||
}
|
||||
_ => {
|
||||
Ok(TransformList(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_zero_value(&self) -> Option<Self> { Some(TransformList(None)) }
|
||||
}
|
||||
|
|
|
@ -688,7 +688,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
use app_units::Au;
|
||||
use values::computed::{LengthOrPercentageOrNumber as ComputedLoPoNumber, LengthOrNumber as ComputedLoN};
|
||||
use values::computed::{LengthOrPercentage as ComputedLoP, Length as ComputedLength};
|
||||
use values::specified::{Angle, Length, LengthOrPercentage};
|
||||
use values::specified::{Angle, Integer, Length, LengthOrPercentage, Percentage};
|
||||
use values::specified::{LengthOrNumber, LengthOrPercentageOrNumber as LoPoNumber, Number};
|
||||
use style_traits::ToCss;
|
||||
use style_traits::values::Css;
|
||||
|
@ -699,7 +699,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
use app_units::Au;
|
||||
use values::CSSFloat;
|
||||
use values::computed;
|
||||
use values::computed::{Length, LengthOrPercentage};
|
||||
use values::computed::{Length, LengthOrPercentage, Percentage};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
@ -756,6 +756,24 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
Scale(CSSFloat, CSSFloat, CSSFloat),
|
||||
Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle),
|
||||
Perspective(computed::Length),
|
||||
// For mismatched transform lists.
|
||||
// A vector of |ComputedOperation| could contain an |InterpolateMatrix| and other
|
||||
// |ComputedOperation|s, and multiple nested |InterpolateMatrix|s is acceptable.
|
||||
// e.g.
|
||||
// [ InterpolateMatrix { from_list: [ InterpolateMatrix { ... },
|
||||
// Scale(...) ],
|
||||
// to_list: [ AccumulateMatrix { from_list: ...,
|
||||
// to_list: [ InterpolateMatrix,
|
||||
// ... ],
|
||||
// count: ... } ],
|
||||
// progress: ... } ]
|
||||
InterpolateMatrix { from_list: T,
|
||||
to_list: T,
|
||||
progress: Percentage },
|
||||
// For accumulate operation of mismatched transform lists.
|
||||
AccumulateMatrix { from_list: T,
|
||||
to_list: T,
|
||||
count: computed::Integer },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -835,6 +853,14 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
///
|
||||
/// The value must be greater than or equal to zero.
|
||||
Perspective(specified::Length),
|
||||
/// A intermediate type for interpolation of mismatched transform lists.
|
||||
InterpolateMatrix { from_list: SpecifiedValue,
|
||||
to_list: SpecifiedValue,
|
||||
progress: Percentage },
|
||||
/// A intermediate type for accumulation of mismatched transform lists.
|
||||
AccumulateMatrix { from_list: SpecifiedValue,
|
||||
to_list: SpecifiedValue,
|
||||
count: Integer },
|
||||
}
|
||||
|
||||
impl ToCss for computed_value::T {
|
||||
|
@ -899,6 +925,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
dest, "rotate3d({}, {}, {}, {})",
|
||||
Css(x), Css(y), Css(z), Css(theta)),
|
||||
Perspective(ref length) => write!(dest, "perspective({})", Css(length)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1440,6 +1467,20 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
Perspective(ref d) => {
|
||||
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
|
||||
}
|
||||
InterpolateMatrix { ref from_list, ref to_list, progress } => {
|
||||
result.push(computed_value::ComputedOperation::InterpolateMatrix {
|
||||
from_list: from_list.to_computed_value(context),
|
||||
to_list: to_list.to_computed_value(context),
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
AccumulateMatrix { ref from_list, ref to_list, count } => {
|
||||
result.push(computed_value::ComputedOperation::AccumulateMatrix {
|
||||
from_list: from_list.to_computed_value(context),
|
||||
to_list: to_list.to_computed_value(context),
|
||||
count: count.value()
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1523,6 +1564,24 @@ ${helpers.predefined_type("scroll-snap-coordinate",
|
|||
ToComputedValue::from_computed_value(d)
|
||||
));
|
||||
}
|
||||
computed_value::ComputedOperation::InterpolateMatrix { ref from_list,
|
||||
ref to_list,
|
||||
progress } => {
|
||||
result.push(SpecifiedOperation::InterpolateMatrix {
|
||||
from_list: SpecifiedValue::from_computed_value(from_list),
|
||||
to_list: SpecifiedValue::from_computed_value(to_list),
|
||||
progress: progress
|
||||
});
|
||||
}
|
||||
computed_value::ComputedOperation::AccumulateMatrix { ref from_list,
|
||||
ref to_list,
|
||||
count } => {
|
||||
result.push(SpecifiedOperation::AccumulateMatrix {
|
||||
from_list: SpecifiedValue::from_computed_value(from_list),
|
||||
to_list: SpecifiedValue::from_computed_value(to_list),
|
||||
count: Integer::new(count)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
result
|
||||
|
|
|
@ -72,12 +72,14 @@ use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
|
|||
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
|
||||
use style::gecko_bindings::structs::IterationCompositeOperation;
|
||||
use style::gecko_bindings::structs::MallocSizeOf;
|
||||
use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
||||
use style::gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||
use style::gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
use style::gecko_bindings::structs::StyleRuleInclusion;
|
||||
use style::gecko_bindings::structs::URLExtraData;
|
||||
use style::gecko_bindings::structs::nsCSSValueSharedList;
|
||||
use style::gecko_bindings::structs::nsCompatibility;
|
||||
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
|
||||
use style::gecko_bindings::structs::nsresult;
|
||||
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
|
||||
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
|
||||
|
@ -1594,6 +1596,28 @@ pub extern "C" fn Servo_GetProperties_Overriding_Animation(element: RawGeckoElem
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_MatrixTransform_Operate(matrix_operator: MatrixTransformOperator,
|
||||
from: *const RawGeckoGfxMatrix4x4,
|
||||
to: *const RawGeckoGfxMatrix4x4,
|
||||
progress: f64,
|
||||
output: *mut RawGeckoGfxMatrix4x4) {
|
||||
use self::MatrixTransformOperator::{Accumulate, Interpolate};
|
||||
use style::properties::longhands::transform::computed_value::ComputedMatrix;
|
||||
|
||||
let from = ComputedMatrix::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix"));
|
||||
let to = ComputedMatrix::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix"));
|
||||
let result = match matrix_operator {
|
||||
Interpolate => from.interpolate(&to, progress),
|
||||
Accumulate => from.accumulate(&to, progress as u64),
|
||||
};
|
||||
|
||||
let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix");
|
||||
if let Ok(result) = result {
|
||||
*output = result.into();
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_ParseStyleAttribute(data: *const nsACString,
|
||||
raw_extra_data: *mut URLExtraData,
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use app_units::Au;
|
||||
use cssparser::RGBA;
|
||||
use style::properties::animated_properties::{Animatable, IntermediateRGBA};
|
||||
use style::properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
||||
use style::properties::longhands::transform::computed_value::T as TransformList;
|
||||
|
||||
fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
|
||||
let from: IntermediateRGBA = from.into();
|
||||
|
@ -11,6 +14,7 @@ fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
|
|||
from.interpolate(&to, progress).unwrap().into()
|
||||
}
|
||||
|
||||
// Color
|
||||
#[test]
|
||||
fn test_rgba_color_interepolation_preserves_transparent() {
|
||||
assert_eq!(interpolate_rgba(RGBA::transparent(),
|
||||
|
@ -54,3 +58,95 @@ fn test_rgba_color_interepolation_out_of_range_clamped_2() {
|
|||
RGBA::from_floats(0.0, 1.0, 0.0, 0.2), 1.5),
|
||||
RGBA::from_floats(0.0, 0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
// Transform
|
||||
#[test]
|
||||
fn test_transform_interpolation_on_translate() {
|
||||
use style::values::computed::{CalcLengthOrPercentage, LengthOrPercentage};
|
||||
|
||||
let from = TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Length(Au(0)),
|
||||
LengthOrPercentage::Length(Au(100)),
|
||||
Au(25))]));
|
||||
let to = TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
||||
LengthOrPercentage::Length(Au(0)),
|
||||
Au(75))]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Length(Au(50)),
|
||||
LengthOrPercentage::Length(Au(50)),
|
||||
Au(50))])));
|
||||
|
||||
let from = TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Percentage(0.5),
|
||||
LengthOrPercentage::Percentage(1.0),
|
||||
Au(25))]));
|
||||
let to = TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
||||
LengthOrPercentage::Length(Au(50)),
|
||||
Au(75))]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Calc(
|
||||
// calc(50px + 25%)
|
||||
CalcLengthOrPercentage::new(Au(50),
|
||||
Some(0.25))),
|
||||
LengthOrPercentage::Calc(
|
||||
// calc(25px + 50%)
|
||||
CalcLengthOrPercentage::new(Au(25),
|
||||
Some(0.5))),
|
||||
Au(50))])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_interpolation_on_scale() {
|
||||
let from = TransformList(Some(vec![TransformOperation::Scale(1.0, 2.0, 1.0)]));
|
||||
let to = TransformList(Some(vec![TransformOperation::Scale(2.0, 4.0, 2.0)]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_interpolation_on_rotate() {
|
||||
use style::values::computed::Angle;
|
||||
|
||||
let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
||||
Angle::from_radians(0.0))]));
|
||||
let to = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
||||
Angle::from_radians(100.0))]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
||||
Angle::from_radians(50.0))])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_interpolation_on_skew() {
|
||||
use style::values::computed::Angle;
|
||||
|
||||
let from = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(0.0),
|
||||
Angle::from_radians(100.0))]));
|
||||
let to = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(100.0),
|
||||
Angle::from_radians(0.0))]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(50.0),
|
||||
Angle::from_radians(50.0))])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_interpolation_on_mismatched_lists() {
|
||||
use style::values::computed::{Angle, LengthOrPercentage, Percentage};
|
||||
|
||||
let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
||||
Angle::from_radians(100.0))]));
|
||||
let to = TransformList(Some(vec![
|
||||
TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
||||
LengthOrPercentage::Length(Au(0)),
|
||||
Au(0))]));
|
||||
assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
||||
TransformList(Some(vec![TransformOperation::InterpolateMatrix {
|
||||
from_list: from.clone(),
|
||||
to_list: to.clone(),
|
||||
progress: Percentage(0.5)
|
||||
}])));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue