summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authoravallee@chromium.org <avallee@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-24 01:33:11 +0000
committeravallee@chromium.org <avallee@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-24 01:33:11 +0000
commit3d86dd7a4ebd896a46f4c07cd7a2fbb6df726217 (patch)
tree54751d98283fefa47e68978a6a0218e1966b53b5 /cc
parent938023f551e85235e39ec7a9ebbb4cd94764d9f0 (diff)
downloadchromium_src-3d86dd7a4ebd896a46f4c07cd7a2fbb6df726217.zip
chromium_src-3d86dd7a4ebd896a46f4c07cd7a2fbb6df726217.tar.gz
chromium_src-3d86dd7a4ebd896a46f4c07cd7a2fbb6df726217.tar.bz2
Compute accurate screen space bounds for animated layers.
This walks up the layer tree transforming a layer's bounds as a box through the different layer transforms and uses the LayerAnimationController::AnimationBoundsForBox to transform the box at layers which are animated. Expansion of the box is included for filters which change the bounds, however, the LayerAnimationController implementation is currently stubbed. + Added computation of animation bounds of layers in the screen space. - Removed invalid assertion in LayerAnimationControllerTest.AnimatedBounds. ~ Renamed methods to use the uniform naming AnimationBounds. ~ Extracted LTHC::ApplySublayerTransformAboutAnchor from LTHC::CalculateDrawPropertiesInternal. ~ Fixed LayerImpl::AsValueInto to correctly calculate bounds. ! Did not substitute implementation of ApplySublayerTransformAboutAnchor inside CalculateDrawPropertiesInternal due to performance regression. BUG=280375 Review URL: https://codereview.chromium.org/109193002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246767 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-rw-r--r--cc/animation/animation_curve.h1
-rw-r--r--cc/animation/keyframed_animation_curve.cc9
-rw-r--r--cc/animation/keyframed_animation_curve.h1
-rw-r--r--cc/animation/layer_animation_controller.cc34
-rw-r--r--cc/animation/layer_animation_controller.h12
-rw-r--r--cc/animation/layer_animation_controller_unittest.cc11
-rw-r--r--cc/cc.gyp2
-rw-r--r--cc/cc_tests.gyp1
-rw-r--r--cc/debug/debug_rect_history.cc6
-rw-r--r--cc/layers/layer.h4
-rw-r--r--cc/layers/layer_impl.cc16
-rw-r--r--cc/layers/layer_impl.h10
-rw-r--r--cc/layers/layer_utils.cc160
-rw-r--r--cc/layers/layer_utils.h29
-rw-r--r--cc/layers/layer_utils_unittest.cc258
-rw-r--r--cc/test/animation_test_common.cc33
-rw-r--r--cc/test/animation_test_common.h5
-rw-r--r--cc/trees/layer_tree_host_common.h15
18 files changed, 574 insertions, 33 deletions
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h
index 9bcccba..0ae2640 100644
--- a/cc/animation/animation_curve.h
+++ b/cc/animation/animation_curve.h
@@ -84,6 +84,7 @@ class CC_EXPORT FilterAnimationCurve : public AnimationCurve {
virtual ~FilterAnimationCurve() {}
virtual FilterOperations GetValue(double t) const = 0;
+ virtual bool HasFilterThatMovesPixels() 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 d855dec..39266b1 100644
--- a/cc/animation/keyframed_animation_curve.cc
+++ b/cc/animation/keyframed_animation_curve.cc
@@ -369,4 +369,13 @@ FilterOperations KeyframedFilterAnimationCurve::GetValue(double t) const {
return GetCurveValue<FilterOperations, FilterKeyframe>(&keyframes_, t);
}
+bool KeyframedFilterAnimationCurve::HasFilterThatMovesPixels() const {
+ for (size_t i = 0; i < keyframes_.size(); ++i) {
+ if (keyframes_[i]->Value().HasFilterThatMovesPixels()) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace cc
diff --git a/cc/animation/keyframed_animation_curve.h b/cc/animation/keyframed_animation_curve.h
index 4b5ac3b..ade65b7 100644
--- a/cc/animation/keyframed_animation_curve.h
+++ b/cc/animation/keyframed_animation_curve.h
@@ -210,6 +210,7 @@ class CC_EXPORT KeyframedFilterAnimationCurve
// FilterAnimationCurve implementation
virtual FilterOperations GetValue(double t) const OVERRIDE;
+ virtual bool HasFilterThatMovesPixels() const OVERRIDE;
private:
KeyframedFilterAnimationCurve();
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 623949d..918d8b8 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -384,8 +384,38 @@ void LayerAnimationController::RemoveEventObserver(
event_observers_.RemoveObserver(observer);
}
-bool LayerAnimationController::AnimatedBoundsForBox(const gfx::BoxF& box,
- gfx::BoxF* bounds) {
+bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const {
+ for (size_t i = 0; i < active_animations_.size(); ++i) {
+ if (!active_animations_[i]->is_finished() &&
+ active_animations_[i]->target_property() == Animation::Filter &&
+ active_animations_[i]
+ ->curve()
+ ->ToFilterAnimationCurve()
+ ->HasFilterThatMovesPixels())
+ return true;
+ }
+
+ return false;
+}
+
+bool LayerAnimationController::HasTransformAnimationThatInflatesBounds() const {
+ return IsAnimatingProperty(Animation::Transform);
+}
+
+bool LayerAnimationController::FilterAnimationBoundsForBox(
+ const gfx::BoxF& box, gfx::BoxF* bounds) const {
+ // TODO(avallee): Implement.
+ return false;
+}
+
+bool LayerAnimationController::TransformAnimationBoundsForBox(
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ DCHECK(HasTransformAnimationThatInflatesBounds())
+ << "TransformAnimationBoundsForBox will give incorrect results if there "
+ << "are no transform animations affecting bounds, non-animated transform "
+ << "is not known";
+
// 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
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h
index 8596b50..bb75107 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -109,7 +109,17 @@ class CC_EXPORT LayerAnimationController
layer_animation_delegate_ = delegate;
}
- bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds);
+ bool HasFilterAnimationThatInflatesBounds() const;
+ bool HasTransformAnimationThatInflatesBounds() const;
+ bool HasAnimationThatInflatesBounds() const {
+ return HasTransformAnimationThatInflatesBounds() ||
+ HasFilterAnimationThatInflatesBounds();
+ }
+
+ bool FilterAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const;
+ bool TransformAnimationBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) 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 f7918cd..fd54572 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -1376,7 +1376,7 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_NE(0.5f, dummy.opacity());
}
-TEST(LayerAnimationControllerTest, AnimatedBounds) {
+TEST(LayerAnimationControllerTest, TransformAnimationBounds) {
scoped_refptr<LayerAnimationController> controller_impl(
LayerAnimationController::Create(0));
@@ -1411,7 +1411,7 @@ TEST(LayerAnimationControllerTest, AnimatedBounds) {
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_TRUE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 13.f, 19.f, 20.f).ToString(),
bounds.ToString());
@@ -1419,7 +1419,7 @@ TEST(LayerAnimationControllerTest, AnimatedBounds) {
->SetRunState(Animation::Finished, 0.0);
// Only the unfinished animation should affect the animated bounds.
- EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_TRUE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 7.f, 16.f, 20.f).ToString(),
bounds.ToString());
@@ -1427,8 +1427,7 @@ TEST(LayerAnimationControllerTest, AnimatedBounds) {
->SetRunState(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());
+ EXPECT_FALSE(controller_impl->HasTransformAnimationThatInflatesBounds());
// Add an animation whose bounds we don't yet support computing.
scoped_ptr<KeyframedTransformAnimationCurve> curve3(
@@ -1444,7 +1443,7 @@ TEST(LayerAnimationControllerTest, AnimatedBounds) {
animation = Animation::Create(
curve3.PassAs<AnimationCurve>(), 3, 3, Animation::Transform);
controller_impl->AddAnimation(animation.Pass());
- EXPECT_FALSE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_FALSE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
}
// Tests that AbortAnimations aborts all animations targeting the specified
diff --git a/cc/cc.gyp b/cc/cc.gyp
index f23fab5..759453d 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -166,6 +166,8 @@
'layers/layer_lists.h',
'layers/layer_position_constraint.cc',
'layers/layer_position_constraint.h',
+ 'layers/layer_utils.cc',
+ 'layers/layer_utils.h',
'layers/nine_patch_layer.cc',
'layers/nine_patch_layer.h',
'layers/nine_patch_layer_impl.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 65efce5..bbf457c 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -33,6 +33,7 @@
'layers/layer_iterator_unittest.cc',
'layers/layer_position_constraint_unittest.cc',
'layers/layer_unittest.cc',
+ 'layers/layer_utils_unittest.cc',
'layers/nine_patch_layer_impl_unittest.cc',
'layers/nine_patch_layer_unittest.cc',
'layers/picture_image_layer_impl_unittest.cc',
diff --git a/cc/debug/debug_rect_history.cc b/cc/debug/debug_rect_history.cc
index 679beaf..c361603 100644
--- a/cc/debug/debug_rect_history.cc
+++ b/cc/debug/debug_rect_history.cc
@@ -6,6 +6,7 @@
#include "cc/base/math_util.h"
#include "cc/layers/layer_impl.h"
+#include "cc/layers/layer_utils.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/layer_tree_host.h"
@@ -247,8 +248,11 @@ void DebugRectHistory::SaveLayerAnimationBoundsRects(
it != end; ++it) {
if (!it.represents_itself())
continue;
+
+ // TODO(avallee): Figure out if we should show something for a layer who's
+ // animating bounds but that we can't compute them.
gfx::BoxF inflated_bounds;
- if (!(*it)->GetAnimationBounds(&inflated_bounds))
+ if (!LayerUtils::GetAnimationBounds(**it, &inflated_bounds))
continue;
debug_rects_.push_back(DebugRect(ANIMATION_BOUNDS_RECT_TYPE,
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 5f9caf6..df358f2 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -397,10 +397,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
void PauseAnimation(int animation_id, double time_offset);
void RemoveAnimation(int animation_id);
- 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/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 7444b77..beb282d 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -17,10 +17,12 @@
#include "cc/debug/micro_benchmark_impl.h"
#include "cc/debug/traced_value.h"
#include "cc/input/layer_scroll_offset_delegate.h"
+#include "cc/layers/layer_utils.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/quad_sink.h"
#include "cc/output/copy_output_request.h"
#include "cc/quads/debug_border_draw_quad.h"
+#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/proxy.h"
@@ -1305,13 +1307,13 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const {
state->SetBoolean("can_use_lcd_text", can_use_lcd_text());
state->SetBoolean("contents_opaque", contents_opaque());
- if (layer_animation_controller_->IsAnimatingProperty(Animation::Transform) ||
- layer_animation_controller_->IsAnimatingProperty(Animation::Filter)) {
- gfx::BoxF box(bounds().width(), bounds().height(), 0.f);
- gfx::BoxF inflated;
- if (layer_animation_controller_->AnimatedBoundsForBox(box, &inflated))
- state->Set("animated_bounds", MathUtil::AsValue(inflated).release());
- }
+ state->SetBoolean(
+ "has_animation_bounds",
+ layer_animation_controller()->HasAnimationThatInflatesBounds());
+
+ gfx::BoxF box;
+ if (LayerUtils::GetAnimationBounds(*this, &box))
+ state->Set("animation_bounds", MathUtil::AsValue(box).release());
if (debug_info_.get()) {
std::string str;
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 75f02ec..66fa84b 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -350,12 +350,6 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver,
float contents_scale_y() const { return draw_properties_.contents_scale_y; }
void SetContentsScale(float contents_scale_x, float contents_scale_y);
- // Computes a box in screen space that should entirely contain the layer's
- // bounds through the entirety of the layer's current animation. Returns true
- // and sets |out| to the inflation if there are animations that can inflate
- // bounds in the path to the root layer. Returns false otherwise.
- bool GetAnimationBounds(gfx::BoxF* out) const { return false; }
-
virtual void CalculateContentsScale(float ideal_contents_scale,
float device_scale_factor,
float page_scale_factor,
@@ -465,6 +459,10 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver,
return layer_animation_controller_.get();
}
+ const LayerAnimationController* layer_animation_controller() const {
+ return layer_animation_controller_.get();
+ }
+
virtual Region VisibleContentOpaqueRegion() const;
virtual void DidBecomeActive();
diff --git a/cc/layers/layer_utils.cc b/cc/layers/layer_utils.cc
new file mode 100644
index 0000000..7da41a3
--- /dev/null
+++ b/cc/layers/layer_utils.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 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/layers/layer_utils.h"
+
+#include "cc/layers/layer_impl.h"
+#include "cc/trees/layer_tree_host_common.h"
+#include "ui/gfx/box_f.h"
+
+namespace cc {
+
+namespace {
+
+bool HasAnimationThatInflatesBounds(const LayerImpl& layer) {
+ return layer.layer_animation_controller()->HasAnimationThatInflatesBounds();
+}
+
+bool HasFilterAnimationThatInflatesBounds(const LayerImpl& layer) {
+ return layer.layer_animation_controller()
+ ->HasFilterAnimationThatInflatesBounds();
+}
+
+bool HasTransformAnimationThatInflatesBounds(const LayerImpl& layer) {
+ return layer.layer_animation_controller()
+ ->HasTransformAnimationThatInflatesBounds();
+}
+
+inline bool HasAncestorTransformAnimation(const LayerImpl& layer) {
+ return layer.screen_space_transform_is_animating();
+}
+
+inline bool HasAncestorFilterAnimation(const LayerImpl& layer) {
+ for (const LayerImpl* current = &layer; current;
+ current = current->parent()) {
+ if (HasFilterAnimationThatInflatesBounds(*current))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+bool LayerUtils::GetAnimationBounds(const LayerImpl& layer_in, gfx::BoxF* out) {
+ // We don't care about animated bounds for invisible layers.
+ if (!layer_in.DrawsContent())
+ return false;
+
+ // We also don't care for layers that are not animated or a child of an
+ // animated layer.
+ if (!HasAncestorTransformAnimation(layer_in) &&
+ !HasAncestorFilterAnimation(layer_in))
+ return false;
+
+ // To compute the inflated bounds for a layer, we start by taking its bounds
+ // and converting it to a 3d box, and then we transform or inflate it
+ // repeatedly as we walk up the layer tree to the root.
+ //
+ // At each layer we apply the following transformations to the box:
+ // 1) We translate so that the anchor point is the origin.
+ // 2) We either apply the layer's transform or inflate if the layer's
+ // transform is animated.
+ // 3) We undo the translation from step 1 and apply a second translation
+ // to account for the layer's position.
+ // 4) We apply the sublayer transform from our parent (about the parent's
+ // anchor point).
+ //
+ gfx::BoxF box(layer_in.bounds().width(), layer_in.bounds().height(), 0.f);
+
+ // We want to inflate/transform the box as few times as possible. Each time
+ // we do this, we have to make the box axis aligned again, so if we make many
+ // small adjustments to the box by transforming it repeatedly rather than
+ // once by the product of all these matrices, we will accumulate a bunch of
+ // unnecessary inflation because of the the many axis-alignment fixes. This
+ // matrix stores said product.
+ gfx::Transform coalesced_transform;
+
+ for (const LayerImpl* layer = &layer_in; layer; layer = layer->parent()) {
+ int anchor_x = layer->anchor_point().x() * layer->bounds().width();
+ int anchor_y = layer->anchor_point().y() * layer->bounds().height();
+ gfx::PointF position = layer->position();
+ if (layer->parent() && !HasAnimationThatInflatesBounds(*layer)) {
+ // |composite_layer_transform| contains 1 - 4 mentioned above. We compute
+ // it separately and apply afterwards because it's a bit more efficient
+ // because post-multiplication appears a bit more expensive, so we want
+ // to do it only once.
+ gfx::Transform composite_layer_transform;
+
+ if (!layer->parent()->sublayer_transform().IsIdentity()) {
+ LayerTreeHostCommon::ApplySublayerTransformAboutAnchor(
+ *layer->parent(),
+ layer->parent()->bounds(),
+ &composite_layer_transform);
+ }
+
+ composite_layer_transform.Translate3d(anchor_x + position.x(),
+ anchor_y + position.y(),
+ layer->anchor_point_z());
+ composite_layer_transform.PreconcatTransform(layer->transform());
+ composite_layer_transform.Translate3d(
+ -anchor_x, -anchor_y, -layer->anchor_point_z());
+
+ // Add this layer's contributions to the |coalesced_transform|.
+ coalesced_transform.ConcatTransform(composite_layer_transform);
+ continue;
+ }
+
+ // First, apply coalesced transform we've been building and reset it.
+ coalesced_transform.TransformBox(&box);
+ coalesced_transform.MakeIdentity();
+
+ // We need to apply the inflation about the layer's anchor point. Rather
+ // than doing this via transforms, we'll just shift the box directly.
+ box.set_origin(box.origin() + gfx::Vector3dF(-anchor_x,
+ -anchor_y,
+ -layer->anchor_point_z()));
+
+ // Perform the inflation
+ if (HasFilterAnimationThatInflatesBounds(*layer)) {
+ gfx::BoxF inflated;
+ if (!layer->layer_animation_controller()->FilterAnimationBoundsForBox(
+ box, &inflated))
+ return false;
+ box = inflated;
+ }
+
+ if (HasTransformAnimationThatInflatesBounds(*layer)) {
+ gfx::BoxF inflated;
+ if (!layer->layer_animation_controller()->TransformAnimationBoundsForBox(
+ box, &inflated))
+ return false;
+ box = inflated;
+ }
+
+ // Apply step 3) mentioned above.
+ box.set_origin(box.origin() + gfx::Vector3dF(anchor_x + position.x(),
+ anchor_y + position.y(),
+ layer->anchor_point_z()));
+
+ // Even for layers with animations, we have to tack in the sublayer
+ // transform of our parent. *Every* layer is repsonsible for including the
+ // sublayer transform of its parent (see step 4 above).
+ if (layer->parent()) {
+ LayerTreeHostCommon::ApplySublayerTransformAboutAnchor(
+ *layer->parent(), layer->parent()->bounds(), &coalesced_transform);
+ }
+ }
+
+ // If we've got an unapplied coalesced transform at this point, it must still
+ // be applied.
+ if (!coalesced_transform.IsIdentity())
+ coalesced_transform.TransformBox(&box);
+
+ *out = box;
+
+ return true;
+}
+
+} // namespace cc
diff --git a/cc/layers/layer_utils.h b/cc/layers/layer_utils.h
new file mode 100644
index 0000000..396f070
--- /dev/null
+++ b/cc/layers/layer_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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_LAYERS_LAYER_UTILS_H_
+#define CC_LAYERS_LAYER_UTILS_H_
+
+#include "cc/base/cc_export.h"
+
+namespace gfx {
+ class BoxF;
+} // namespace gfx
+
+namespace cc {
+ class LayerImpl;
+
+ class CC_EXPORT LayerUtils {
+ public:
+ // Computes a box in screen space that should entirely contain the layer's
+ // bounds through the entirety of the layer's current animation. Returns
+ // true and sets |out| to the inflation if there are animations that can
+ // inflate bounds in the path to the root layer and that it was able to
+ // inflate correctly. Returns false otherwise.
+ static bool GetAnimationBounds(const LayerImpl& layer, gfx::BoxF* out);
+ };
+
+} // namespace cc
+
+#endif // CC_LAYERS_LAYER_UTILS_H_
diff --git a/cc/layers/layer_utils_unittest.cc b/cc/layers/layer_utils_unittest.cc
new file mode 100644
index 0000000..9738f60
--- /dev/null
+++ b/cc/layers/layer_utils_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright 2014 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/layers/layer_utils.h"
+
+#include "cc/animation/transform_operations.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
+#include "ui/gfx/test/gfx_util.h"
+
+namespace cc {
+namespace {
+
+float diagonal(float width, float height) {
+ return std::sqrt(width * width + height * height);
+}
+
+class LayerUtilsGetAnimationBoundsTest : public testing::Test {
+ public:
+ LayerUtilsGetAnimationBoundsTest()
+ : host_impl_(&proxy_),
+ root_(CreateThreeNodeTree(host_impl_)),
+ parent_(root_->children()[0]),
+ child_(parent_->children()[0]) {}
+
+ LayerImpl* root() { return root_.get(); }
+ LayerImpl* parent() { return parent_; }
+ LayerImpl* child() { return child_; }
+
+ private:
+ static scoped_ptr<LayerImpl> CreateThreeNodeTree(
+ LayerTreeHostImpl& host_impl) {
+ scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
+ root->AddChild(LayerImpl::Create(host_impl.active_tree(), 2));
+ root->children()[0]
+ ->AddChild(LayerImpl::Create(host_impl.active_tree(), 3));
+ return root.Pass();
+ }
+
+ FakeImplProxy proxy_;
+ FakeLayerTreeHostImpl host_impl_;
+ scoped_ptr<LayerImpl> root_;
+ LayerImpl* parent_;
+ LayerImpl* child_;
+};
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, ScaleRoot) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendScale(1.f, 1.f, 1.f);
+ TransformOperations end;
+ end.AppendScale(2.f, 2.f, 1.f);
+ AddAnimatedTransformToLayer(root(), duration, start, end);
+
+ root()->SetPosition(gfx::PointF());
+ parent()->SetPosition(gfx::PointF());
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(gfx::Size(100, 200));
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(150.f, 50.f, 0.f, 350.f, 450.f, 0.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, TranslateParentLayer) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendTranslate(0.f, 0.f, 0.f);
+ TransformOperations end;
+ end.AppendTranslate(50.f, 50.f, 0.f);
+ AddAnimatedTransformToLayer(parent(), duration, start, end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(gfx::Size(100, 200));
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(150.f, 50.f, 0.f, 150.f, 250.f, 0.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, TranslateChildLayer) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendTranslate(0.f, 0.f, 0.f);
+ TransformOperations end;
+ end.AppendTranslate(50.f, 50.f, 0.f);
+ AddAnimatedTransformToLayer(child(), duration, start, end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(gfx::Size(100, 200));
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(150.f, 50.f, 0.f, 150.f, 250.f, 0.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, TranslateBothLayers) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendTranslate(0.f, 0.f, 0.f);
+ TransformOperations child_end;
+ child_end.AppendTranslate(50.f, 0.f, 0.f);
+ AddAnimatedTransformToLayer(parent(), duration, start, child_end);
+
+ TransformOperations grand_child_end;
+ grand_child_end.AppendTranslate(0.f, 50.f, 0.f);
+ AddAnimatedTransformToLayer(child(), duration, start, grand_child_end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(gfx::Size(100, 200));
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(150.f, 50.f, 0.f, 150.f, 250.f, 0.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, RotateXNoPerspective) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendRotate(1.f, 0.f, 0.f, 0.f);
+ TransformOperations end;
+ end.AppendRotate(1.f, 0.f, 0.f, 90.f);
+ AddAnimatedTransformToLayer(child(), duration, start, end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ gfx::Size bounds(100, 100);
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(bounds);
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(150.f, 50.f, -50.f, 100.f, 100.f, 100.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, RotateXWithPerspective) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendRotate(1.f, 0.f, 0.f, 0.f);
+ TransformOperations end;
+ end.AppendRotate(1.f, 0.f, 0.f, 90.f);
+ AddAnimatedTransformToLayer(child(), duration, start, end);
+
+ // Make the anchor point not the default 0.5 value and line up with the
+ // child center to make the math easier.
+ parent()->SetAnchorPoint(gfx::PointF(0.375f, 0.375f));
+ parent()->SetBounds(gfx::Size(400, 400));
+
+ gfx::Transform perspective;
+ perspective.ApplyPerspectiveDepth(100.f);
+ parent()->SetSublayerTransform(perspective);
+
+ gfx::Size bounds(100, 100);
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(100.f, 100.f));
+ child()->SetBounds(bounds);
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ gfx::BoxF expected(50.f, 50.f, -33.333336f, 200.f, 200.f, 133.333344f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, RotateZ) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendRotate(0.f, 0.f, 1.f, 0.f);
+ TransformOperations end;
+ end.AppendRotate(0.f, 0.f, 1.f, 90.f);
+ AddAnimatedTransformToLayer(child(), duration, start, end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ gfx::Size bounds(100, 100);
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(bounds);
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_TRUE(success);
+ float diag = diagonal(bounds.width(), bounds.height());
+ gfx::BoxF expected(150.f + 0.5f * (bounds.width() - diag),
+ 50.f + 0.5f * (bounds.height() - diag),
+ 0.f,
+ diag,
+ diag,
+ 0.f);
+ EXPECT_BOXF_EQ(expected, box);
+}
+
+TEST_F(LayerUtilsGetAnimationBoundsTest, MismatchedTransforms) {
+ double duration = 1.0;
+
+ TransformOperations start;
+ start.AppendTranslate(5, 6, 7);
+ TransformOperations end;
+ end.AppendRotate(0.f, 0.f, 1.f, 90.f);
+ AddAnimatedTransformToLayer(child(), duration, start, end);
+
+ parent()->SetBounds(gfx::Size(350, 200));
+
+ gfx::Size bounds(100, 100);
+ child()->SetDrawsContent(true);
+ child()->draw_properties().screen_space_transform_is_animating = true;
+ child()->SetPosition(gfx::PointF(150.f, 50.f));
+ child()->SetBounds(bounds);
+
+ gfx::BoxF box;
+ bool success = LayerUtils::GetAnimationBounds(*child(), &box);
+ EXPECT_FALSE(success);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index a9a674f..712b06c 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -55,20 +55,16 @@ int AddOpacityTransition(Target* target,
template <class Target>
int AddAnimatedTransform(Target* target,
double duration,
- int delta_x,
- int delta_y) {
+ TransformOperations start_operations,
+ TransformOperations operations) {
scoped_ptr<KeyframedTransformAnimationCurve>
curve(KeyframedTransformAnimationCurve::Create());
if (duration > 0.0) {
- TransformOperations start_operations;
- start_operations.AppendTranslate(delta_x, delta_y, 0.0);
curve->AddKeyframe(TransformKeyframe::Create(
0.0, start_operations, scoped_ptr<TimingFunction>()));
}
- TransformOperations operations;
- operations.AppendTranslate(delta_x, delta_y, 0.0);
curve->AddKeyframe(TransformKeyframe::Create(
duration, operations, scoped_ptr<TimingFunction>()));
@@ -86,6 +82,21 @@ int AddAnimatedTransform(Target* target,
}
template <class Target>
+int AddAnimatedTransform(Target* target,
+ double duration,
+ int delta_x,
+ int delta_y) {
+ TransformOperations start_operations;
+ if (duration > 0.0) {
+ start_operations.AppendTranslate(delta_x, delta_y, 0.0);
+ }
+
+ TransformOperations operations;
+ operations.AppendTranslate(delta_x, delta_y, 0.0);
+ return AddAnimatedTransform(target, duration, start_operations, operations);
+}
+
+template <class Target>
int AddAnimatedFilter(Target* target,
double duration,
float start_brightness,
@@ -297,6 +308,16 @@ int AddAnimatedTransformToLayer(LayerImpl* layer,
delta_y);
}
+int AddAnimatedTransformToLayer(LayerImpl* layer,
+ double duration,
+ TransformOperations start_operations,
+ TransformOperations operations) {
+ return AddAnimatedTransform(layer->layer_animation_controller(),
+ duration,
+ start_operations,
+ operations);
+}
+
int AddAnimatedFilterToLayer(Layer* layer,
double duration,
float start_brightness,
diff --git a/cc/test/animation_test_common.h b/cc/test/animation_test_common.h
index e23be0b..bb57339 100644
--- a/cc/test/animation_test_common.h
+++ b/cc/test/animation_test_common.h
@@ -153,6 +153,11 @@ int AddAnimatedTransformToLayer(LayerImpl* layer,
int delta_x,
int delta_y);
+int AddAnimatedTransformToLayer(LayerImpl* layer,
+ double duration,
+ TransformOperations start_operations,
+ TransformOperations operations);
+
int AddAnimatedFilterToLayer(Layer* layer,
double duration,
float start_brightness,
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h
index d639afd..cd30b93 100644
--- a/cc/trees/layer_tree_host_common.h
+++ b/cc/trees/layer_tree_host_common.h
@@ -122,6 +122,21 @@ class CC_EXPORT LayerTreeHostCommon {
template <typename LayerType>
static LayerType* FindLayerInSubtree(LayerType* root_layer, int layer_id);
+ // Applies the layer's sublayer transform about its anchor point to the
+ // given transform.
+ template <typename LayerType>
+ static void ApplySublayerTransformAboutAnchor(const LayerType& layer,
+ gfx::Size bounds,
+ gfx::Transform* transform) {
+ if (!layer.sublayer_transform().IsIdentity()) {
+ transform->Translate(layer.anchor_point().x() * bounds.width(),
+ layer.anchor_point().y() * bounds.height());
+ transform->PreconcatTransform(layer.sublayer_transform());
+ transform->Translate(-layer.anchor_point().x() * bounds.width(),
+ -layer.anchor_point().y() * bounds.height());
+ }
+ }
+
static Layer* get_child_as_raw_ptr(
const LayerList& children,
size_t index) {