Bug 1335998 - Part 2: Define ComputedOperation::AccmulateMatrix. draft
authorBoris Chiou <boris.chiou@gmail.com>
Wed, 31 May 2017 15:38:14 +0800
changeset 589577 a975479666b4e9d07f04b8045d358d071b312012
parent 589576 c1f529e3a30eab69b1d17365fa12a0d9ac492599
child 589578 f6613b687230a8fc9551209ce88bff8a4c5dd321
push id62434
push userbmo:boris.chiou@gmail.com
push dateTue, 06 Jun 2017 12:26:50 +0000
bugs1335998
milestone55.0a1
Bug 1335998 - Part 2: Define ComputedOperation::AccmulateMatrix. If we set the composite operation to accumulate, we may need to accumulate two mismatched transform lists, and then to interpolate them. In order to accumulate two mismatched transform lists, we also need the reference box information, so use the same idea as that for interpolation. i.e. use AccmulateMatrix to store it temporarily, and convert it into matrix later. MozReview-Commit-ID: 89XO0NRNL0Q
servo/components/layout/fragment.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -2891,18 +2891,20 @@ impl Fragment {
                 transform::ComputedOperation::MatrixWithPercents(_) => {
                     // `-moz-transform` is not implemented in Servo yet.
                     unreachable!()
                 }
                 transform::ComputedOperation::Skew(theta_x, theta_y) => {
                     Matrix4D::create_skew(Radians::new(theta_x.radians()),
                                           Radians::new(theta_y.radians()))
                 }
-                transform::ComputedOperation::InterpolateMatrix { .. } => {
-                    // TODO: Convert InterpolateMatrix into a valid Matrix4D by the reference box.
+                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);
         }
 
         Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2321,17 +2321,19 @@ fn static_assert() {
                                              + ["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"])}
                     _ => {
                         // TODO: Convert ComputedOperation::InterpolateMatrix into
-                        //       eCSSKeyword_interpolatematrix.
+                        //       eCSSKeyword_interpolatematrix, and convert
+                        //       ComputedOperation::AccumulateMatrix into
+                        //       eCSSKeyword_accumulatematrix in the patch series.
                         gecko_value.mUnit = structs::nsCSSUnit::eCSSUnit_None;
                     }
                 }
                 cur = (*cur).mNext;
             }
         }
         debug_assert!(iter.next().is_none());
         unsafe { output.set_move(list) };
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -630,17 +630,41 @@ impl Animatable for AnimationValue {
                         (&AnimationValue::${prop.camel_case}(ref from),
                          &AnimationValue::${prop.camel_case}(ref to)) => {
                             from.add(to).map(AnimationValue::${prop.camel_case})
                         }
                     % endif
                 % 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 Expected accumulation of computed values of the same \
                         property, got: {:?}, {:?}", self, other);
             }
         }
     }
 
     fn get_zero_value(&self) -> Option<Self> {
         match self {
             % for prop in data.longhands:
@@ -1629,22 +1653,23 @@ fn build_identity_transform_list(list: &
                                                           Au(0)));
             }
             TransformOperation::Scale(..) => {
                 result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
             }
             TransformOperation::Rotate(..) => {
                 result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::zero()));
             }
-            TransformOperation::Perspective(..) => {
+            TransformOperation::Perspective(..) |
+            TransformOperation::AccumulateMatrix { .. } => {
                 // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
                 let identity = ComputedMatrix::identity();
                 result.push(TransformOperation::Matrix(identity));
             }
-            TransformOperation::InterpolateMatrix { .. } => panic!("Not supported"),
+            _ => panic!("Not supported"),
         }
     }
 
     result
 }
 
 /// A wrapper for calling add_weighted that interpolates the distance of the two values from
 /// an initial_value and uses that to produce an interpolated value.
@@ -2588,16 +2613,55 @@ impl Animatable for TransformList {
             }
             _ => {
                 Ok(TransformList(None))
             }
         }
     }
 
     #[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(),
+                        // For accumulation, we need to increase accumulation count for |other| if
+                        // we do matrix decomposition/interpolation/recomposition on Gecko. After
+                        // changing to Servo matrix decomposition/interpolation/recomposition, we
+                        // don't need to increase one. I will remove this in the patch series.
+                        count: cmp::min(count + 1, 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)) }
 }
 
 impl<T, U> Animatable for Either<T, U>
         where T: Animatable + Copy, U: Animatable + Copy,
 {
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -1108,17 +1108,17 @@
 
 <%helpers:longhand name="transform" extra_prefixes="webkit"
                    animation_value_type="ComputedValue"
                    flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
                    spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
     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, Percentage};
+    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;
 
     use std::fmt;
 
     pub mod computed_value {
         use app_units::Au;
@@ -1182,24 +1182,28 @@
             Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle),
             Perspective(computed::Length),
             // For mismatched transform lists.
             // A vector of |ComptuedOperation| could contain an |InterpolateMatrix| and other
             // |ComputedOperation|s, and multiple nested |InterpolateMatrix|s is acceptable.
             // e.g.
             // [ InterpolateMatrix { from_list: [ InterpolateMatrix { ... },
             //                                    Scale(...) ],
-            //                       to_list: [ InterpolateMatrix { from_list: ...,
-            //                                                      to_list: [ InterpolateMatrix,
+            //                       to_list: [ AccumulateMatrix { from_list: ...,
+            //                                                     to_list: [ InterpolateMatrix,
             //                                                                 ... ],
-            //                                                      progress: ... } ],
+            //                                                     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)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub Option<Vec<ComputedOperation>>);
     }
 
     /// Describes a single parsed
@@ -1273,16 +1277,20 @@
         /// [ยง 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective).
         ///
         /// 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 {
         fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
             // TODO(pcwalton)
             Ok(())
         }
     }
@@ -1886,16 +1894,23 @@
                     }
                     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()
+                        });
+                    }
                 };
             }
 
             computed_value::T(Some(result))
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
@@ -1978,16 +1993,25 @@
                                                                                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
             }).unwrap_or(Vec::new()))
         }
     }
 
     // Converts computed LengthOrPercentageOrNumber into computed