| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/animation/transform_operations.h" |
| |
| #include <algorithm> |
| |
| #include "ui/gfx/animation/tween.h" |
| #include "ui/gfx/box_f.h" |
| #include "ui/gfx/transform_util.h" |
| #include "ui/gfx/vector3d_f.h" |
| |
| namespace cc { |
| |
| TransformOperations::TransformOperations() |
| : decomposed_transform_dirty_(true) { |
| } |
| |
| TransformOperations::TransformOperations(const TransformOperations& other) { |
| operations_ = other.operations_; |
| decomposed_transform_dirty_ = other.decomposed_transform_dirty_; |
| if (!decomposed_transform_dirty_) { |
| decomposed_transform_.reset( |
| new gfx::DecomposedTransform(*other.decomposed_transform_.get())); |
| } |
| } |
| |
| TransformOperations::~TransformOperations() { |
| } |
| |
| gfx::Transform TransformOperations::Apply() const { |
| gfx::Transform to_return; |
| for (size_t i = 0; i < operations_.size(); ++i) |
| to_return.PreconcatTransform(operations_[i].matrix); |
| return to_return; |
| } |
| |
| gfx::Transform TransformOperations::Blend(const TransformOperations& from, |
| SkMScalar progress) const { |
| gfx::Transform to_return; |
| BlendInternal(from, progress, &to_return); |
| return to_return; |
| } |
| |
| bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box, |
| const TransformOperations& from, |
| SkMScalar min_progress, |
| SkMScalar max_progress, |
| gfx::BoxF* bounds) const { |
| *bounds = box; |
| |
| bool from_identity = from.IsIdentity(); |
| bool to_identity = IsIdentity(); |
| if (from_identity && to_identity) |
| return true; |
| |
| if (!MatchesTypes(from)) |
| return false; |
| |
| size_t num_operations = |
| std::max(from_identity ? 0 : from.operations_.size(), |
| to_identity ? 0 : operations_.size()); |
| for (size_t i = 0; i < num_operations; ++i) { |
| gfx::BoxF bounds_for_operation; |
| const TransformOperation* from_op = |
| from_identity ? NULL : &from.operations_[i]; |
| const TransformOperation* to_op = to_identity ? NULL : &operations_[i]; |
| if (!TransformOperation::BlendedBoundsForBox(*bounds, |
| from_op, |
| to_op, |
| min_progress, |
| max_progress, |
| &bounds_for_operation)) |
| return false; |
| *bounds = bounds_for_operation; |
| } |
| |
| return true; |
| } |
| |
| bool TransformOperations::AffectsScale() const { |
| for (size_t i = 0; i < operations_.size(); ++i) { |
| if (operations_[i].type == TransformOperation::TransformOperationScale) |
| return true; |
| if (operations_[i].type == TransformOperation::TransformOperationMatrix && |
| !operations_[i].matrix.IsIdentityOrTranslation()) |
| return true; |
| } |
| return false; |
| } |
| |
| bool TransformOperations::IsTranslation() const { |
| for (size_t i = 0; i < operations_.size(); ++i) { |
| switch (operations_[i].type) { |
| case TransformOperation::TransformOperationIdentity: |
| case TransformOperation::TransformOperationTranslate: |
| continue; |
| case TransformOperation::TransformOperationMatrix: |
| if (!operations_[i].matrix.IsIdentityOrTranslation()) |
| return false; |
| continue; |
| case TransformOperation::TransformOperationRotate: |
| case TransformOperation::TransformOperationScale: |
| case TransformOperation::TransformOperationSkew: |
| case TransformOperation::TransformOperationPerspective: |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool TransformOperations::MaximumScale(const TransformOperations& from, |
| SkMScalar min_progress, |
| SkMScalar max_progress, |
| float* max_scale) const { |
| if (!MatchesTypes(from)) |
| return false; |
| |
| gfx::Vector3dF from_scale; |
| gfx::Vector3dF to_scale; |
| |
| if (!from.ScaleComponent(&from_scale) || !ScaleComponent(&to_scale)) |
| return false; |
| |
| gfx::Vector3dF scale_at_min_progress( |
| std::abs(gfx::Tween::FloatValueBetween( |
| min_progress, from_scale.x(), to_scale.x())), |
| std::abs(gfx::Tween::FloatValueBetween( |
| min_progress, from_scale.y(), to_scale.y())), |
| std::abs(gfx::Tween::FloatValueBetween( |
| min_progress, from_scale.z(), to_scale.z()))); |
| gfx::Vector3dF scale_at_max_progress( |
| std::abs(gfx::Tween::FloatValueBetween( |
| max_progress, from_scale.x(), to_scale.x())), |
| std::abs(gfx::Tween::FloatValueBetween( |
| max_progress, from_scale.y(), to_scale.y())), |
| std::abs(gfx::Tween::FloatValueBetween( |
| max_progress, from_scale.z(), to_scale.z()))); |
| |
| gfx::Vector3dF max_scale_3d = scale_at_min_progress; |
| max_scale_3d.SetToMax(scale_at_max_progress); |
| *max_scale = |
| std::max(max_scale_3d.x(), std::max(max_scale_3d.y(), max_scale_3d.z())); |
| return true; |
| } |
| |
| bool TransformOperations::ScaleComponent(gfx::Vector3dF* scale) const { |
| *scale = gfx::Vector3dF(1.f, 1.f, 1.f); |
| bool has_scale_component = false; |
| for (size_t i = 0; i < operations_.size(); ++i) { |
| switch (operations_[i].type) { |
| case TransformOperation::TransformOperationIdentity: |
| case TransformOperation::TransformOperationTranslate: |
| continue; |
| case TransformOperation::TransformOperationMatrix: |
| if (!operations_[i].matrix.IsIdentityOrTranslation()) |
| return false; |
| continue; |
| case TransformOperation::TransformOperationRotate: |
| case TransformOperation::TransformOperationSkew: |
| case TransformOperation::TransformOperationPerspective: |
| return false; |
| case TransformOperation::TransformOperationScale: |
| if (has_scale_component) |
| return false; |
| has_scale_component = true; |
| scale->Scale(operations_[i].scale.x, |
| operations_[i].scale.y, |
| operations_[i].scale.z); |
| } |
| } |
| return true; |
| } |
| |
| bool TransformOperations::MatchesTypes(const TransformOperations& other) const { |
| if (IsIdentity() || other.IsIdentity()) |
| return true; |
| |
| if (operations_.size() != other.operations_.size()) |
| return false; |
| |
| for (size_t i = 0; i < operations_.size(); ++i) { |
| if (operations_[i].type != other.operations_[i].type |
| && !operations_[i].IsIdentity() |
| && !other.operations_[i].IsIdentity()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool TransformOperations::CanBlendWith( |
| const TransformOperations& other) const { |
| gfx::Transform dummy; |
| return BlendInternal(other, 0.5, &dummy); |
| } |
| |
| void TransformOperations::AppendTranslate(SkMScalar x, |
| SkMScalar y, |
| SkMScalar z) { |
| TransformOperation to_add; |
| to_add.matrix.Translate3d(x, y, z); |
| to_add.type = TransformOperation::TransformOperationTranslate; |
| to_add.translate.x = x; |
| to_add.translate.y = y; |
| to_add.translate.z = z; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendRotate(SkMScalar x, |
| SkMScalar y, |
| SkMScalar z, |
| SkMScalar degrees) { |
| TransformOperation to_add; |
| to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees); |
| to_add.type = TransformOperation::TransformOperationRotate; |
| to_add.rotate.axis.x = x; |
| to_add.rotate.axis.y = y; |
| to_add.rotate.axis.z = z; |
| to_add.rotate.angle = degrees; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) { |
| TransformOperation to_add; |
| to_add.matrix.Scale3d(x, y, z); |
| to_add.type = TransformOperation::TransformOperationScale; |
| to_add.scale.x = x; |
| to_add.scale.y = y; |
| to_add.scale.z = z; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) { |
| TransformOperation to_add; |
| to_add.matrix.SkewX(x); |
| to_add.matrix.SkewY(y); |
| to_add.type = TransformOperation::TransformOperationSkew; |
| to_add.skew.x = x; |
| to_add.skew.y = y; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendPerspective(SkMScalar depth) { |
| TransformOperation to_add; |
| to_add.matrix.ApplyPerspectiveDepth(depth); |
| to_add.type = TransformOperation::TransformOperationPerspective; |
| to_add.perspective_depth = depth; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendMatrix(const gfx::Transform& matrix) { |
| TransformOperation to_add; |
| to_add.matrix = matrix; |
| to_add.type = TransformOperation::TransformOperationMatrix; |
| operations_.push_back(to_add); |
| decomposed_transform_dirty_ = true; |
| } |
| |
| void TransformOperations::AppendIdentity() { |
| operations_.push_back(TransformOperation()); |
| } |
| |
| bool TransformOperations::IsIdentity() const { |
| for (size_t i = 0; i < operations_.size(); ++i) { |
| if (!operations_[i].IsIdentity()) |
| return false; |
| } |
| return true; |
| } |
| |
| bool TransformOperations::BlendInternal(const TransformOperations& from, |
| SkMScalar progress, |
| gfx::Transform* result) const { |
| bool from_identity = from.IsIdentity(); |
| bool to_identity = IsIdentity(); |
| if (from_identity && to_identity) |
| return true; |
| |
| if (MatchesTypes(from)) { |
| size_t num_operations = |
| std::max(from_identity ? 0 : from.operations_.size(), |
| to_identity ? 0 : operations_.size()); |
| for (size_t i = 0; i < num_operations; ++i) { |
| gfx::Transform blended; |
| if (!TransformOperation::BlendTransformOperations( |
| from_identity ? 0 : &from.operations_[i], |
| to_identity ? 0 : &operations_[i], |
| progress, |
| &blended)) |
| return false; |
| result->PreconcatTransform(blended); |
| } |
| return true; |
| } |
| |
| if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform()) |
| return false; |
| |
| gfx::DecomposedTransform to_return; |
| if (!gfx::BlendDecomposedTransforms(&to_return, |
| *decomposed_transform_.get(), |
| *from.decomposed_transform_.get(), |
| progress)) |
| return false; |
| |
| *result = ComposeTransform(to_return); |
| return true; |
| } |
| |
| bool TransformOperations::ComputeDecomposedTransform() const { |
| if (decomposed_transform_dirty_) { |
| if (!decomposed_transform_) |
| decomposed_transform_.reset(new gfx::DecomposedTransform()); |
| gfx::Transform transform = Apply(); |
| if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform)) |
| return false; |
| decomposed_transform_dirty_ = false; |
| } |
| return true; |
| } |
| |
| } // namespace cc |