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 #include "cc/animation/transform_operations.h" 6 7 #include <algorithm> 8 9 #include "ui/gfx/box_f.h" 10 #include "ui/gfx/transform_util.h" 11 #include "ui/gfx/vector3d_f.h" 12 13 namespace cc { 14 15 TransformOperations::TransformOperations() 16 : decomposed_transform_dirty_(true) { 17 } 18 19 TransformOperations::TransformOperations(const TransformOperations& other) { 20 operations_ = other.operations_; 21 decomposed_transform_dirty_ = other.decomposed_transform_dirty_; 22 if (!decomposed_transform_dirty_) { 23 decomposed_transform_.reset( 24 new gfx::DecomposedTransform(*other.decomposed_transform_.get())); 25 } 26 } 27 28 TransformOperations::~TransformOperations() { 29 } 30 31 gfx::Transform TransformOperations::Apply() const { 32 gfx::Transform to_return; 33 for (size_t i = 0; i < operations_.size(); ++i) 34 to_return.PreconcatTransform(operations_[i].matrix); 35 return to_return; 36 } 37 38 gfx::Transform TransformOperations::Blend(const TransformOperations& from, 39 SkMScalar progress) const { 40 gfx::Transform to_return; 41 BlendInternal(from, progress, &to_return); 42 return to_return; 43 } 44 45 bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box, 46 const TransformOperations& from, 47 SkMScalar min_progress, 48 SkMScalar max_progress, 49 gfx::BoxF* bounds) const { 50 *bounds = box; 51 52 bool from_identity = from.IsIdentity(); 53 bool to_identity = IsIdentity(); 54 if (from_identity && to_identity) 55 return true; 56 57 if (!MatchesTypes(from)) 58 return false; 59 60 size_t num_operations = 61 std::max(from_identity ? 0 : from.operations_.size(), 62 to_identity ? 0 : operations_.size()); 63 for (size_t i = 0; i < num_operations; ++i) { 64 gfx::BoxF bounds_for_operation; 65 const TransformOperation* from_op = 66 from_identity ? NULL : &from.operations_[i]; 67 const TransformOperation* to_op = to_identity ? NULL : &operations_[i]; 68 if (!TransformOperation::BlendedBoundsForBox(*bounds, 69 from_op, 70 to_op, 71 min_progress, 72 max_progress, 73 &bounds_for_operation)) 74 return false; 75 *bounds = bounds_for_operation; 76 } 77 78 return true; 79 } 80 81 bool TransformOperations::MatchesTypes(const TransformOperations& other) const { 82 if (IsIdentity() || other.IsIdentity()) 83 return true; 84 85 if (operations_.size() != other.operations_.size()) 86 return false; 87 88 for (size_t i = 0; i < operations_.size(); ++i) { 89 if (operations_[i].type != other.operations_[i].type 90 && !operations_[i].IsIdentity() 91 && !other.operations_[i].IsIdentity()) 92 return false; 93 } 94 95 return true; 96 } 97 98 bool TransformOperations::CanBlendWith( 99 const TransformOperations& other) const { 100 gfx::Transform dummy; 101 return BlendInternal(other, 0.5, &dummy); 102 } 103 104 void TransformOperations::AppendTranslate(SkMScalar x, 105 SkMScalar y, 106 SkMScalar z) { 107 TransformOperation to_add; 108 to_add.matrix.Translate3d(x, y, z); 109 to_add.type = TransformOperation::TransformOperationTranslate; 110 to_add.translate.x = x; 111 to_add.translate.y = y; 112 to_add.translate.z = z; 113 operations_.push_back(to_add); 114 decomposed_transform_dirty_ = true; 115 } 116 117 void TransformOperations::AppendRotate(SkMScalar x, 118 SkMScalar y, 119 SkMScalar z, 120 SkMScalar degrees) { 121 TransformOperation to_add; 122 to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees); 123 to_add.type = TransformOperation::TransformOperationRotate; 124 to_add.rotate.axis.x = x; 125 to_add.rotate.axis.y = y; 126 to_add.rotate.axis.z = z; 127 to_add.rotate.angle = degrees; 128 operations_.push_back(to_add); 129 decomposed_transform_dirty_ = true; 130 } 131 132 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) { 133 TransformOperation to_add; 134 to_add.matrix.Scale3d(x, y, z); 135 to_add.type = TransformOperation::TransformOperationScale; 136 to_add.scale.x = x; 137 to_add.scale.y = y; 138 to_add.scale.z = z; 139 operations_.push_back(to_add); 140 decomposed_transform_dirty_ = true; 141 } 142 143 void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) { 144 TransformOperation to_add; 145 to_add.matrix.SkewX(x); 146 to_add.matrix.SkewY(y); 147 to_add.type = TransformOperation::TransformOperationSkew; 148 to_add.skew.x = x; 149 to_add.skew.y = y; 150 operations_.push_back(to_add); 151 decomposed_transform_dirty_ = true; 152 } 153 154 void TransformOperations::AppendPerspective(SkMScalar depth) { 155 TransformOperation to_add; 156 to_add.matrix.ApplyPerspectiveDepth(depth); 157 to_add.type = TransformOperation::TransformOperationPerspective; 158 to_add.perspective_depth = depth; 159 operations_.push_back(to_add); 160 decomposed_transform_dirty_ = true; 161 } 162 163 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) { 164 TransformOperation to_add; 165 to_add.matrix = matrix; 166 to_add.type = TransformOperation::TransformOperationMatrix; 167 operations_.push_back(to_add); 168 decomposed_transform_dirty_ = true; 169 } 170 171 void TransformOperations::AppendIdentity() { 172 operations_.push_back(TransformOperation()); 173 } 174 175 bool TransformOperations::IsIdentity() const { 176 for (size_t i = 0; i < operations_.size(); ++i) { 177 if (!operations_[i].IsIdentity()) 178 return false; 179 } 180 return true; 181 } 182 183 bool TransformOperations::BlendInternal(const TransformOperations& from, 184 SkMScalar progress, 185 gfx::Transform* result) const { 186 bool from_identity = from.IsIdentity(); 187 bool to_identity = IsIdentity(); 188 if (from_identity && to_identity) 189 return true; 190 191 if (MatchesTypes(from)) { 192 size_t num_operations = 193 std::max(from_identity ? 0 : from.operations_.size(), 194 to_identity ? 0 : operations_.size()); 195 for (size_t i = 0; i < num_operations; ++i) { 196 gfx::Transform blended; 197 if (!TransformOperation::BlendTransformOperations( 198 from_identity ? 0 : &from.operations_[i], 199 to_identity ? 0 : &operations_[i], 200 progress, 201 &blended)) 202 return false; 203 result->PreconcatTransform(blended); 204 } 205 return true; 206 } 207 208 if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform()) 209 return false; 210 211 gfx::DecomposedTransform to_return; 212 if (!gfx::BlendDecomposedTransforms(&to_return, 213 *decomposed_transform_.get(), 214 *from.decomposed_transform_.get(), 215 progress)) 216 return false; 217 218 *result = ComposeTransform(to_return); 219 return true; 220 } 221 222 bool TransformOperations::ComputeDecomposedTransform() const { 223 if (decomposed_transform_dirty_) { 224 if (!decomposed_transform_) 225 decomposed_transform_.reset(new gfx::DecomposedTransform()); 226 gfx::Transform transform = Apply(); 227 if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform)) 228 return false; 229 decomposed_transform_dirty_ = false; 230 } 231 return true; 232 } 233 234 } // namespace cc 235