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