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/animation/tween.h" 10 #include "ui/gfx/box_f.h" 11 #include "ui/gfx/transform_util.h" 12 #include "ui/gfx/vector3d_f.h" 13 14 namespace cc { 15 16 TransformOperations::TransformOperations() 17 : decomposed_transform_dirty_(true) { 18 } 19 20 TransformOperations::TransformOperations(const TransformOperations& other) { 21 operations_ = other.operations_; 22 decomposed_transform_dirty_ = other.decomposed_transform_dirty_; 23 if (!decomposed_transform_dirty_) { 24 decomposed_transform_.reset( 25 new gfx::DecomposedTransform(*other.decomposed_transform_.get())); 26 } 27 } 28 29 TransformOperations::~TransformOperations() { 30 } 31 32 gfx::Transform TransformOperations::Apply() const { 33 gfx::Transform to_return; 34 for (size_t i = 0; i < operations_.size(); ++i) 35 to_return.PreconcatTransform(operations_[i].matrix); 36 return to_return; 37 } 38 39 gfx::Transform TransformOperations::Blend(const TransformOperations& from, 40 SkMScalar progress) const { 41 gfx::Transform to_return; 42 BlendInternal(from, progress, &to_return); 43 return to_return; 44 } 45 46 bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box, 47 const TransformOperations& from, 48 SkMScalar min_progress, 49 SkMScalar max_progress, 50 gfx::BoxF* bounds) const { 51 *bounds = box; 52 53 bool from_identity = from.IsIdentity(); 54 bool to_identity = IsIdentity(); 55 if (from_identity && to_identity) 56 return true; 57 58 if (!MatchesTypes(from)) 59 return false; 60 61 size_t num_operations = 62 std::max(from_identity ? 0 : from.operations_.size(), 63 to_identity ? 0 : operations_.size()); 64 65 // Because we are squashing all of the matrices together when applying 66 // them to the animation, we must apply them in reverse order when 67 // not squashing them. 68 for (int i = num_operations - 1; i >= 0; --i) { 69 gfx::BoxF bounds_for_operation; 70 const TransformOperation* from_op = 71 from_identity ? NULL : &from.operations_[i]; 72 const TransformOperation* to_op = to_identity ? NULL : &operations_[i]; 73 if (!TransformOperation::BlendedBoundsForBox(*bounds, 74 from_op, 75 to_op, 76 min_progress, 77 max_progress, 78 &bounds_for_operation)) 79 return false; 80 *bounds = bounds_for_operation; 81 } 82 83 return true; 84 } 85 86 bool TransformOperations::AffectsScale() const { 87 for (size_t i = 0; i < operations_.size(); ++i) { 88 if (operations_[i].type == TransformOperation::TransformOperationScale) 89 return true; 90 if (operations_[i].type == TransformOperation::TransformOperationMatrix && 91 !operations_[i].matrix.IsIdentityOrTranslation()) 92 return true; 93 } 94 return false; 95 } 96 97 bool TransformOperations::IsTranslation() const { 98 for (size_t i = 0; i < operations_.size(); ++i) { 99 switch (operations_[i].type) { 100 case TransformOperation::TransformOperationIdentity: 101 case TransformOperation::TransformOperationTranslate: 102 continue; 103 case TransformOperation::TransformOperationMatrix: 104 if (!operations_[i].matrix.IsIdentityOrTranslation()) 105 return false; 106 continue; 107 case TransformOperation::TransformOperationRotate: 108 case TransformOperation::TransformOperationScale: 109 case TransformOperation::TransformOperationSkew: 110 case TransformOperation::TransformOperationPerspective: 111 return false; 112 } 113 } 114 return true; 115 } 116 117 bool TransformOperations::MaximumScale(const TransformOperations& from, 118 SkMScalar min_progress, 119 SkMScalar max_progress, 120 float* max_scale) const { 121 if (!MatchesTypes(from)) 122 return false; 123 124 gfx::Vector3dF from_scale; 125 gfx::Vector3dF to_scale; 126 127 if (!from.ScaleComponent(&from_scale) || !ScaleComponent(&to_scale)) 128 return false; 129 130 gfx::Vector3dF scale_at_min_progress( 131 std::abs(gfx::Tween::FloatValueBetween( 132 min_progress, from_scale.x(), to_scale.x())), 133 std::abs(gfx::Tween::FloatValueBetween( 134 min_progress, from_scale.y(), to_scale.y())), 135 std::abs(gfx::Tween::FloatValueBetween( 136 min_progress, from_scale.z(), to_scale.z()))); 137 gfx::Vector3dF scale_at_max_progress( 138 std::abs(gfx::Tween::FloatValueBetween( 139 max_progress, from_scale.x(), to_scale.x())), 140 std::abs(gfx::Tween::FloatValueBetween( 141 max_progress, from_scale.y(), to_scale.y())), 142 std::abs(gfx::Tween::FloatValueBetween( 143 max_progress, from_scale.z(), to_scale.z()))); 144 145 gfx::Vector3dF max_scale_3d = scale_at_min_progress; 146 max_scale_3d.SetToMax(scale_at_max_progress); 147 *max_scale = 148 std::max(max_scale_3d.x(), std::max(max_scale_3d.y(), max_scale_3d.z())); 149 return true; 150 } 151 152 bool TransformOperations::ScaleComponent(gfx::Vector3dF* scale) const { 153 *scale = gfx::Vector3dF(1.f, 1.f, 1.f); 154 bool has_scale_component = false; 155 for (size_t i = 0; i < operations_.size(); ++i) { 156 switch (operations_[i].type) { 157 case TransformOperation::TransformOperationIdentity: 158 case TransformOperation::TransformOperationTranslate: 159 continue; 160 case TransformOperation::TransformOperationMatrix: 161 if (!operations_[i].matrix.IsIdentityOrTranslation()) 162 return false; 163 continue; 164 case TransformOperation::TransformOperationRotate: 165 case TransformOperation::TransformOperationSkew: 166 case TransformOperation::TransformOperationPerspective: 167 return false; 168 case TransformOperation::TransformOperationScale: 169 if (has_scale_component) 170 return false; 171 has_scale_component = true; 172 scale->Scale(operations_[i].scale.x, 173 operations_[i].scale.y, 174 operations_[i].scale.z); 175 } 176 } 177 return true; 178 } 179 180 bool TransformOperations::MatchesTypes(const TransformOperations& other) const { 181 if (IsIdentity() || other.IsIdentity()) 182 return true; 183 184 if (operations_.size() != other.operations_.size()) 185 return false; 186 187 for (size_t i = 0; i < operations_.size(); ++i) { 188 if (operations_[i].type != other.operations_[i].type 189 && !operations_[i].IsIdentity() 190 && !other.operations_[i].IsIdentity()) 191 return false; 192 } 193 194 return true; 195 } 196 197 bool TransformOperations::CanBlendWith( 198 const TransformOperations& other) const { 199 gfx::Transform dummy; 200 return BlendInternal(other, 0.5, &dummy); 201 } 202 203 void TransformOperations::AppendTranslate(SkMScalar x, 204 SkMScalar y, 205 SkMScalar z) { 206 TransformOperation to_add; 207 to_add.matrix.Translate3d(x, y, z); 208 to_add.type = TransformOperation::TransformOperationTranslate; 209 to_add.translate.x = x; 210 to_add.translate.y = y; 211 to_add.translate.z = z; 212 operations_.push_back(to_add); 213 decomposed_transform_dirty_ = true; 214 } 215 216 void TransformOperations::AppendRotate(SkMScalar x, 217 SkMScalar y, 218 SkMScalar z, 219 SkMScalar degrees) { 220 TransformOperation to_add; 221 to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees); 222 to_add.type = TransformOperation::TransformOperationRotate; 223 to_add.rotate.axis.x = x; 224 to_add.rotate.axis.y = y; 225 to_add.rotate.axis.z = z; 226 to_add.rotate.angle = degrees; 227 operations_.push_back(to_add); 228 decomposed_transform_dirty_ = true; 229 } 230 231 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) { 232 TransformOperation to_add; 233 to_add.matrix.Scale3d(x, y, z); 234 to_add.type = TransformOperation::TransformOperationScale; 235 to_add.scale.x = x; 236 to_add.scale.y = y; 237 to_add.scale.z = z; 238 operations_.push_back(to_add); 239 decomposed_transform_dirty_ = true; 240 } 241 242 void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) { 243 TransformOperation to_add; 244 to_add.matrix.SkewX(x); 245 to_add.matrix.SkewY(y); 246 to_add.type = TransformOperation::TransformOperationSkew; 247 to_add.skew.x = x; 248 to_add.skew.y = y; 249 operations_.push_back(to_add); 250 decomposed_transform_dirty_ = true; 251 } 252 253 void TransformOperations::AppendPerspective(SkMScalar depth) { 254 TransformOperation to_add; 255 to_add.matrix.ApplyPerspectiveDepth(depth); 256 to_add.type = TransformOperation::TransformOperationPerspective; 257 to_add.perspective_depth = depth; 258 operations_.push_back(to_add); 259 decomposed_transform_dirty_ = true; 260 } 261 262 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) { 263 TransformOperation to_add; 264 to_add.matrix = matrix; 265 to_add.type = TransformOperation::TransformOperationMatrix; 266 operations_.push_back(to_add); 267 decomposed_transform_dirty_ = true; 268 } 269 270 void TransformOperations::AppendIdentity() { 271 operations_.push_back(TransformOperation()); 272 } 273 274 bool TransformOperations::IsIdentity() const { 275 for (size_t i = 0; i < operations_.size(); ++i) { 276 if (!operations_[i].IsIdentity()) 277 return false; 278 } 279 return true; 280 } 281 282 bool TransformOperations::BlendInternal(const TransformOperations& from, 283 SkMScalar progress, 284 gfx::Transform* result) const { 285 bool from_identity = from.IsIdentity(); 286 bool to_identity = IsIdentity(); 287 if (from_identity && to_identity) 288 return true; 289 290 if (MatchesTypes(from)) { 291 size_t num_operations = 292 std::max(from_identity ? 0 : from.operations_.size(), 293 to_identity ? 0 : operations_.size()); 294 for (size_t i = 0; i < num_operations; ++i) { 295 gfx::Transform blended; 296 if (!TransformOperation::BlendTransformOperations( 297 from_identity ? 0 : &from.operations_[i], 298 to_identity ? 0 : &operations_[i], 299 progress, 300 &blended)) 301 return false; 302 result->PreconcatTransform(blended); 303 } 304 return true; 305 } 306 307 if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform()) 308 return false; 309 310 gfx::DecomposedTransform to_return; 311 if (!gfx::BlendDecomposedTransforms(&to_return, 312 *decomposed_transform_.get(), 313 *from.decomposed_transform_.get(), 314 progress)) 315 return false; 316 317 *result = ComposeTransform(to_return); 318 return true; 319 } 320 321 bool TransformOperations::ComputeDecomposedTransform() const { 322 if (decomposed_transform_dirty_) { 323 if (!decomposed_transform_) 324 decomposed_transform_.reset(new gfx::DecomposedTransform()); 325 gfx::Transform transform = Apply(); 326 if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform)) 327 return false; 328 decomposed_transform_dirty_ = false; 329 } 330 return true; 331 } 332 333 } // namespace cc 334