1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CC_ANIMATION_TRANSFORM_OPERATIONS_H_
6 #define CC_ANIMATION_TRANSFORM_OPERATIONS_H_
7 
8 #include <memory>
9 #include <unordered_map>
10 #include <vector>
11 
12 #include "base/check_op.h"
13 #include "base/gtest_prod_util.h"
14 #include "cc/animation/animation_export.h"
15 #include "cc/animation/transform_operation.h"
16 #include "ui/gfx/transform.h"
17 
18 namespace gfx {
19 class BoxF;
20 struct DecomposedTransform;
21 }
22 
23 namespace cc {
24 
25 // Transform operations are a decomposed transformation matrix. It can be
26 // applied to obtain a gfx::Transform at any time, and can be blended
27 // intelligently with other transform operations, so long as they represent the
28 // same decomposition. For example, if we have a transform that is made up of
29 // a rotation followed by skew, it can be blended intelligently with another
30 // transform made up of a rotation followed by a skew. Blending is possible if
31 // we have two dissimilar sets of transform operations, but the effect may not
32 // be what was intended. For more information, see the comments for the blend
33 // function below.
34 class CC_ANIMATION_EXPORT TransformOperations {
35  public:
36   TransformOperations();
37   TransformOperations(const TransformOperations& other);
38   ~TransformOperations();
39 
40   TransformOperations& operator=(const TransformOperations& other);
41 
42   // Returns a transformation matrix representing these transform operations.
43   gfx::Transform Apply() const;
44 
45   // Returns a transformation matrix representing the set of transform
46   // operations from index |start| to the end of the list.
47   gfx::Transform ApplyRemaining(size_t start) const;
48 
49   // Given another set of transform operations and a progress in the range
50   // [0, 1], returns a transformation matrix representing the intermediate
51   // value. If this->MatchesTypes(from), then each of the operations are
52   // blended separately and then combined. Otherwise, the two sets of
53   // transforms are baked to matrices (using apply), and the matrices are
54   // then decomposed and interpolated. For more information, see
55   // http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition.
56   //
57   // If either of the matrices are non-decomposable for the blend, Blend applies
58   // discrete interpolation between them based on the progress value.
59   TransformOperations Blend(const TransformOperations& from,
60                             SkScalar progress) const;
61 
62   // Sets |bounds| be the bounding box for the region within which |box| will
63   // exist when it is transformed by the result of calling Blend on |from| and
64   // with progress in the range [min_progress, max_progress]. If this region
65   // cannot be computed, returns false.
66   bool BlendedBoundsForBox(const gfx::BoxF& box,
67                            const TransformOperations& from,
68                            SkScalar min_progress,
69                            SkScalar max_progress,
70                            gfx::BoxF* bounds) const;
71 
72   // Returns true if these operations are only translations.
73   bool IsTranslation() const;
74 
75   // Returns false if the operations affect 2d axis alignment.
76   bool PreservesAxisAlignment() const;
77 
78   // Returns true if this operation and its descendants have the same types
79   // as other and its descendants.
80   bool MatchesTypes(const TransformOperations& other) const;
81 
82   // Returns the number of matching transform operations at the start of the
83   // transform lists. If one list is shorter but pairwise compatible, it will be
84   // extended with matching identity operators per spec
85   // (https://drafts.csswg.org/css-transforms/#interpolation-of-transforms).
86   size_t MatchingPrefixLength(const TransformOperations& other) const;
87 
88   // Returns true if these operations can be blended. It will only return
89   // false if we must resort to matrix interpolation, and matrix interpolation
90   // fails (this can happen if either matrix cannot be decomposed).
91   bool CanBlendWith(const TransformOperations& other) const;
92 
93   // If none of these operations have a perspective component, sets |scale| to
94   // be the product of the scale component of every operation. Otherwise,
95   // returns false.
96   bool ScaleComponent(SkScalar* scale) const;
97 
98   void AppendTranslate(SkScalar x, SkScalar y, SkScalar z);
99   void AppendRotate(SkScalar x, SkScalar y, SkScalar z, SkScalar degrees);
100   void AppendScale(SkScalar x, SkScalar y, SkScalar z);
101   void AppendSkewX(SkScalar x);
102   void AppendSkewY(SkScalar y);
103   void AppendSkew(SkScalar x, SkScalar y);
104   void AppendPerspective(SkScalar depth);
105   void AppendMatrix(const gfx::Transform& matrix);
106   void AppendIdentity();
107   void Append(const TransformOperation& operation);
108   bool IsIdentity() const;
109 
size()110   size_t size() const { return operations_.size(); }
111 
at(size_t index)112   const TransformOperation& at(size_t index) const {
113     DCHECK_LT(index, size());
114     return operations_[index];
115   }
at(size_t index)116   TransformOperation& at(size_t index) {
117     DCHECK_LT(index, size());
118     return operations_[index];
119   }
120 
121   bool ApproximatelyEqual(const TransformOperations& other,
122                           SkScalar tolerance) const;
123 
124  private:
125   FRIEND_TEST_ALL_PREFIXES(TransformOperationsTest, TestDecompositionCache);
126 
127   bool BlendInternal(const TransformOperations& from,
128                      SkScalar progress,
129                      TransformOperations* result) const;
130 
131   std::vector<TransformOperation> operations_;
132 
133   bool ComputeDecomposedTransform(size_t start_offset) const;
134 
135   // For efficiency, we cache the decomposed transforms.
136   mutable std::unordered_map<size_t, std::unique_ptr<gfx::DecomposedTransform>>
137       decomposed_transforms_;
138 };
139 
140 }  // namespace cc
141 
142 #endif  // CC_ANIMATION_TRANSFORM_OPERATIONS_H_
143