summaryrefslogtreecommitdiffstats
path: root/cc/animation
diff options
context:
space:
mode:
authorajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 23:56:02 +0000
committerajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 23:56:02 +0000
commit6248bcc8d0717d4af4836299f54f51d0939042a8 (patch)
treef15c96ee198d1a424a46847227fd2a1ae7e0ca78 /cc/animation
parent22646b0458de4b49e9f84d051d5fb24316ad0271 (diff)
downloadchromium_src-6248bcc8d0717d4af4836299f54f51d0939042a8.zip
chromium_src-6248bcc8d0717d4af4836299f54f51d0939042a8.tar.gz
chromium_src-6248bcc8d0717d4af4836299f54f51d0939042a8.tar.bz2
cc: Compute the maximum scale of animations
This adds MaximumScale and HasOnlyTranslationTransforms methods to LayerAnimationController. BUG=224913 Review URL: https://codereview.chromium.org/214003004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261583 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/animation')
-rw-r--r--cc/animation/animation_curve.h7
-rw-r--r--cc/animation/keyframed_animation_curve.cc32
-rw-r--r--cc/animation/keyframed_animation_curve.h2
-rw-r--r--cc/animation/keyframed_animation_curve_unittest.cc63
-rw-r--r--cc/animation/layer_animation_controller.cc33
-rw-r--r--cc/animation/layer_animation_controller.h6
-rw-r--r--cc/animation/layer_animation_controller_unittest.cc125
-rw-r--r--cc/animation/transform_operations.cc84
-rw-r--r--cc/animation/transform_operations.h16
-rw-r--r--cc/animation/transform_operations_unittest.cc188
10 files changed, 556 insertions, 0 deletions
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h
index 4c15225..72aadef 100644
--- a/cc/animation/animation_curve.h
+++ b/cc/animation/animation_curve.h
@@ -78,6 +78,13 @@ class CC_EXPORT TransformAnimationCurve : public AnimationCurve {
// Returns true if this animation affects scale.
virtual bool AffectsScale() const = 0;
+ // Returns true if this animation is a translation.
+ virtual bool IsTranslation() const = 0;
+
+ // Set |max_scale| to the maximum scale along any dimension during this
+ // animation. Returns false if the maximum scale cannot be computed.
+ virtual bool MaximumScale(float* max_scale) const = 0;
+
// Partial Animation implementation.
virtual CurveType Type() const OVERRIDE;
};
diff --git a/cc/animation/keyframed_animation_curve.cc b/cc/animation/keyframed_animation_curve.cc
index 2fce022..4522ca2 100644
--- a/cc/animation/keyframed_animation_curve.cc
+++ b/cc/animation/keyframed_animation_curve.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
+
#include "cc/animation/keyframed_animation_curve.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/box_f.h"
@@ -341,6 +343,36 @@ bool KeyframedTransformAnimationCurve::AffectsScale() const {
return false;
}
+bool KeyframedTransformAnimationCurve::IsTranslation() const {
+ for (size_t i = 0; i < keyframes_.size(); ++i) {
+ if (!keyframes_[i]->Value().IsTranslation() &&
+ !keyframes_[i]->Value().IsIdentity())
+ return false;
+ }
+ return true;
+}
+
+bool KeyframedTransformAnimationCurve::MaximumScale(float* max_scale) const {
+ DCHECK_GE(keyframes_.size(), 2ul);
+ *max_scale = 0.f;
+ for (size_t i = 1; i < keyframes_.size(); ++i) {
+ float min_progress = 0.f;
+ float max_progress = 1.f;
+ if (keyframes_[i - 1]->timing_function())
+ keyframes_[i - 1]->timing_function()->Range(&min_progress, &max_progress);
+
+ float max_scale_for_segment = 0.f;
+ if (!keyframes_[i]->Value().MaximumScale(keyframes_[i - 1]->Value(),
+ min_progress,
+ max_progress,
+ &max_scale_for_segment))
+ return false;
+
+ *max_scale = std::max(*max_scale, max_scale_for_segment);
+ }
+ return true;
+}
+
scoped_ptr<KeyframedFilterAnimationCurve> KeyframedFilterAnimationCurve::
Create() {
return make_scoped_ptr(new KeyframedFilterAnimationCurve);
diff --git a/cc/animation/keyframed_animation_curve.h b/cc/animation/keyframed_animation_curve.h
index ceb900f..ded48c6 100644
--- a/cc/animation/keyframed_animation_curve.h
+++ b/cc/animation/keyframed_animation_curve.h
@@ -184,6 +184,8 @@ class CC_EXPORT KeyframedTransformAnimationCurve
virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
gfx::BoxF* bounds) const OVERRIDE;
virtual bool AffectsScale() const OVERRIDE;
+ virtual bool IsTranslation() const OVERRIDE;
+ virtual bool MaximumScale(float* max_scale) const OVERRIDE;
private:
KeyframedTransformAnimationCurve();
diff --git a/cc/animation/keyframed_animation_curve_unittest.cc b/cc/animation/keyframed_animation_curve_unittest.cc
index 1fb14966..eceba6f 100644
--- a/cc/animation/keyframed_animation_curve_unittest.cc
+++ b/cc/animation/keyframed_animation_curve_unittest.cc
@@ -482,5 +482,68 @@ TEST(KeyframedAnimationCurveTest, AffectsScale) {
EXPECT_TRUE(curve->AffectsScale());
}
+// Tests that animations that are translations are correctly identified.
+TEST(KeyframedAnimationCurveTest, IsTranslation) {
+ scoped_ptr<KeyframedTransformAnimationCurve> curve(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendTranslate(2.0, 3.0, -1.0);
+ TransformOperations operations2;
+ operations2.AppendTranslate(4.0, 1.0, 2.0);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, scoped_ptr<TimingFunction>()));
+
+ EXPECT_TRUE(curve->IsTranslation());
+
+ TransformOperations operations3;
+ operations3.AppendScale(2.f, 2.f, 2.f);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 2.0, operations3, scoped_ptr<TimingFunction>()));
+
+ EXPECT_FALSE(curve->IsTranslation());
+
+ TransformOperations operations4;
+ operations3.AppendTranslate(2.f, 2.f, 2.f);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 3.0, operations4, scoped_ptr<TimingFunction>()));
+
+ EXPECT_FALSE(curve->IsTranslation());
+}
+
+// Tests that maximum scale is computed as expected.
+TEST(KeyframedAnimationCurveTest, MaximumScale) {
+ scoped_ptr<KeyframedTransformAnimationCurve> curve(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendScale(2.f, -3.f, 1.f);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations1, EaseTimingFunction::Create()));
+
+ float maximum_scale = 0.f;
+ EXPECT_TRUE(curve->MaximumScale(&maximum_scale));
+ EXPECT_EQ(3.f, maximum_scale);
+
+ TransformOperations operations2;
+ operations2.AppendScale(6.f, 3.f, 2.f);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 2.0, operations2, EaseTimingFunction::Create()));
+
+ EXPECT_TRUE(curve->MaximumScale(&maximum_scale));
+ EXPECT_EQ(6.f, maximum_scale);
+
+ TransformOperations operations3;
+ operations3.AppendRotate(1.f, 0.f, 0.f, 90.f);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 3.0, operations3, EaseTimingFunction::Create()));
+
+ EXPECT_FALSE(curve->MaximumScale(&maximum_scale));
+}
+
} // namespace
} // namespace cc
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 12f5355..4c0b8bb 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -452,6 +452,39 @@ bool LayerAnimationController::HasAnimationThatAffectsScale() const {
return false;
}
+bool LayerAnimationController::HasOnlyTranslationTransforms() const {
+ for (size_t i = 0; i < active_animations_.size(); ++i) {
+ if (active_animations_[i]->is_finished() ||
+ active_animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ active_animations_[i]->curve()->ToTransformAnimationCurve();
+ if (!transform_animation_curve->IsTranslation())
+ return false;
+ }
+
+ return true;
+}
+
+bool LayerAnimationController::MaximumScale(float* max_scale) const {
+ *max_scale = 0.f;
+ for (size_t i = 0; i < active_animations_.size(); ++i) {
+ if (active_animations_[i]->is_finished() ||
+ active_animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ active_animations_[i]->curve()->ToTransformAnimationCurve();
+ float animation_scale = 0.f;
+ if (!transform_animation_curve->MaximumScale(&animation_scale))
+ return false;
+ *max_scale = std::max(*max_scale, animation_scale);
+ }
+
+ return true;
+}
+
void LayerAnimationController::PushNewAnimationsToImplThread(
LayerAnimationController* controller_impl) const {
// Any new animations owned by the main thread's controller are cloned and
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h
index 0aba413..c5f0a31 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -121,6 +121,12 @@ class CC_EXPORT LayerAnimationController
bool HasAnimationThatAffectsScale() const;
+ bool HasOnlyTranslationTransforms() const;
+
+ // Sets |max_scale| to the maximum scale along any dimension during active
+ // animations. Returns false if the maximum scale cannot be computed.
+ bool MaximumScale(float* max_scale) const;
+
protected:
friend class base::RefCounted<LayerAnimationController>;
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index 32e5d49..af795b2 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -1732,5 +1732,130 @@ TEST(LayerAnimationControllerTest, HasAnimationThatAffectsScale) {
EXPECT_FALSE(controller_impl->HasAnimationThatAffectsScale());
}
+TEST(LayerAnimationControllerTest, HasOnlyTranslationTransforms) {
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+
+ EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
+
+ controller_impl->AddAnimation(CreateAnimation(
+ scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
+ 1,
+ Animation::Opacity));
+
+ // Opacity animations aren't non-translation transforms.
+ EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve1(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendTranslate(10.0, 15.0, 0.0);
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations1, scoped_ptr<TimingFunction>()));
+
+ scoped_ptr<Animation> animation(Animation::Create(
+ curve1.PassAs<AnimationCurve>(), 2, 2, Animation::Transform));
+ controller_impl->AddAnimation(animation.Pass());
+
+ // The only transform animation we've added is a translation.
+ EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve2(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations2;
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations2, scoped_ptr<TimingFunction>()));
+ operations2.AppendScale(2.0, 3.0, 4.0);
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, scoped_ptr<TimingFunction>()));
+
+ animation = Animation::Create(
+ curve2.PassAs<AnimationCurve>(), 3, 3, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+
+ // A scale animation is not a translation.
+ EXPECT_FALSE(controller_impl->HasOnlyTranslationTransforms());
+
+ controller_impl->GetAnimation(3, Animation::Transform)
+ ->SetRunState(Animation::Finished, 0.0);
+
+ // Only unfinished animations should be considered by
+ // HasOnlyTranslationTransforms.
+ EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
+}
+
+TEST(LayerAnimationControllerTest, MaximumScale) {
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+
+ float max_scale = 0.f;
+ EXPECT_TRUE(controller_impl->MaximumScale(&max_scale));
+ EXPECT_EQ(0.f, max_scale);
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve1(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendScale(2.0, 3.0, 4.0);
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations1, scoped_ptr<TimingFunction>()));
+
+ scoped_ptr<Animation> animation(Animation::Create(
+ curve1.PassAs<AnimationCurve>(), 1, 1, Animation::Transform));
+ controller_impl->AddAnimation(animation.Pass());
+
+ EXPECT_TRUE(controller_impl->MaximumScale(&max_scale));
+ EXPECT_EQ(4.f, max_scale);
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve2(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations2;
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations2, scoped_ptr<TimingFunction>()));
+ operations2.AppendScale(6.0, 5.0, 4.0);
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, scoped_ptr<TimingFunction>()));
+
+ animation = Animation::Create(
+ curve2.PassAs<AnimationCurve>(), 2, 2, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+
+ EXPECT_TRUE(controller_impl->MaximumScale(&max_scale));
+ EXPECT_EQ(6.f, max_scale);
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve3(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations3;
+ curve3->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations3, scoped_ptr<TimingFunction>()));
+ operations3.AppendPerspective(6.0);
+ curve3->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations3, scoped_ptr<TimingFunction>()));
+
+ animation = Animation::Create(
+ curve3.PassAs<AnimationCurve>(), 3, 3, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+
+ EXPECT_FALSE(controller_impl->MaximumScale(&max_scale));
+
+ controller_impl->GetAnimation(3, Animation::Transform)
+ ->SetRunState(Animation::Finished, 0.0);
+ controller_impl->GetAnimation(2, Animation::Transform)
+ ->SetRunState(Animation::Finished, 0.0);
+
+ // Only unfinished animations should be considered by
+ // MaximumScale.
+ EXPECT_TRUE(controller_impl->MaximumScale(&max_scale));
+ EXPECT_EQ(4.f, max_scale);
+}
+
} // namespace
} // namespace cc
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index 5b20436..2d34290 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -6,6 +6,7 @@
#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"
@@ -89,6 +90,89 @@ bool TransformOperations::AffectsScale() const {
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;
diff --git a/cc/animation/transform_operations.h b/cc/animation/transform_operations.h
index 7d42cea..f086eb1 100644
--- a/cc/animation/transform_operations.h
+++ b/cc/animation/transform_operations.h
@@ -60,6 +60,17 @@ class CC_EXPORT TransformOperations {
// Returns true if these operations affect scale.
bool AffectsScale() const;
+ // Returns true if these operations are only translations.
+ bool IsTranslation() const;
+
+ // Sets |max_scale| to be the maximum scale in any dimension when calling
+ // Blend on |from| with progress in the range [min_progress, max_progress]. If
+ // this maximum scale cannot be computed, returns false.
+ bool MaximumScale(const TransformOperations& from,
+ SkMScalar min_progress,
+ SkMScalar max_progress,
+ float* max_scale) const;
+
// Returns true if this operation and its descendants have the same types
// as other and its descendants.
bool MatchesTypes(const TransformOperations& other) const;
@@ -87,6 +98,11 @@ class CC_EXPORT TransformOperations {
bool ComputeDecomposedTransform() const;
+ // If these operations have no more than one scale operation, and if the only
+ // other operations are translations, sets |scale| to the scale component
+ // of these operations. Otherwise, returns false.
+ bool ScaleComponent(gfx::Vector3dF* scale) const;
+
// For efficiency, we cache the decomposed transform.
mutable scoped_ptr<gfx::DecomposedTransform> decomposed_transform_;
mutable bool decomposed_transform_dirty_;
diff --git a/cc/animation/transform_operations_unittest.cc b/cc/animation/transform_operations_unittest.cc
index 8dad032..ce7d121 100644
--- a/cc/animation/transform_operations_unittest.cc
+++ b/cc/animation/transform_operations_unittest.cc
@@ -1317,5 +1317,193 @@ TEST(TransformOperationTest, AffectsScaleWithMultipleOperations) {
EXPECT_TRUE(operations2.AffectsScale());
}
+TEST(TransformOperationTest, IsTranslationWithSingleOperation) {
+ TransformOperations empty_operations;
+ EXPECT_TRUE(empty_operations.IsTranslation());
+
+ TransformOperations identity;
+ identity.AppendIdentity();
+ EXPECT_TRUE(identity.IsTranslation());
+
+ TransformOperations translate;
+ translate.AppendTranslate(1.f, 2.f, 3.f);
+ EXPECT_TRUE(translate.IsTranslation());
+
+ TransformOperations rotate;
+ rotate.AppendRotate(1.f, 2.f, 3.f, 4.f);
+ EXPECT_FALSE(rotate.IsTranslation());
+
+ TransformOperations scale;
+ scale.AppendScale(1.f, 2.f, 3.f);
+ EXPECT_FALSE(scale.IsTranslation());
+
+ TransformOperations skew;
+ skew.AppendSkew(1.f, 2.f);
+ EXPECT_FALSE(skew.IsTranslation());
+
+ TransformOperations perspective;
+ perspective.AppendPerspective(1.f);
+ EXPECT_FALSE(perspective.IsTranslation());
+
+ TransformOperations identity_matrix;
+ identity_matrix.AppendMatrix(gfx::Transform());
+ EXPECT_TRUE(identity_matrix.IsTranslation());
+
+ TransformOperations translation_matrix;
+ gfx::Transform translation_transform;
+ translation_transform.Translate3d(1.f, 2.f, 3.f);
+ translation_matrix.AppendMatrix(translation_transform);
+ EXPECT_TRUE(translation_matrix.IsTranslation());
+
+ TransformOperations scaling_matrix;
+ gfx::Transform scaling_transform;
+ scaling_transform.Scale(2.f, 2.f);
+ scaling_matrix.AppendMatrix(scaling_transform);
+ EXPECT_FALSE(scaling_matrix.IsTranslation());
+}
+
+TEST(TransformOperationTest, IsTranslationWithMultipleOperations) {
+ TransformOperations operations1;
+ operations1.AppendSkew(1.f, 2.f);
+ operations1.AppendTranslate(1.f, 2.f, 3.f);
+ operations1.AppendIdentity();
+ EXPECT_FALSE(operations1.IsTranslation());
+
+ TransformOperations operations2;
+ operations2.AppendIdentity();
+ operations2.AppendTranslate(3.f, 2.f, 1.f);
+ gfx::Transform translation_transform;
+ translation_transform.Translate3d(1.f, 2.f, 3.f);
+ operations2.AppendMatrix(translation_transform);
+ EXPECT_TRUE(operations2.IsTranslation());
+}
+
+TEST(TransformOperationTest, MaximumScale) {
+ TransformOperations operations1;
+ operations1.AppendScale(3.f, 2.f, 5.f);
+ TransformOperations operations2;
+ operations2.AppendScale(6.f, 5.f, 2.f);
+
+ float max_scale = 0.f;
+ EXPECT_TRUE(operations2.MaximumScale(operations1, 0.f, 1.f, &max_scale));
+ // x at progress 1.f
+ EXPECT_EQ(6.f, max_scale);
+
+ EXPECT_TRUE(operations2.MaximumScale(operations1, -1.f, 1.f, &max_scale));
+ // z at progress -1.f
+ EXPECT_EQ(8.f, max_scale);
+
+ EXPECT_TRUE(operations2.MaximumScale(operations1, 0.f, 2.f, &max_scale));
+ // x at progress 2.f
+ EXPECT_EQ(9.f, max_scale);
+
+ TransformOperations operations3;
+ operations3.AppendScale(1.f, 4.f, 1.f);
+ TransformOperations operations4;
+
+ EXPECT_TRUE(operations4.MaximumScale(operations3, 0.f, 1.f, &max_scale));
+ // y at progress 0.f
+ EXPECT_EQ(4.f, max_scale);
+
+ EXPECT_TRUE(operations4.MaximumScale(operations3, -1.f, 1.f, &max_scale));
+ // y at progress -1.f
+ EXPECT_EQ(7.f, max_scale);
+
+ EXPECT_TRUE(operations4.MaximumScale(operations3, 0.f, 2.f, &max_scale));
+ // y at progress 0.f
+ EXPECT_EQ(4.f, max_scale);
+
+ TransformOperations operations5;
+ operations5.AppendTranslate(1.f, 2.f, 3.f);
+ operations5.AppendScale(1.f, 1.f, 4.f);
+ gfx::Transform translation_transform;
+ translation_transform.Translate3d(1.f, 2.f, 3.f);
+ operations5.AppendMatrix(translation_transform);
+ TransformOperations operations6;
+ operations6.AppendTranslate(2.f, 3.f, 4.f);
+ operations6.AppendScale(2.f, 5.f, 1.f);
+ operations6.AppendMatrix(translation_transform);
+
+ EXPECT_TRUE(operations6.MaximumScale(operations5, 0.f, 1.f, &max_scale));
+ // y at progress 1.f
+ EXPECT_EQ(5.f, max_scale);
+
+ EXPECT_TRUE(operations6.MaximumScale(operations5, -1.f, 1.f, &max_scale));
+ // z at progress -1.f
+ EXPECT_EQ(7.f, max_scale);
+
+ EXPECT_TRUE(operations6.MaximumScale(operations5, 0.f, 2.f, &max_scale));
+ // y at progress 2.f
+ EXPECT_EQ(9.f, max_scale);
+}
+
+TEST(TransformOperationTest, MaximumScaleCannotBeComputed) {
+ TransformOperations operations1;
+ operations1.AppendScale(2.f, 2.f, 2.f);
+ TransformOperations operations2;
+ operations2.AppendTranslate(1.f, 2.f, 3.f);
+
+ float max_scale = 0.f;
+
+ // Non-matching operations.
+ EXPECT_FALSE(operations2.MaximumScale(operations1, 0.f, 1.f, &max_scale));
+
+ TransformOperations operations3;
+ operations3.AppendScale(2.f, 2.f, 2.f);
+ operations3.AppendTranslate(1.f, 2.f, 3.f);
+ operations3.AppendScale(3.f, 3.f, 3.f);
+ TransformOperations operations4;
+ operations4.AppendScale(4.f, 4.f, 4.f);
+ operations4.AppendTranslate(2.f, 3.f, 4.f);
+ operations4.AppendScale(5.f, 5.f, 5.f);
+
+ // More that one scale operation in a sequence.
+ EXPECT_FALSE(operations4.MaximumScale(operations3, 0.f, 1.f, &max_scale));
+
+ TransformOperations operations5;
+ operations5.AppendScale(2.f, 2.f, 2.f);
+ gfx::Transform scaling_transform;
+ scaling_transform.Scale(2.f, 2.f);
+ operations5.AppendMatrix(scaling_transform);
+ TransformOperations operations6;
+ operations6.AppendScale(3.f, 3.f, 3.f);
+ gfx::Transform translation_transform;
+ translation_transform.Translate3d(1.f, 2.f, 3.f);
+ operations6.AppendMatrix(translation_transform);
+
+ // Non-translation matrix operation.
+ EXPECT_FALSE(operations6.MaximumScale(operations5, 0.f, 1.f, &max_scale));
+
+ TransformOperations operations7;
+ operations7.AppendScale(2.f, 2.f, 2.f);
+ operations7.AppendRotate(1.f, 2.f, 3.f, 4.f);
+ TransformOperations operations8;
+ operations8.AppendScale(3.f, 3.f, 3.f);
+ operations8.AppendRotate(3.f, 4.f, 5.f, 6.f);
+
+ // Rotation operation.
+ EXPECT_FALSE(operations8.MaximumScale(operations7, 0.f, 1.f, &max_scale));
+
+ TransformOperations operations9;
+ operations9.AppendScale(2.f, 2.f, 2.f);
+ operations9.AppendSkew(1.f, 2.f);
+ TransformOperations operations10;
+ operations10.AppendScale(3.f, 3.f, 3.f);
+ operations10.AppendSkew(3.f, 4.f);
+
+ // Skew operation.
+ EXPECT_FALSE(operations10.MaximumScale(operations9, 0.f, 1.f, &max_scale));
+
+ TransformOperations operations11;
+ operations11.AppendScale(2.f, 2.f, 2.f);
+ operations11.AppendPerspective(1.f);
+ TransformOperations operations12;
+ operations12.AppendScale(3.f, 3.f, 3.f);
+ operations12.AppendPerspective(3.f);
+
+ // Perspective operation.
+ EXPECT_FALSE(operations12.MaximumScale(operations11, 0.f, 1.f, &max_scale));
+}
+
} // namespace
} // namespace cc