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:
bors-servo 2017-06-06 23:54:41 -07:00 committed by GitHub
commit f4a720483d
10 changed files with 4840 additions and 4085 deletions

View file

@ -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)) }
}