summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-15 22:03:14 +0000
committerajuma@chromium.org <ajuma@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-15 22:03:14 +0000
commitdb2586f0cdfde4c4dc873e3bcd1d856382d4d2fa (patch)
tree05e0953f2ca06ca716bf458ece2d0f16e89f691e
parent61f697f26142f5081aaee176ccd82a2af170eabf (diff)
downloadchromium_src-db2586f0cdfde4c4dc873e3bcd1d856382d4d2fa.zip
chromium_src-db2586f0cdfde4c4dc873e3bcd1d856382d4d2fa.tar.gz
chromium_src-db2586f0cdfde4c4dc873e3bcd1d856382d4d2fa.tar.bz2
Compute bounds of animated layers
This adds support for computing the bounds of a box when transformed by the animations currently running on layer. This CL handles Scale and Translate animations, leaving other TransformOperations for future CLs. BUG=252472 Review URL: https://chromiumcodereview.appspot.com/21604002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217845 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--cc/animation/animation_curve.h10
-rw-r--r--cc/animation/keyframed_animation_curve.cc23
-rw-r--r--cc/animation/keyframed_animation_curve.h2
-rw-r--r--cc/animation/keyframed_animation_curve_unittest.cc25
-rw-r--r--cc/animation/layer_animation_controller.cc26
-rw-r--r--cc/animation/layer_animation_controller.h7
-rw-r--r--cc/animation/layer_animation_controller_unittest.cc70
-rw-r--r--cc/animation/timing_function.cc53
-rw-r--r--cc/animation/timing_function.h6
-rw-r--r--cc/animation/timing_function_unittest.cc70
-rw-r--r--cc/animation/transform_operation.cc121
-rw-r--r--cc/animation/transform_operation.h11
-rw-r--r--cc/animation/transform_operations.cc37
-rw-r--r--cc/animation/transform_operations.h11
-rw-r--r--cc/animation/transform_operations_unittest.cc172
-rw-r--r--cc/layers/layer.h8
-rw-r--r--cc/test/animation_test_common.cc5
-rw-r--r--cc/test/animation_test_common.h2
-rw-r--r--ui/compositor/transform_animation_curve_adapter.cc9
-rw-r--r--ui/compositor/transform_animation_curve_adapter.h2
20 files changed, 666 insertions, 4 deletions
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h
index 3860234..f4cdb10 100644
--- a/cc/animation/animation_curve.h
+++ b/cc/animation/animation_curve.h
@@ -10,6 +10,10 @@
#include "cc/output/filter_operations.h"
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
class FilterAnimationCurve;
@@ -49,6 +53,12 @@ class CC_EXPORT TransformAnimationCurve : public AnimationCurve {
virtual gfx::Transform GetValue(double t) const = 0;
+ // Sets |bounds| to be the bounding box for the region within which |box|
+ // will move during this animation. If this region cannot be computed,
+ // returns false.
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) 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 c016c91..14dfd9c 100644
--- a/cc/animation/keyframed_animation_curve.cc
+++ b/cc/animation/keyframed_animation_curve.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "cc/animation/keyframed_animation_curve.h"
+#include "ui/gfx/box_f.h"
namespace cc {
@@ -231,6 +232,28 @@ gfx::Transform KeyframedTransformAnimationCurve::GetValue(double t) const {
return GetCurveValue<gfx::Transform, TransformKeyframe>(&keyframes_, t);
}
+bool KeyframedTransformAnimationCurve::AnimatedBoundsForBox(
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ DCHECK_GE(keyframes_.size(), 2ul);
+ *bounds = gfx::BoxF();
+ for (size_t i = 0; i < keyframes_.size() - 1; ++i) {
+ gfx::BoxF bounds_for_step;
+ float min_progress = 0.0;
+ float max_progress = 1.0;
+ if (keyframes_[i]->timing_function())
+ keyframes_[i]->timing_function()->Range(&min_progress, &max_progress);
+ if (!keyframes_[i+1]->Value().BlendedBoundsForBox(box,
+ keyframes_[i]->Value(),
+ min_progress,
+ max_progress,
+ &bounds_for_step))
+ return false;
+ bounds->Union(bounds_for_step);
+ }
+ 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 9dda773..5892dc7 100644
--- a/cc/animation/keyframed_animation_curve.h
+++ b/cc/animation/keyframed_animation_curve.h
@@ -135,6 +135,8 @@ class CC_EXPORT KeyframedTransformAnimationCurve
// TransformAnimationCurve implementation
virtual gfx::Transform GetValue(double t) const OVERRIDE;
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const OVERRIDE;
private:
KeyframedTransformAnimationCurve();
diff --git a/cc/animation/keyframed_animation_curve_unittest.cc b/cc/animation/keyframed_animation_curve_unittest.cc
index 32efc14..48f511c0 100644
--- a/cc/animation/keyframed_animation_curve_unittest.cc
+++ b/cc/animation/keyframed_animation_curve_unittest.cc
@@ -7,6 +7,7 @@
#include "cc/animation/transform_operations.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
namespace cc {
namespace {
@@ -332,5 +333,29 @@ TEST(KeyframedAnimationCurveTest, CubicBezierTimingFunction) {
EXPECT_FLOAT_EQ(1.f, curve->GetValue(1.f));
}
+// Tests that animated bounds are computed as expected.
+TEST(KeyframedAnimationCurveTest, AnimatedBounds) {
+ 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);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 0.5, operations1, scoped_ptr<TimingFunction>()));
+ TransformOperations operations2;
+ operations2.AppendTranslate(4.0, 1.0, 2.0);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, EaseTimingFunction::Create()));
+
+ gfx::BoxF box(2.f, 3.f, 4.f, 1.f, 3.f, 2.f);
+ gfx::BoxF bounds;
+
+ EXPECT_TRUE(curve->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(2.f, 3.f, 3.f, 5.f, 6.f, 5.f).ToString(),
+ bounds.ToString());
+}
+
} // namespace
} // namespace cc
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 5823b79..08e2db7 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -12,6 +12,7 @@
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/animation/layer_animation_value_observer.h"
#include "cc/base/scoped_ptr_algorithm.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
@@ -343,6 +344,31 @@ void LayerAnimationController::RemoveEventObserver(
event_observers_.RemoveObserver(observer);
}
+bool LayerAnimationController::AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) {
+ // Compute bounds based on animations for which is_finished() is false.
+ // Do nothing if there are no such animations; in this case, it is assumed
+ // that callers will take care of computing bounds based on the owning layer's
+ // actual transform.
+ *bounds = gfx::BoxF();
+ 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();
+ gfx::BoxF animation_bounds;
+ bool success =
+ transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
+ if (!success)
+ return false;
+ bounds->Union(animation_bounds);
+ }
+
+ 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 e450f15..4138704 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -17,7 +17,10 @@
#include "cc/base/scoped_ptr_vector.h"
#include "ui/gfx/transform.h"
-namespace gfx { class Transform; }
+namespace gfx {
+class BoxF;
+class Transform;
+}
namespace cc {
@@ -100,6 +103,8 @@ class CC_EXPORT LayerAnimationController
layer_animation_delegate_ = delegate;
}
+ bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds);
+
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 2e06f5c..6e94734 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -12,6 +12,7 @@
#include "cc/test/animation_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
@@ -1117,5 +1118,74 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_NE(0.5f, dummy.opacity());
}
+TEST(LayerAnimationControllerTest, AnimatedBounds) {
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+
+ 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>(), 1, 1, Animation::Transform));
+ controller_impl->AddAnimation(animation.Pass());
+
+ 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>(), 2, 2, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+
+ gfx::BoxF box(1.f, 2.f, -1.f, 3.f, 4.f, 5.f);
+ gfx::BoxF bounds;
+
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 13.f, 19.f, 20.f).ToString(),
+ bounds.ToString());
+
+ controller_impl->GetAnimation(1, Animation::Transform)->SetRunState(
+ cc::Animation::Finished, 0.0);
+
+ // Only the unfinished animation should affect the animated bounds.
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 7.f, 16.f, 20.f).ToString(),
+ bounds.ToString());
+
+ controller_impl->GetAnimation(2, Animation::Transform)->SetRunState(
+ cc::Animation::Finished, 0.0);
+
+ // There are no longer any running animations.
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString());
+
+ // Add an animation whose bounds we don't yet support computing.
+ scoped_ptr<KeyframedTransformAnimationCurve> curve3(
+ KeyframedTransformAnimationCurve::Create());
+ TransformOperations operations3;
+ curve3->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations3, scoped_ptr<TimingFunction>()));
+ operations3.AppendSkew(1.0, 2.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->AnimatedBoundsForBox(box, &bounds));
+}
+
} // namespace
} // namespace cc
diff --git a/cc/animation/timing_function.cc b/cc/animation/timing_function.cc
index 65d607f..7fdb37f 100644
--- a/cc/animation/timing_function.cc
+++ b/cc/animation/timing_function.cc
@@ -12,7 +12,7 @@ namespace cc {
namespace {
-static const double BEZIER_EPSILON = 1e-7;
+static const double kBezierEpsilon = 1e-7;
static const int MAX_STEPS = 30;
static double eval_bezier(double x1, double x2, double t) {
@@ -48,14 +48,14 @@ static double bezier_interp(double x1,
double step = 1.0;
for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) {
const double error = eval_bezier(x1, x2, t) - x;
- if (std::abs(error) < BEZIER_EPSILON)
+ if (std::abs(error) < kBezierEpsilon)
break;
t += error > 0.0 ? -step : step;
}
// We should have terminated the above loop because we got close to x, not
// because we exceeded MAX_STEPS. Do a DCHECK here to confirm.
- DCHECK_GT(BEZIER_EPSILON, std::abs(eval_bezier(x1, x2, t) - x));
+ DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x));
// Step 2. Return the interpolated y values at the t we computed above.
return eval_bezier(y1, y2, t);
@@ -93,6 +93,53 @@ scoped_ptr<AnimationCurve> CubicBezierTimingFunction::Clone() const {
new CubicBezierTimingFunction(*this)).PassAs<AnimationCurve>();
}
+void CubicBezierTimingFunction::Range(float* min, float* max) const {
+ *min = 0.f;
+ *max = 1.f;
+ if (0.f <= y1_ && y1_ < 1.f && 0.f <= y2_ && y2_ <= 1.f)
+ return;
+
+ // Represent the function's derivative in the form at^2 + bt + c.
+ float a = 3.f * (y1_ - y2_) + 1.f;
+ float b = 2.f * (y2_ - 2.f * y1_);
+ float c = y1_;
+
+ // Check if the derivative is constant.
+ if (std::abs(a) < kBezierEpsilon &&
+ std::abs(b) < kBezierEpsilon)
+ return;
+
+ // Zeros of the function's derivative.
+ float t_1 = 0.f;
+ float t_2 = 0.f;
+
+ if (std::abs(a) < kBezierEpsilon) {
+ // The function's derivative is linear.
+ t_1 = -c / b;
+ } else {
+ // The function's derivative is a quadratic. We find the zeros of this
+ // quadratic using the quadratic formula.
+ float discriminant = b * b - 4 * a * c;
+ if (discriminant < 0.f)
+ return;
+ float discriminant_sqrt = sqrt(discriminant);
+ t_1 = (-b + discriminant_sqrt) / (2.f * a);
+ t_2 = (-b - discriminant_sqrt) / (2.f * a);
+ }
+
+ float sol_1 = 0.f;
+ float sol_2 = 0.f;
+
+ if (0.f < t_1 && t_1 < 1.f)
+ sol_1 = eval_bezier(y1_, y2_, t_1);
+
+ if (0.f < t_2 && t_2 < 1.f)
+ sol_2 = eval_bezier(y1_, y2_, t_2);
+
+ *min = std::min(std::min(*min, sol_1), sol_2);
+ *max = std::max(std::max(*max, sol_1), sol_2);
+}
+
// These numbers come from
// http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag.
scoped_ptr<TimingFunction> EaseTimingFunction::Create() {
diff --git a/cc/animation/timing_function.h b/cc/animation/timing_function.h
index 3aa2f25..056ad41 100644
--- a/cc/animation/timing_function.h
+++ b/cc/animation/timing_function.h
@@ -19,6 +19,10 @@ class CC_EXPORT TimingFunction : public FloatAnimationCurve {
// Partial implementation of FloatAnimationCurve.
virtual double Duration() const OVERRIDE;
+ // The smallest and largest values returned by GetValue for inputs in
+ // [0, 1].
+ virtual void Range(float* min, float* max) const = 0;
+
protected:
TimingFunction();
@@ -36,6 +40,8 @@ class CC_EXPORT CubicBezierTimingFunction : public TimingFunction {
virtual float GetValue(double time) const OVERRIDE;
virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE;
+ virtual void Range(float* min, float* max) const OVERRIDE;
+
protected:
CubicBezierTimingFunction(double x1, double y1, double x2, double y2);
diff --git a/cc/animation/timing_function_unittest.cc b/cc/animation/timing_function_unittest.cc
index 2caa12d..4be225f 100644
--- a/cc/animation/timing_function_unittest.cc
+++ b/cc/animation/timing_function_unittest.cc
@@ -67,5 +67,75 @@ TEST(TimingFunctionTest, CubicBezierTimingFunctionUnclampedYValues) {
EXPECT_NEAR(function->GetValue(1.0), 1.0, epsilon);
}
+TEST(TimingFunctionTest, CubicBezierTimingFunctionRange) {
+ double epsilon = 0.00015;
+ float min, max;
+
+ // Derivative is a constant.
+ scoped_ptr<CubicBezierTimingFunction> function =
+ CubicBezierTimingFunction::Create(0.25, (1.0 / 3.0), 0.75, (2.0 / 3.0));
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative is linear.
+ function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, (-1.0 / 6.0));
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.225, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has no real roots.
+ function = CubicBezierTimingFunction::Create(0.25, 0.25, 0.75, 0.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has exactly one real root.
+ function = CubicBezierTimingFunction::Create(0.0, 1.0, 1.0, 0.0);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has one root < 0 and one root > 1.
+ function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 0.9);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots in [0,1].
+ function = CubicBezierTimingFunction::Create(0.25, 2.5, 0.75, 0.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_NEAR(max, 1.28818, epsilon);
+ function = CubicBezierTimingFunction::Create(0.25, 0.5, 0.75, -1.5);
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.28818, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has one root < 0 and one root in [0,1].
+ function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 1.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_NEAR(max, 1.10755, epsilon);
+
+ // Derivative has one root in [0,1] and one root > 1.
+ function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, 0.9);
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.10755, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots < 0.
+ function = CubicBezierTimingFunction::Create(0.25, 0.3, 0.75, 0.633);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots > 1.
+ function = CubicBezierTimingFunction::Create(0.25, 0.367, 0.75, 0.7);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+}
+
} // namespace
} // namespace cc
diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc
index dacea06..b7bb063 100644
--- a/cc/animation/transform_operation.cc
+++ b/cc/animation/transform_operation.cc
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
#include <cmath>
#include <limits>
#include "cc/animation/transform_operation.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/vector3d_f.h"
namespace {
@@ -177,4 +179,123 @@ bool TransformOperation::BlendTransformOperations(
return true;
}
+static void ApplyScaleToBox(float x_scale,
+ float y_scale,
+ float z_scale,
+ gfx::BoxF* box) {
+ if (x_scale < 0)
+ box->set_x(-box->right());
+ if (y_scale < 0)
+ box->set_y(-box->bottom());
+ if (z_scale < 0)
+ box->set_z(-box->front());
+ box->Scale(std::abs(x_scale), std::abs(y_scale), std::abs(z_scale));
+}
+
+static void UnionBoxWithZeroScale(gfx::BoxF* box) {
+ float min_x = std::min(box->x(), 0.f);
+ float min_y = std::min(box->y(), 0.f);
+ float min_z = std::min(box->z(), 0.f);
+ float max_x = std::max(box->right(), 0.f);
+ float max_y = std::max(box->bottom(), 0.f);
+ float max_z = std::max(box->front(), 0.f);
+ *box = gfx::BoxF(
+ min_x, min_y, min_z, max_x - min_x, max_y - min_y, max_z - min_z);
+}
+
+bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperation* from,
+ const TransformOperation* to,
+ double min_progress,
+ double max_progress,
+ gfx::BoxF* bounds) {
+ bool is_identity_from = IsOperationIdentity(from);
+ bool is_identity_to = IsOperationIdentity(to);
+ if (is_identity_from && is_identity_to) {
+ *bounds = box;
+ return true;
+ }
+
+ TransformOperation::Type interpolation_type =
+ TransformOperation::TransformOperationIdentity;
+ if (is_identity_to)
+ interpolation_type = from->type;
+ else
+ interpolation_type = to->type;
+
+ switch (interpolation_type) {
+ case TransformOperation::TransformOperationTranslate: {
+ double from_x, from_y, from_z;
+ if (is_identity_from) {
+ from_x = from_y = from_z = 0.0;
+ } else {
+ from_x = from->translate.x;
+ from_y = from->translate.y;
+ from_z = from->translate.z;
+ }
+ double to_x, to_y, to_z;
+ if (is_identity_to) {
+ to_x = to_y = to_z = 0.0;
+ } else {
+ to_x = to->translate.x;
+ to_y = to->translate.y;
+ to_z = to->translate.z;
+ }
+ *bounds = box;
+ *bounds += gfx::Vector3dF(BlendDoubles(from_x, to_x, min_progress),
+ BlendDoubles(from_y, to_y, min_progress),
+ BlendDoubles(from_z, to_z, min_progress));
+ gfx::BoxF bounds_max = box;
+ bounds_max += gfx::Vector3dF(BlendDoubles(from_x, to_x, max_progress),
+ BlendDoubles(from_y, to_y, max_progress),
+ BlendDoubles(from_z, to_z, max_progress));
+ bounds->Union(bounds_max);
+ return true;
+ }
+ case TransformOperation::TransformOperationScale: {
+ double from_x, from_y, from_z;
+ if (is_identity_from) {
+ from_x = from_y = from_z = 1.0;
+ } else {
+ from_x = from->scale.x;
+ from_y = from->scale.y;
+ from_z = from->scale.z;
+ }
+ double to_x, to_y, to_z;
+ if (is_identity_to) {
+ to_x = to_y = to_z = 1.0;
+ } else {
+ to_x = to->scale.x;
+ to_y = to->scale.y;
+ to_z = to->scale.z;
+ }
+ *bounds = box;
+ ApplyScaleToBox(BlendDoubles(from_x, to_x, min_progress),
+ BlendDoubles(from_y, to_y, min_progress),
+ BlendDoubles(from_z, to_z, min_progress),
+ bounds);
+ gfx::BoxF bounds_max = box;
+ ApplyScaleToBox(BlendDoubles(from_x, to_x, max_progress),
+ BlendDoubles(from_y, to_y, max_progress),
+ BlendDoubles(from_z, to_z, max_progress),
+ &bounds_max);
+ if (!bounds->IsEmpty() && !bounds_max.IsEmpty()) {
+ bounds->Union(bounds_max);
+ } else if (!bounds->IsEmpty()) {
+ UnionBoxWithZeroScale(bounds);
+ } else if (!bounds_max.IsEmpty()) {
+ UnionBoxWithZeroScale(&bounds_max);
+ *bounds = bounds_max;
+ }
+
+ return true;
+ }
+ case TransformOperation::TransformOperationIdentity:
+ *bounds = box;
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace cc
diff --git a/cc/animation/transform_operation.h b/cc/animation/transform_operation.h
index 74673ab..d5f2830 100644
--- a/cc/animation/transform_operation.h
+++ b/cc/animation/transform_operation.h
@@ -7,6 +7,10 @@
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
struct TransformOperation {
@@ -56,6 +60,13 @@ struct TransformOperation {
const TransformOperation* to,
double progress,
gfx::Transform* result);
+
+ static bool BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperation* from,
+ const TransformOperation* to,
+ double min_progress,
+ double max_progress,
+ gfx::BoxF* bounds);
};
} // namespace cc
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index d74f1a5..42b3559 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/gfx/vector3d_f.h"
@@ -41,6 +42,42 @@ gfx::Transform TransformOperations::Blend(
return to_return;
}
+bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperations& from,
+ double min_progress,
+ double 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::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 b5f960f..96bf1d1 100644
--- a/cc/animation/transform_operations.h
+++ b/cc/animation/transform_operations.h
@@ -13,6 +13,7 @@
#include "ui/gfx/transform.h"
namespace gfx {
+class BoxF;
struct DecomposedTransform;
}
@@ -45,6 +46,16 @@ class CC_EXPORT TransformOperations {
// http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition.
gfx::Transform Blend(const TransformOperations& from, double progress) const;
+ // Sets |bounds| be the bounding box for the region within which |box| will
+ // exist when it is transformed by the result of calling Blend on |from| and
+ // with progress in the range [min_progress, max_progress]. If this region
+ // cannot be computed, returns false.
+ bool BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperations& from,
+ double min_progress,
+ double max_progress,
+ gfx::BoxF* bounds) const;
+
// Returns true if this operation and its descendants have the same types
// as other and its descendants.
bool MatchesTypes(const TransformOperations& other) const;
diff --git a/cc/animation/transform_operations_unittest.cc b/cc/animation/transform_operations_unittest.cc
index 8fc9b94..b82d911 100644
--- a/cc/animation/transform_operations_unittest.cc
+++ b/cc/animation/transform_operations_unittest.cc
@@ -8,6 +8,7 @@
#include "cc/animation/transform_operations.h"
#include "cc/test/geometry_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/vector3d_f.h"
namespace cc {
@@ -707,5 +708,176 @@ TEST(TransformOperationTest, ExtrapolateMatrixBlending) {
expected, operations1.Blend(operations2, -0.5));
}
+TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) {
+ TransformOperations operations_from;
+ operations_from.AppendScale(2.0, 4.0, 8.0);
+ operations_from.AppendTranslate(1.0, 2.0, 3.0);
+
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(10.0, 20.0, 30.0);
+ operations_to.AppendScale(4.0, 8.0, 16.0);
+
+ gfx::BoxF box(1.f, 1.f, 1.f);
+ gfx::BoxF bounds;
+
+ double min_progress = 0.0;
+ double max_progress = 1.0;
+
+ EXPECT_FALSE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+}
+
+TEST(TransformOperationTest, BlendedBoundsForIdentity) {
+ TransformOperations operations_from;
+ operations_from.AppendIdentity();
+ TransformOperations operations_to;
+ operations_to.AppendIdentity();
+
+ gfx::BoxF box(1.f, 2.f, 3.f);
+ gfx::BoxF bounds;
+
+ double min_progress = 0.0;
+ double max_progress = 1.0;
+
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(box.ToString(), bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForTranslate) {
+ TransformOperations operations_from;
+ operations_from.AppendTranslate(3.0, -4.0, 2.0);
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(7.0, 4.0, -2.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ double min_progress = -0.5;
+ double max_progress = 1.5;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(2.f, -6.f, -1.f, 12.f, 20.f, 12.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.0;
+ max_progress = 1.0;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(4.f, -2.f, 1.f, 8.f, 12.f, 8.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, 1.f, 11.f, 8.f, 6.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, -2.f, 3.f, 7.f, 8.f, 6.f).ToString(),
+ bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForScale) {
+ TransformOperations operations_from;
+ operations_from.AppendScale(3.0, 0.5, 2.0);
+ TransformOperations operations_to;
+ operations_to.AppendScale(7.0, 4.0, -2.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ double min_progress = -0.5;
+ double max_progress = 1.5;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, -7.5f, -28.f, 44.f, 42.f, 56.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.0;
+ max_progress = 1.0;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(3.f, 1.f, -14.f, 32.f, 23.f, 28.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -14.f, 34.f, 22.f, 21.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 1.f, 3.f, 14.f, 5.f, 11.f).ToString(),
+ bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsWithZeroScale) {
+ TransformOperations zero_scale;
+ zero_scale.AppendScale(0.0, 0.0, 0.0);
+ TransformOperations non_zero_scale;
+ non_zero_scale.AppendScale(2.0, -4.0, 5.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ double min_progress = 0.0;
+ double max_progress = 1.0;
+ EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
+ box, non_zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(non_zero_scale.BlendedBoundsForBox(
+ box, zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
+ box, zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForSequence) {
+ TransformOperations operations_from;
+ operations_from.AppendTranslate(2.0, 4.0, -1.0);
+ operations_from.AppendScale(-1.0, 2.0, 3.0);
+ operations_from.AppendTranslate(1.0, -5.0, 1.0);
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(6.0, -2.0, 3.0);
+ operations_to.AppendScale(-3.0, -2.0, 5.0);
+ operations_to.AppendTranslate(13.0, -1.0, 5.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ double min_progress = -0.5;
+ double max_progress = 1.5;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-57.f, -59.f, -1.f, 76.f, 112.f, 80.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.0;
+ max_progress = 1.0;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-32.f, -25.f, 7.f, 42.f, 44.f, 48.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-33.f, -13.f, 3.f, 57.f, 19.f, 52.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-7.f, -3.f, 2.f, 15.f, 23.f, 20.f).ToString(),
+ bounds.ToString());
+}
+
} // namespace
} // namespace cc
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 035e071..6c08071 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -30,6 +30,10 @@
#include "ui/gfx/rect_f.h"
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
class Animation;
@@ -328,6 +332,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
void SuspendAnimations(double monotonic_time);
void ResumeAnimations(double monotonic_time);
+ bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds) {
+ return layer_animation_controller_->AnimatedBoundsForBox(box, bounds);
+ }
+
LayerAnimationController* layer_animation_controller() {
return layer_animation_controller_.get();
}
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index a667c1b..cfab637 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -124,6 +124,11 @@ gfx::Transform FakeTransformTransition::GetValue(double time) const {
return gfx::Transform();
}
+bool FakeTransformTransition::AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ return false;
+}
+
scoped_ptr<cc::AnimationCurve> FakeTransformTransition::Clone() const {
return make_scoped_ptr(
new FakeTransformTransition(*this)).PassAs<cc::AnimationCurve>();
diff --git a/cc/test/animation_test_common.h b/cc/test/animation_test_common.h
index cab2ccf..4d3efa6 100644
--- a/cc/test/animation_test_common.h
+++ b/cc/test/animation_test_common.h
@@ -38,6 +38,8 @@ class FakeTransformTransition : public TransformAnimationCurve {
virtual double Duration() const OVERRIDE;
virtual gfx::Transform GetValue(double time) const OVERRIDE;
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const OVERRIDE;
virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE;
diff --git a/ui/compositor/transform_animation_curve_adapter.cc b/ui/compositor/transform_animation_curve_adapter.cc
index 96500b3..4b493bc 100644
--- a/ui/compositor/transform_animation_curve_adapter.cc
+++ b/ui/compositor/transform_animation_curve_adapter.cc
@@ -51,4 +51,13 @@ gfx::Transform TransformAnimationCurveAdapter::GetValue(
return gfx::ComposeTransform(to_return);
}
+bool TransformAnimationCurveAdapter::AnimatedBoundsForBox(
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ // TODO(ajuma): Once cc::TransformOperation::BlendedBoundsForBox supports
+ // computing bounds for TransformOperationMatrix, use that to compute
+ // the bounds we need here.
+ return false;
+}
+
} // namespace ui
diff --git a/ui/compositor/transform_animation_curve_adapter.h b/ui/compositor/transform_animation_curve_adapter.h
index 7b6bdce..e721550 100644
--- a/ui/compositor/transform_animation_curve_adapter.h
+++ b/ui/compositor/transform_animation_curve_adapter.h
@@ -26,6 +26,8 @@ class TransformAnimationCurveAdapter :public cc::TransformAnimationCurve {
virtual double Duration() const OVERRIDE;
virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE;
virtual gfx::Transform GetValue(double t) const OVERRIDE;
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const OVERRIDE;
private:
Tween::Type tween_type_;