diff options
-rw-r--r-- | cc/animation/animation_curve.h | 1 | ||||
-rw-r--r-- | cc/animation/keyframed_animation_curve.cc | 9 | ||||
-rw-r--r-- | cc/animation/keyframed_animation_curve.h | 1 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.cc | 34 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller.h | 12 | ||||
-rw-r--r-- | cc/animation/layer_animation_controller_unittest.cc | 11 | ||||
-rw-r--r-- | cc/cc.gyp | 2 | ||||
-rw-r--r-- | cc/cc_tests.gyp | 1 | ||||
-rw-r--r-- | cc/debug/debug_rect_history.cc | 6 | ||||
-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 | ||||
-rw-r--r-- | cc/test/animation_test_common.cc | 33 | ||||
-rw-r--r-- | cc/test/animation_test_common.h | 5 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_common.h | 15 |
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 @@ -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) { |