diff options
author | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-18 09:34:48 +0000 |
---|---|---|
committer | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-18 09:34:48 +0000 |
commit | cdb284d7a99f11f80fa07ac0390eacf65322d1ef (patch) | |
tree | 9268fd0d17b0d41dbfd3548e6286434393e045e3 /cc/animation | |
parent | d59adfc29b142ce07e26822203f432658473504a (diff) | |
download | chromium_src-cdb284d7a99f11f80fa07ac0390eacf65322d1ef.zip chromium_src-cdb284d7a99f11f80fa07ac0390eacf65322d1ef.tar.gz chromium_src-cdb284d7a99f11f80fa07ac0390eacf65322d1ef.tar.bz2 |
Part 11 of cc/ directory shuffles: fix up stragglers
Continuation of https://src.chromium.org/viewvc/chrome?view=rev&revision=188681
This moves files left behind in previous patches and completes the series.
BUG=190824
TBR=enne@chromium.org
Review URL: https://codereview.chromium.org/12916002
Review URL: https://codereview.chromium.org/12604010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188707 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/animation')
-rw-r--r-- | cc/animation/keyframed_animation_curve.h | 4 | ||||
-rw-r--r-- | cc/animation/keyframed_animation_curve_unittest.cc | 2 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller_unittest.cc | 2 | ||||
-rw-r--r-- | cc/animation/timing_function.cc | 139 | ||||
-rw-r--r-- | cc/animation/timing_function.h | 67 | ||||
-rw-r--r-- | cc/animation/timing_function_unittest.cc | 41 | ||||
-rw-r--r-- | cc/animation/transform_operation.cc | 184 | ||||
-rw-r--r-- | cc/animation/transform_operation.h | 63 | ||||
-rw-r--r-- | cc/animation/transform_operations.cc | 200 | ||||
-rw-r--r-- | cc/animation/transform_operations.h | 81 | ||||
-rw-r--r-- | cc/animation/transform_operations_unittest.cc | 603 |
11 files changed, 1382 insertions, 4 deletions
diff --git a/cc/animation/keyframed_animation_curve.h b/cc/animation/keyframed_animation_curve.h index aa53b67..09f269c 100644 --- a/cc/animation/keyframed_animation_curve.h +++ b/cc/animation/keyframed_animation_curve.h @@ -6,10 +6,10 @@ #define CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ #include "cc/animation/animation_curve.h" +#include "cc/animation/timing_function.h" +#include "cc/animation/transform_operations.h" #include "cc/base/cc_export.h" #include "cc/base/scoped_ptr_vector.h" -#include "cc/timing_function.h" -#include "cc/transform_operations.h" namespace cc { diff --git a/cc/animation/keyframed_animation_curve_unittest.cc b/cc/animation/keyframed_animation_curve_unittest.cc index 446801d..dd7f869 100644 --- a/cc/animation/keyframed_animation_curve_unittest.cc +++ b/cc/animation/keyframed_animation_curve_unittest.cc @@ -4,7 +4,7 @@ #include "cc/animation/keyframed_animation_curve.h" -#include "cc/transform_operations.h" +#include "cc/animation/transform_operations.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc index 2e7c29b..d7ecf9f 100644 --- a/cc/animation/layer_animation_controller_unittest.cc +++ b/cc/animation/layer_animation_controller_unittest.cc @@ -7,8 +7,8 @@ #include "cc/animation/animation.h" #include "cc/animation/animation_curve.h" #include "cc/animation/keyframed_animation_curve.h" +#include "cc/animation/transform_operations.h" #include "cc/test/animation_test_common.h" -#include "cc/transform_operations.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/transform.h" diff --git a/cc/animation/timing_function.cc b/cc/animation/timing_function.cc new file mode 100644 index 0000000..c0af3c2 --- /dev/null +++ b/cc/animation/timing_function.cc @@ -0,0 +1,139 @@ +// Copyright 2012 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/timing_function.h" + +#include "third_party/skia/include/core/SkMath.h" + +// TODO(danakj) These methods come from SkInterpolator.cpp. When such a method +// is available in the public Skia API, we should switch to using that. +// http://crbug.com/159735 +namespace { + +// Dot14 has 14 bits for decimal places, and the remainder for whole numbers. +typedef int Dot14; +#define DOT14_ONE (1 << 14) +#define DOT14_HALF (1 << 13) + +#define Dot14ToFloat(x) ((x) / 16384.f) + +static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) { + return (a * b + DOT14_HALF) >> 14; +} + +static inline Dot14 EvalCubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) { + return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t); +} + +static inline Dot14 PinAndConvert(SkScalar x) { + if (x <= 0) + return 0; + if (x >= SK_Scalar1) + return DOT14_ONE; + return SkScalarToFixed(x) >> 2; +} + +SkScalar SkUnitCubicInterp(SkScalar bx, SkScalar by, + SkScalar cx, SkScalar cy, + SkScalar value) { + Dot14 x = PinAndConvert(value); + + if (x == 0) return 0; + if (x == DOT14_ONE) return SK_Scalar1; + + Dot14 b = PinAndConvert(bx); + Dot14 c = PinAndConvert(cx); + + // Now compute our coefficients from the control points. + // t -> 3b + // t^2 -> 3c - 6b + // t^3 -> 3b - 3c + 1 + Dot14 A = 3 * b; + Dot14 B = 3 * (c - 2 * b); + Dot14 C = 3 * (b - c) + DOT14_ONE; + + // Now search for a t value given x. + Dot14 t = DOT14_HALF; + Dot14 dt = DOT14_HALF; + for (int i = 0; i < 13; i++) { + dt >>= 1; + Dot14 guess = EvalCubic(t, A, B, C); + if (x < guess) + t -= dt; + else + t += dt; + } + + // Now we have t, so compute the coefficient for Y and evaluate. + b = PinAndConvert(by); + c = PinAndConvert(cy); + A = 3 * b; + B = 3 * (c - 2 * b); + C = 3 * (b - c) + DOT14_ONE; + return SkFixedToScalar(EvalCubic(t, A, B, C) << 2); +} + +} // namespace + +namespace cc { + +TimingFunction::TimingFunction() { +} + +TimingFunction::~TimingFunction() { +} + +double TimingFunction::Duration() const { + return 1.0; +} + +scoped_ptr<CubicBezierTimingFunction> CubicBezierTimingFunction::create( + double x1, double y1, double x2, double y2) { + return make_scoped_ptr(new CubicBezierTimingFunction(x1, y1, x2, y2)); +} + +CubicBezierTimingFunction::CubicBezierTimingFunction(double x1, double y1, + double x2, double y2) + : x1_(SkDoubleToScalar(x1)), + y1_(SkDoubleToScalar(y1)), + x2_(SkDoubleToScalar(x2)), + y2_(SkDoubleToScalar(y2)) { +} + +CubicBezierTimingFunction::~CubicBezierTimingFunction() { +} + +float CubicBezierTimingFunction::GetValue(double x) const { + SkScalar value = SkUnitCubicInterp(x1_, y1_, x2_, y2_, x); + return SkScalarToFloat(value); +} + +scoped_ptr<AnimationCurve> CubicBezierTimingFunction::Clone() const { + return make_scoped_ptr( + new CubicBezierTimingFunction(*this)).PassAs<AnimationCurve>(); +} + +// These numbers come from +// http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag. +scoped_ptr<TimingFunction> EaseTimingFunction::create() { + return CubicBezierTimingFunction::create( + 0.25, 0.1, 0.25, 1).PassAs<TimingFunction>(); +} + +scoped_ptr<TimingFunction> EaseInTimingFunction::create() { + return CubicBezierTimingFunction::create( + 0.42, 0, 1.0, 1).PassAs<TimingFunction>(); +} + +scoped_ptr<TimingFunction> EaseOutTimingFunction::create() { + return CubicBezierTimingFunction::create( + 0, 0, 0.58, 1).PassAs<TimingFunction>(); +} + +scoped_ptr<TimingFunction> EaseInOutTimingFunction::create() { + return CubicBezierTimingFunction::create( + 0.42, 0, 0.58, 1).PassAs<TimingFunction>(); +} + +} // namespace cc diff --git a/cc/animation/timing_function.h b/cc/animation/timing_function.h new file mode 100644 index 0000000..3c88ecb --- /dev/null +++ b/cc/animation/timing_function.h @@ -0,0 +1,67 @@ +// Copyright 2012 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. + +#ifndef CC_ANIMATION_TIMING_FUNCTION_H_ +#define CC_ANIMATION_TIMING_FUNCTION_H_ + +#include "cc/animation/animation_curve.h" +#include "cc/base/cc_export.h" +#include "third_party/skia/include/core/SkScalar.h" + +namespace cc { + +// See http://www.w3.org/TR/css3-transitions/. +class CC_EXPORT TimingFunction : public FloatAnimationCurve { + public: + virtual ~TimingFunction(); + + // Partial implementation of FloatAnimationCurve. + virtual double Duration() const OVERRIDE; + + protected: + TimingFunction(); +}; + +class CC_EXPORT CubicBezierTimingFunction : public TimingFunction { + public: + static scoped_ptr<CubicBezierTimingFunction> create(double x1, double y1, + double x2, double y2); + virtual ~CubicBezierTimingFunction(); + + // Partial implementation of FloatAnimationCurve. + virtual float GetValue(double time) const OVERRIDE; + virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE; + + protected: + CubicBezierTimingFunction(double x1, double y1, double x2, double y2); + + SkScalar x1_; + SkScalar y1_; + SkScalar x2_; + SkScalar y2_; +}; + +class CC_EXPORT EaseTimingFunction { + public: + static scoped_ptr<TimingFunction> create(); +}; + +class CC_EXPORT EaseInTimingFunction { + public: + static scoped_ptr<TimingFunction> create(); +}; + +class CC_EXPORT EaseOutTimingFunction { + public: + static scoped_ptr<TimingFunction> create(); +}; + +class CC_EXPORT EaseInOutTimingFunction { + public: + static scoped_ptr<TimingFunction> create(); +}; + +} // namespace cc + +#endif // CC_ANIMATION_TIMING_FUNCTION_H_ diff --git a/cc/animation/timing_function_unittest.cc b/cc/animation/timing_function_unittest.cc new file mode 100644 index 0000000..f97201b --- /dev/null +++ b/cc/animation/timing_function_unittest.cc @@ -0,0 +1,41 @@ +// Copyright 2011 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/timing_function.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +TEST(TimingFunctionTest, CubicBezierTimingFunction) { + scoped_ptr<CubicBezierTimingFunction> function = + CubicBezierTimingFunction::create(0.25, 0, 0.75, 1); + + double epsilon = 0.00015; + + EXPECT_NEAR(function->GetValue(0), 0, epsilon); + EXPECT_NEAR(function->GetValue(0.05), 0.01136, epsilon); + EXPECT_NEAR(function->GetValue(0.1), 0.03978, epsilon); + EXPECT_NEAR(function->GetValue(0.15), 0.079780, epsilon); + EXPECT_NEAR(function->GetValue(0.2), 0.12803, epsilon); + EXPECT_NEAR(function->GetValue(0.25), 0.18235, epsilon); + EXPECT_NEAR(function->GetValue(0.3), 0.24115, epsilon); + EXPECT_NEAR(function->GetValue(0.35), 0.30323, epsilon); + EXPECT_NEAR(function->GetValue(0.4), 0.36761, epsilon); + EXPECT_NEAR(function->GetValue(0.45), 0.43345, epsilon); + EXPECT_NEAR(function->GetValue(0.5), 0.5, epsilon); + EXPECT_NEAR(function->GetValue(0.6), 0.63238, epsilon); + EXPECT_NEAR(function->GetValue(0.65), 0.69676, epsilon); + EXPECT_NEAR(function->GetValue(0.7), 0.75884, epsilon); + EXPECT_NEAR(function->GetValue(0.75), 0.81764, epsilon); + EXPECT_NEAR(function->GetValue(0.8), 0.87196, epsilon); + EXPECT_NEAR(function->GetValue(0.85), 0.92021, epsilon); + EXPECT_NEAR(function->GetValue(0.9), 0.96021, epsilon); + EXPECT_NEAR(function->GetValue(0.95), 0.98863, epsilon); + EXPECT_NEAR(function->GetValue(1), 1, epsilon); +} + +} // namespace +} // namespace cc diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc new file mode 100644 index 0000000..65769b4 --- /dev/null +++ b/cc/animation/transform_operation.cc @@ -0,0 +1,184 @@ +// 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 <cmath> +#include <limits> + +#include "cc/animation/transform_operation.h" +#include "ui/gfx/vector3d_f.h" + +namespace { +const double kAngleEpsilon = 1e-4; +} + +namespace cc { + +bool TransformOperation::IsIdentity() const { + return matrix.IsIdentity(); +} + +static bool IsOperationIdentity(const TransformOperation* operation) { + return !operation || operation->IsIdentity(); +} + +static bool ShareSameAxis(const TransformOperation* from, + const TransformOperation* to, + double& axis_x, double& axis_y, double& axis_z, + double& angle_from) { + if (IsOperationIdentity(from) && IsOperationIdentity(to)) + return false; + + if (IsOperationIdentity(from) && !IsOperationIdentity(to)) { + axis_x = to->rotate.axis.x; + axis_y = to->rotate.axis.y; + axis_z = to->rotate.axis.z; + angle_from = 0; + return true; + } + + if (!IsOperationIdentity(from) && IsOperationIdentity(to)) { + axis_x = from->rotate.axis.x; + axis_y = from->rotate.axis.y; + axis_z = from->rotate.axis.z; + angle_from = from->rotate.angle; + return true; + } + + double length_2 = from->rotate.axis.x * from->rotate.axis.x + + from->rotate.axis.y * from->rotate.axis.y + + from->rotate.axis.z * from->rotate.axis.z; + double other_length_2 = to->rotate.axis.x * to->rotate.axis.x + + to->rotate.axis.y * to->rotate.axis.y + + to->rotate.axis.z * to->rotate.axis.z; + + if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon) + return false; + + double dot = to->rotate.axis.x * from->rotate.axis.x + + to->rotate.axis.y * from->rotate.axis.y + + to->rotate.axis.z * from->rotate.axis.z; + double error = std::fabs(1.0 - (dot * dot) / (length_2 * other_length_2)); + bool result = error < kAngleEpsilon; + if (result) { + axis_x = to->rotate.axis.x; + axis_y = to->rotate.axis.y; + axis_z = to->rotate.axis.z; + // If the axes are pointing in opposite directions, we need to reverse + // the angle. + angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle; + } + return result; +} + +static double BlendDoubles(double from, double to, double progress) { + if (progress <= 0.0) + return from; + + if (progress >= 1.0) + return to; + + return from * (1 - progress) + to * progress; +} + +bool TransformOperation::BlendTransformOperations( + const TransformOperation* from, + const TransformOperation* to, + double progress, + gfx::Transform& result) { + if (IsOperationIdentity(from) && IsOperationIdentity(to)) + return true; + + TransformOperation::Type interpolation_type = + TransformOperation::TransformOperationIdentity; + if (IsOperationIdentity(to)) + interpolation_type = from->type; + else + interpolation_type = to->type; + + switch (interpolation_type) { + case TransformOperation::TransformOperationTranslate: { + double from_x = IsOperationIdentity(from) ? 0 : from->translate.x; + double from_y = IsOperationIdentity(from) ? 0 : from->translate.y; + double from_z = IsOperationIdentity(from) ? 0 : from->translate.z; + double to_x = IsOperationIdentity(to) ? 0 : to->translate.x; + double to_y = IsOperationIdentity(to) ? 0 : to->translate.y; + double to_z = IsOperationIdentity(to) ? 0 : to->translate.z; + result.Translate3d(BlendDoubles(from_x, to_x, progress), + BlendDoubles(from_y, to_y, progress), + BlendDoubles(from_z, to_z, progress)); + break; + } + case TransformOperation::TransformOperationRotate: { + double axis_x = 0; + double axis_y = 0; + double axis_z = 1; + double from_angle = 0; + double to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle; + if (ShareSameAxis(from, to, axis_x, axis_y, axis_z, from_angle)) + result.RotateAbout(gfx::Vector3dF(axis_x, axis_y, axis_z), + BlendDoubles(from_angle, to_angle, progress)); + else { + gfx::Transform to_matrix; + if (!IsOperationIdentity(to)) + to_matrix = to->matrix; + gfx::Transform from_matrix; + if (!IsOperationIdentity(from)) + from_matrix = from->matrix; + result = to_matrix; + if (!result.Blend(from_matrix, progress)) + return false; + } + break; + } + case TransformOperation::TransformOperationScale: { + double from_x = IsOperationIdentity(from) ? 1 : from->scale.x; + double from_y = IsOperationIdentity(from) ? 1 : from->scale.y; + double from_z = IsOperationIdentity(from) ? 1 : from->scale.z; + double to_x = IsOperationIdentity(to) ? 1 : to->scale.x; + double to_y = IsOperationIdentity(to) ? 1 : to->scale.y; + double to_z = IsOperationIdentity(to) ? 1 : to->scale.z; + result.Scale3d(BlendDoubles(from_x, to_x, progress), + BlendDoubles(from_y, to_y, progress), + BlendDoubles(from_z, to_z, progress)); + break; + } + case TransformOperation::TransformOperationSkew: { + double from_x = IsOperationIdentity(from) ? 0 : from->skew.x; + double from_y = IsOperationIdentity(from) ? 0 : from->skew.y; + double to_x = IsOperationIdentity(to) ? 0 : to->skew.x; + double to_y = IsOperationIdentity(to) ? 0 : to->skew.y; + result.SkewX(BlendDoubles(from_x, to_x, progress)); + result.SkewY(BlendDoubles(from_y, to_y, progress)); + break; + } + case TransformOperation::TransformOperationPerspective: { + double from_perspective_depth = IsOperationIdentity(from) ? + std::numeric_limits<double>::max() : from->perspective_depth; + double to_perspective_depth = IsOperationIdentity(to) ? + std::numeric_limits<double>::max() : to->perspective_depth; + result.ApplyPerspectiveDepth( + BlendDoubles(from_perspective_depth, to_perspective_depth, progress)); + break; + } + case TransformOperation::TransformOperationMatrix: { + gfx::Transform to_matrix; + if (!IsOperationIdentity(to)) + to_matrix = to->matrix; + gfx::Transform from_matrix; + if (!IsOperationIdentity(from)) + from_matrix = from->matrix; + result = to_matrix; + if (!result.Blend(from_matrix, progress)) + return false; + break; + } + case TransformOperation::TransformOperationIdentity: + // Do nothing. + break; + } + + return true; +} + +} // namespace cc diff --git a/cc/animation/transform_operation.h b/cc/animation/transform_operation.h new file mode 100644 index 0000000..0581862 --- /dev/null +++ b/cc/animation/transform_operation.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef CC_ANIMATION_TRANSFORM_OPERATION_H_ +#define CC_ANIMATION_TRANSFORM_OPERATION_H_ + +#include "ui/gfx/transform.h" + +namespace cc { + +struct TransformOperation { + enum Type { + TransformOperationTranslate, + TransformOperationRotate, + TransformOperationScale, + TransformOperationSkew, + TransformOperationPerspective, + TransformOperationMatrix, + TransformOperationIdentity + }; + + TransformOperation() + : type(TransformOperationIdentity) { + } + + Type type; + gfx::Transform matrix; + + union { + double perspective_depth; + + struct { + double x, y; + } skew; + + struct { + double x, y, z; + } scale; + + struct { + double x, y, z; + } translate; + + struct { + struct { + double x, y, z; + } axis; + + double angle; + } rotate; + }; + + bool IsIdentity() const; + static bool BlendTransformOperations(const TransformOperation* from, + const TransformOperation* to, + double progress, + gfx::Transform& result); +}; + +} // namespace cc + +#endif // CC_ANIMATION_TRANSFORM_OPERATION_H_ diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc new file mode 100644 index 0000000..4de283e --- /dev/null +++ b/cc/animation/transform_operations.cc @@ -0,0 +1,200 @@ +// 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 "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, double progress) const { + gfx::Transform to_return; + BlendInternal(from, progress, &to_return); + return to_return; +} + +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(double x, double y, double 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(double x, double y, double z, + double 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(double x, double y, double 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(double x, double 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(double 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, + double 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 (progress <= 0.0) { + *result = from.Apply(); + return true; + } + + if (progress >= 1.0) { + *result = Apply(); + 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 diff --git a/cc/animation/transform_operations.h b/cc/animation/transform_operations.h new file mode 100644 index 0000000..9ab0196 --- /dev/null +++ b/cc/animation/transform_operations.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef CC_ANIMATION_TRANSFORM_OPERATIONS_H_ +#define CC_ANIMATION_TRANSFORM_OPERATIONS_H_ + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "cc/animation/transform_operation.h" +#include "cc/base/cc_export.h" +#include "ui/gfx/transform.h" + +namespace gfx { +struct DecomposedTransform; +} + +namespace cc { + +// Transform operations are a decomposed transformation matrix. It can be +// applied to obtain a gfx::Transform at any time, and can be blended +// intelligently with other transform operations, so long as they represent the +// same decomposition. For example, if we have a transform that is made up of +// a rotation followed by skew, it can be blended intelligently with another +// transform made up of a rotation followed by a skew. Blending is possible if +// we have two dissimilar sets of transform operations, but the effect may not +// be what was intended. For more information, see the comments for the blend +// function below. +class CC_EXPORT TransformOperations { + public: + TransformOperations(); + TransformOperations(const TransformOperations& other); + ~TransformOperations(); + + // Returns a transformation matrix representing these transform operations. + gfx::Transform Apply() const; + + // Given another set of transform operations and a progress in the range + // [0, 1], returns a transformation matrix representing the intermediate + // value. If this->MatchesTypes(from), then each of the operations are + // blended separately and then combined. Otherwise, the two sets of + // transforms are baked to matrices (using apply), and the matrices are + // then decomposed and interpolated. For more information, see + // http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition. + gfx::Transform Blend(const TransformOperations& from, double progress) const; + + // Returns true if this operation and its descendants have the same types + // as other and its descendants. + bool MatchesTypes(const TransformOperations& other) const; + + // Returns true if these operations can be blended. It will only return + // false if we must resort to matrix interpolation, and matrix interpolation + // fails (this can happen if either matrix cannot be decomposed). + bool CanBlendWith(const TransformOperations& other) const; + + void AppendTranslate(double x, double y, double z); + void AppendRotate(double x, double y, double z, double degrees); + void AppendScale(double x, double y, double z); + void AppendSkew(double x, double y); + void AppendPerspective(double depth); + void AppendMatrix(const gfx::Transform& matrix); + void AppendIdentity(); + bool IsIdentity() const; + + private: + bool BlendInternal(const TransformOperations& from, double progress, + gfx::Transform* result) const; + + std::vector<TransformOperation> operations_; + + bool ComputeDecomposedTransform() const; + + // For efficiency, we cache the decomposed transform. + mutable scoped_ptr<gfx::DecomposedTransform> decomposed_transform_; + mutable bool decomposed_transform_dirty_; +}; + +} // namespace cc + +#endif // CC_ANIMATION_TRANSFORM_OPERATIONS_H_ diff --git a/cc/animation/transform_operations_unittest.cc b/cc/animation/transform_operations_unittest.cc new file mode 100644 index 0000000..206a9f6 --- /dev/null +++ b/cc/animation/transform_operations_unittest.cc @@ -0,0 +1,603 @@ +// 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 "base/memory/scoped_vector.h" +#include "cc/animation/transform_operations.h" +#include "cc/test/geometry_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/vector3d_f.h" + +namespace cc { +namespace { + +TEST(TransformOperationTest, TransformTypesAreUnique) { + ScopedVector<TransformOperations> transforms; + + TransformOperations* to_add = new TransformOperations(); + to_add->AppendTranslate(1, 0, 0); + transforms.push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendRotate(0, 0, 1, 2); + transforms.push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendScale(2, 2, 2); + transforms.push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendSkew(1, 0); + transforms.push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendPerspective(800); + transforms.push_back(to_add); + + for (size_t i = 0; i < transforms.size(); ++i) { + for (size_t j = 0; j < transforms.size(); ++j) { + bool matches_type = transforms[i]->MatchesTypes(*transforms[j]); + EXPECT_TRUE((i == j && matches_type) || !matches_type); + } + } +} + +TEST(TransformOperationTest, MatchTypesSameLength) { + TransformOperations translates; + translates.AppendTranslate(1, 0, 0); + translates.AppendTranslate(1, 0, 0); + translates.AppendTranslate(1, 0, 0); + + TransformOperations skews; + skews.AppendSkew(0, 2); + skews.AppendSkew(0, 2); + skews.AppendSkew(0, 2); + + TransformOperations translates2; + translates2.AppendTranslate(0, 2, 0); + translates2.AppendTranslate(0, 2, 0); + translates2.AppendTranslate(0, 2, 0); + + TransformOperations translates3 = translates2; + + EXPECT_FALSE(translates.MatchesTypes(skews)); + EXPECT_TRUE(translates.MatchesTypes(translates2)); + EXPECT_TRUE(translates.MatchesTypes(translates3)); +} + +TEST(TransformOperationTest, MatchTypesDifferentLength) { + TransformOperations translates; + translates.AppendTranslate(1, 0, 0); + translates.AppendTranslate(1, 0, 0); + translates.AppendTranslate(1, 0, 0); + + TransformOperations skews; + skews.AppendSkew(2, 0); + skews.AppendSkew(2, 0); + + TransformOperations translates2; + translates2.AppendTranslate(0, 2, 0); + translates2.AppendTranslate(0, 2, 0); + + EXPECT_FALSE(translates.MatchesTypes(skews)); + EXPECT_FALSE(translates.MatchesTypes(translates2)); +} + +void GetIdentityOperations(ScopedVector<TransformOperations>* operations) { + TransformOperations* to_add = new TransformOperations(); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendTranslate(0, 0, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendTranslate(0, 0, 0); + to_add->AppendTranslate(0, 0, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendScale(1, 1, 1); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendScale(1, 1, 1); + to_add->AppendScale(1, 1, 1); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendSkew(0, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendSkew(0, 0); + to_add->AppendSkew(0, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendRotate(0, 0, 1, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendRotate(0, 0, 1, 0); + to_add->AppendRotate(0, 0, 1, 0); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendMatrix(gfx::Transform()); + operations->push_back(to_add); + + to_add = new TransformOperations(); + to_add->AppendMatrix(gfx::Transform()); + to_add->AppendMatrix(gfx::Transform()); + operations->push_back(to_add); +} + +TEST(TransformOperationTest, IdentityAlwaysMatches) { + ScopedVector<TransformOperations> operations; + GetIdentityOperations(&operations); + + for (size_t i = 0; i < operations.size(); ++i) { + for (size_t j = 0; j < operations.size(); ++j) + EXPECT_TRUE(operations[i]->MatchesTypes(*operations[j])); + } +} + +TEST(TransformOperationTest, ApplyTranslate) { + double x = 1; + double y = 2; + double z = 3; + TransformOperations operations; + operations.AppendTranslate(x, y, z); + gfx::Transform expected; + expected.Translate3d(x, y, z); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); +} + +TEST(TransformOperationTest, ApplyRotate) { + double x = 1; + double y = 2; + double z = 3; + double degrees = 80; + TransformOperations operations; + operations.AppendRotate(x, y, z, degrees); + gfx::Transform expected; + expected.RotateAbout(gfx::Vector3dF(x, y, z), degrees); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); +} + +TEST(TransformOperationTest, ApplyScale) { + double x = 1; + double y = 2; + double z = 3; + TransformOperations operations; + operations.AppendScale(x, y, z); + gfx::Transform expected; + expected.Scale3d(x, y, z); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); +} + +TEST(TransformOperationTest, ApplySkew) { + double x = 1; + double y = 2; + TransformOperations operations; + operations.AppendSkew(x, y); + gfx::Transform expected; + expected.SkewX(x); + expected.SkewY(y); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); +} + +TEST(TransformOperationTest, ApplyPerspective) { + double depth = 800; + TransformOperations operations; + operations.AppendPerspective(depth); + gfx::Transform expected; + expected.ApplyPerspectiveDepth(depth); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, operations.Apply()); +} + +TEST(TransformOperationTest, ApplyMatrix) { + double dx = 1; + double dy = 2; + double dz = 3; + gfx::Transform expected_matrix; + expected_matrix.Translate3d(dx, dy, dz); + TransformOperations matrix_transform; + matrix_transform.AppendMatrix(expected_matrix); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_matrix, matrix_transform.Apply()); +} + +TEST(TransformOperationTest, ApplyOrder) { + double sx = 2; + double sy = 4; + double sz = 8; + + double dx = 1; + double dy = 2; + double dz = 3; + + TransformOperations operations; + operations.AppendScale(sx, sy, sz); + operations.AppendTranslate(dx, dy, dz); + + gfx::Transform expected_scale_matrix; + expected_scale_matrix.Scale3d(sx, sy, sz); + + gfx::Transform expected_translate_matrix; + expected_translate_matrix.Translate3d(dx, dy, dz); + + gfx::Transform expected_combined_matrix = expected_scale_matrix; + expected_combined_matrix.PreconcatTransform(expected_translate_matrix); + + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_combined_matrix, operations.Apply()); +} + +TEST(TransformOperationTest, BlendOrder) { + double sx1 = 2; + double sy1 = 4; + double sz1 = 8; + + double dx1 = 1; + double dy1 = 2; + double dz1 = 3; + + double sx2 = 4; + double sy2 = 8; + double sz2 = 16; + + double dx2 = 10; + double dy2 = 20; + double dz2 = 30; + + TransformOperations operations_from; + operations_from.AppendScale(sx1, sy1, sz1); + operations_from.AppendTranslate(dx1, dy1, dz1); + + TransformOperations operations_to; + operations_to.AppendScale(sx2, sy2, sz2); + operations_to.AppendTranslate(dx2, dy2, dz2); + + gfx::Transform scale_from; + scale_from.Scale3d(sx1, sy1, sz1); + gfx::Transform translate_from; + translate_from.Translate3d(dx1, dy1, dz1); + + gfx::Transform scale_to; + scale_to.Scale3d(sx2, sy2, sz2); + gfx::Transform translate_to; + translate_to.Translate3d(dx2, dy2, dz2); + + double progress = 0.25; + + gfx::Transform blended_scale = scale_to; + blended_scale.Blend(scale_from, progress); + + gfx::Transform blended_translate = translate_to; + blended_translate.Blend(translate_from, progress); + + gfx::Transform expected = blended_scale; + expected.PreconcatTransform(blended_translate); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations_to.Blend(operations_from, progress)); +} + +static void CheckProgress(double progress, + const gfx::Transform& from_matrix, + const gfx::Transform& to_matrix, + const TransformOperations& from_transform, + const TransformOperations& to_transform) { + gfx::Transform expected_matrix = to_matrix; + expected_matrix.Blend(from_matrix, progress); + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected_matrix, to_transform.Blend(from_transform, progress)); +} + +TEST(TransformOperationTest, BlendProgress) { + double sx = 2; + double sy = 4; + double sz = 8; + TransformOperations operations_from; + operations_from.AppendScale(sx, sy, sz); + + gfx::Transform matrix_from; + matrix_from.Scale3d(sx, sy, sz); + + sx = 4; + sy = 8; + sz = 16; + TransformOperations operations_to; + operations_to.AppendScale(sx, sy, sz); + + gfx::Transform matrix_to; + matrix_to.Scale3d(sx, sy, sz); + + CheckProgress(-1, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(0, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(0.25, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(0.5, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(1, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(2, matrix_from, matrix_to, operations_from, operations_to); +} + +TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) { + double sx1 = 2; + double sy1 = 4; + double sz1 = 8; + + double dx1 = 1; + double dy1 = 2; + double dz1 = 3; + + double sx2 = 4; + double sy2 = 8; + double sz2 = 16; + + double dx2 = 10; + double dy2 = 20; + double dz2 = 30; + + TransformOperations operations_from; + operations_from.AppendScale(sx1, sy1, sz1); + operations_from.AppendTranslate(dx1, dy1, dz1); + + TransformOperations operations_to; + operations_to.AppendTranslate(dx2, dy2, dz2); + operations_to.AppendScale(sx2, sy2, sz2); + + gfx::Transform from; + from.Scale3d(sx1, sy1, sz1); + from.Translate3d(dx1, dy1, dz1); + + gfx::Transform to; + to.Translate3d(dx2, dy2, dz2); + to.Scale3d(sx2, sy2, sz2); + + double progress = 0.25; + + gfx::Transform expected = to; + expected.Blend(from, progress); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations_to.Blend(operations_from, progress)); +} + +TEST(TransformOperationTest, LargeRotationsWithSameAxis) { + TransformOperations operations_from; + operations_from.AppendRotate(0, 0, 1, 0); + + TransformOperations operations_to; + operations_to.AppendRotate(0, 0, 2, 360); + + double progress = 0.5; + + gfx::Transform expected; + expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations_to.Blend(operations_from, progress)); +} + +TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) { + TransformOperations operations_from; + operations_from.AppendRotate(0, 0, 1, 180); + + TransformOperations operations_to; + operations_to.AppendRotate(0, 0, -1, 180); + + double progress = 0.5; + + gfx::Transform expected; + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations_to.Blend(operations_from, progress)); +} + +TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) { + TransformOperations operations_from; + operations_from.AppendRotate(0, 0, 1, 175); + + TransformOperations operations_to; + operations_to.AppendRotate(0, 1, 0, 175); + + double progress = 0.5; + gfx::Transform matrix_from; + matrix_from.RotateAbout(gfx::Vector3dF(0, 0, 1), 175); + + gfx::Transform matrix_to; + matrix_to.RotateAbout(gfx::Vector3dF(0, 1, 0), 175); + + gfx::Transform expected = matrix_to; + expected.Blend(matrix_from, progress); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations_to.Blend(operations_from, progress)); +} + +TEST(TransformOperationTest, BlendRotationFromIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendRotate(0, 0, 1, 360); + + double progress = 0.5; + + gfx::Transform expected; + expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations.Blend(*identity_operations[i], progress)); + } +} + +TEST(TransformOperationTest, BlendTranslationFromIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendTranslate(2, 2, 2); + + double progress = 0.5; + + gfx::Transform expected; + expected.Translate3d(1, 1, 1); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations.Blend(*identity_operations[i], progress)); + } +} + +TEST(TransformOperationTest, BlendScaleFromIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendScale(3, 3, 3); + + double progress = 0.5; + + gfx::Transform expected; + expected.Scale3d(2, 2, 2); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations.Blend(*identity_operations[i], progress)); + } +} + +TEST(TransformOperationTest, BlendSkewFromIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendSkew(2, 2); + + double progress = 0.5; + + gfx::Transform expected; + expected.SkewX(1); + expected.SkewY(1); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations.Blend(*identity_operations[i], progress)); + } +} + +TEST(TransformOperationTest, BlendPerspectiveFromIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendPerspective(1000); + + double progress = 0.5; + + gfx::Transform expected; + expected.ApplyPerspectiveDepth( + 500 + 0.5 * std::numeric_limits<double>::max()); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, operations.Blend(*identity_operations[i], progress)); + } +} + +TEST(TransformOperationTest, BlendRotationToIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendRotate(0, 0, 1, 360); + + double progress = 0.5; + + gfx::Transform expected; + expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, identity_operations[i]->Blend(operations, progress)); + } +} + +TEST(TransformOperationTest, BlendTranslationToIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendTranslate(2, 2, 2); + + double progress = 0.5; + + gfx::Transform expected; + expected.Translate3d(1, 1, 1); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, identity_operations[i]->Blend(operations, progress)); + } +} + +TEST(TransformOperationTest, BlendScaleToIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendScale(3, 3, 3); + + double progress = 0.5; + + gfx::Transform expected; + expected.Scale3d(2, 2, 2); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, identity_operations[i]->Blend(operations, progress)); + } +} + +TEST(TransformOperationTest, BlendSkewToIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendSkew(2, 2); + + double progress = 0.5; + + gfx::Transform expected; + expected.SkewX(1); + expected.SkewY(1); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, identity_operations[i]->Blend(operations, progress)); + } +} + +TEST(TransformOperationTest, BlendPerspectiveToIdentity) { + ScopedVector<TransformOperations> identity_operations; + GetIdentityOperations(&identity_operations); + + for (size_t i = 0; i < identity_operations.size(); ++i) { + TransformOperations operations; + operations.AppendPerspective(1000); + + double progress = 0.5; + + gfx::Transform expected; + expected.ApplyPerspectiveDepth( + 500 + 0.5 * std::numeric_limits<double>::max()); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected, identity_operations[i]->Blend(operations, progress)); + } +} + +} // namespace +} // namespace cc |