diff options
author | avallee@chromium.org <avallee@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-24 01:33:11 +0000 |
---|---|---|
committer | avallee@chromium.org <avallee@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-24 01:33:11 +0000 |
commit | 3d86dd7a4ebd896a46f4c07cd7a2fbb6df726217 (patch) | |
tree | 54751d98283fefa47e68978a6a0218e1966b53b5 /cc/layers | |
parent | 938023f551e85235e39ec7a9ebbb4cd94764d9f0 (diff) | |
download | chromium_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/layers')
-rw-r--r-- | cc/layers/layer.h | 4 | ||||
-rw-r--r-- | cc/layers/layer_impl.cc | 16 | ||||
-rw-r--r-- | cc/layers/layer_impl.h | 10 | ||||
-rw-r--r-- | cc/layers/layer_utils.cc | 160 | ||||
-rw-r--r-- | cc/layers/layer_utils.h | 29 | ||||
-rw-r--r-- | cc/layers/layer_utils_unittest.cc | 258 |
6 files changed, 460 insertions, 17 deletions
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 |