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