// 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 #include "ui/gfx/animation/tween.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/vector3d_f.h" #include "ui/gfx/transform_util.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()); // Because we are squashing all of the matrices together when applying // them to the animation, we must apply them in reverse order when // not squashing them. for (int i = num_operations - 1; i >= 0; --i) { gfx::BoxF bounds_for_operation; const TransformOperation* from_op = from_identity ? nullptr : &from.operations_[i]; const TransformOperation* to_op = to_identity ? nullptr : &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::PreservesAxisAlignment() const { for (size_t i = 0; i < operations_.size(); ++i) { switch (operations_[i].type) { case TransformOperation::TransformOperationIdentity: case TransformOperation::TransformOperationTranslate: case TransformOperation::TransformOperationScale: continue; case TransformOperation::TransformOperationMatrix: if (!operations_[i].matrix.IsIdentity() && !operations_[i].matrix.IsScaleOrTranslation()) return false; continue; case TransformOperation::TransformOperationRotate: case TransformOperation::TransformOperationSkew: case TransformOperation::TransformOperationPerspective: return false; } } return true; } 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::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