| // Copyright 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/trees/property_tree_builder.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "cc/animation/filter_animation_curve.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/layer_impl.h" |
| #include "cc/layers/picture_layer.h" |
| #include "cc/layers/render_surface_impl.h" |
| #include "cc/layers/texture_layer.h" |
| #include "cc/test/animation_test_common.h" |
| #include "cc/test/fake_content_layer_client.h" |
| #include "cc/test/layer_tree_impl_test_base.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/animation/keyframe/keyframed_animation_curve.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/geometry/test/geometry_util.h" |
| #include "ui/gfx/geometry/transform.h" |
| #include "ui/gfx/geometry/vector2d_conversions.h" |
| |
| namespace cc { |
| namespace { |
| |
| class PropertyTreeBuilderTest : public LayerTreeImplTestBase, |
| public testing::Test { |
| public: |
| PropertyTreeBuilderTest() : LayerTreeImplTestBase(LayerTreeSettings()) {} |
| |
| void UpdateMainDrawProperties(float device_scale_factor = 1.0f) { |
| SetDeviceScaleAndUpdateViewportRect(host(), device_scale_factor); |
| UpdateDrawProperties(host()); |
| } |
| |
| LayerImpl* ImplOf(const scoped_refptr<Layer>& layer) { |
| return layer ? host_impl()->active_tree()->LayerById(layer->id()) : nullptr; |
| } |
| RenderSurfaceImpl* GetRenderSurfaceImpl(const scoped_refptr<Layer>& layer) { |
| return GetRenderSurface(ImplOf(layer)); |
| } |
| LayerImpl* GetLayerImpl(const scoped_refptr<Layer>& layer) { |
| return host()->host_impl()->active_tree()->LayerById(layer->id()); |
| } |
| |
| // Updates main thread draw properties, commits main thread tree to |
| // impl-side pending tree, and updates pending tree draw properties. |
| void Commit(float device_scale_factor = 1.0f) { |
| UpdateMainDrawProperties(device_scale_factor); |
| if (!host_impl()->pending_tree()) |
| host_impl()->CreatePendingTree(); |
| host()->CommitAndCreatePendingTree(); |
| // TODO(https://crbug.com/939968) This call should be handled by |
| // FakeLayerTreeHost instead of manually pushing the properties from the |
| // layer tree host to the pending tree. |
| host_impl()->pending_tree()->PullLayerTreePropertiesFrom( |
| *host()->GetPendingCommitState()); |
| UpdateDrawProperties(host_impl()->pending_tree()); |
| } |
| |
| // Calls Commit(), then activates the pending tree, and updates active tree |
| // draw properties. |
| void CommitAndActivate(float device_scale_factor = 1.0f) { |
| Commit(device_scale_factor); |
| host_impl()->ActivateSyncTree(); |
| DCHECK_EQ(device_scale_factor, |
| host_impl()->active_tree()->device_scale_factor()); |
| UpdateActiveTreeDrawProperties(device_scale_factor); |
| } |
| |
| const RenderSurfaceList& GetRenderSurfaceList() { |
| return host_impl()->active_tree()->GetRenderSurfaceList(); |
| } |
| }; |
| |
| TEST_F(PropertyTreeBuilderTest, EffectTreeTransformIdTest) { |
| // Tests that effect tree node gets a valid transform id when a layer |
| // has opacity but doesn't create a render surface. |
| auto parent = Layer::Create(); |
| host()->SetRootLayer(parent); |
| auto child = Layer::Create(); |
| parent->AddChild(child); |
| child->SetIsDrawable(true); |
| |
| parent->SetBounds(gfx::Size(100, 100)); |
| child->SetPosition(gfx::PointF(10, 10)); |
| child->SetBounds(gfx::Size(100, 100)); |
| child->SetOpacity(0.f); |
| UpdateMainDrawProperties(); |
| EffectNode* node = GetEffectNode(child.get()); |
| const int transform_tree_size = |
| GetPropertyTrees(parent.get())->transform_tree().next_available_id(); |
| EXPECT_LT(node->transform_id, transform_tree_size); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RenderSurfaceForNonAxisAlignedClipping) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto rotated_and_transparent = Layer::Create(); |
| root->AddChild(rotated_and_transparent); |
| auto clips_subtree = Layer::Create(); |
| rotated_and_transparent->AddChild(clips_subtree); |
| auto draws_content = Layer::Create(); |
| clips_subtree->AddChild(draws_content); |
| |
| root->SetBounds(gfx::Size(10, 10)); |
| rotated_and_transparent->SetBounds(gfx::Size(10, 10)); |
| rotated_and_transparent->SetOpacity(0.5f); |
| gfx::Transform rotate; |
| rotate.Rotate(2); |
| rotated_and_transparent->SetTransform(rotate); |
| clips_subtree->SetBounds(gfx::Size(10, 10)); |
| clips_subtree->SetMasksToBounds(true); |
| draws_content->SetBounds(gfx::Size(10, 10)); |
| draws_content->SetIsDrawable(true); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_TRUE(GetEffectNode(clips_subtree.get())->HasRenderSurface()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, EffectNodesForNonAxisAlignedClips) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto rotate_and_clip = Layer::Create(); |
| root->AddChild(rotate_and_clip); |
| auto only_clip = Layer::Create(); |
| rotate_and_clip->AddChild(only_clip); |
| auto rotate_and_clip2 = Layer::Create(); |
| only_clip->AddChild(rotate_and_clip2); |
| |
| gfx::Transform rotate; |
| rotate.Rotate(2); |
| root->SetBounds(gfx::Size(10, 10)); |
| rotate_and_clip->SetBounds(gfx::Size(10, 10)); |
| rotate_and_clip->SetTransform(rotate); |
| rotate_and_clip->SetMasksToBounds(true); |
| only_clip->SetBounds(gfx::Size(10, 10)); |
| only_clip->SetMasksToBounds(true); |
| rotate_and_clip2->SetBounds(gfx::Size(10, 10)); |
| rotate_and_clip2->SetTransform(rotate); |
| rotate_and_clip2->SetMasksToBounds(true); |
| |
| UpdateMainDrawProperties(); |
| // non-axis aligned clip should create an effect node |
| EXPECT_NE(root->effect_tree_index(), rotate_and_clip->effect_tree_index()); |
| // Since only_clip's clip is in the same non-axis aligned space as |
| // rotate_and_clip's clip, no new effect node should be created. |
| EXPECT_EQ(rotate_and_clip->effect_tree_index(), |
| only_clip->effect_tree_index()); |
| // rotate_and_clip2's clip and only_clip's clip are in different non-axis |
| // aligned spaces. So, new effect node should be created. |
| EXPECT_NE(rotate_and_clip2->effect_tree_index(), |
| only_clip->effect_tree_index()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, |
| RenderSurfaceListForRenderSurfaceWithClippedLayer) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto render_surface1 = Layer::Create(); |
| root->AddChild(render_surface1); |
| auto child = Layer::Create(); |
| render_surface1->AddChild(child); |
| |
| root->SetBounds(gfx::Size(10, 10)); |
| root->SetMasksToBounds(true); |
| render_surface1->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetForceRenderSurfaceForTesting(true); |
| child->SetIsDrawable(true); |
| child->SetPosition(gfx::PointF(30.f, 30.f)); |
| child->SetBounds(gfx::Size(10, 10)); |
| |
| CommitAndActivate(); |
| |
| // The child layer's content is entirely outside the root's clip rect, so |
| // the intermediate render surface should not be listed here, even if it was |
| // forced to be created. Render surfaces without children or visible content |
| // are unexpected at draw time (e.g. we might try to create a content texture |
| // of size 0). |
| ASSERT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(1U, GetRenderSurfaceList().size()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto render_surface1 = Layer::Create(); |
| root->AddChild(render_surface1); |
| auto child = Layer::Create(); |
| render_surface1->AddChild(child); |
| |
| root->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetForceRenderSurfaceForTesting(true); |
| render_surface1->SetOpacity(0.f); |
| child->SetBounds(gfx::Size(10, 10)); |
| child->SetIsDrawable(true); |
| |
| CommitAndActivate(); |
| |
| // Since the layer is transparent, render_surface1_impl->GetRenderSurface() |
| // should not have gotten added anywhere. Also, the drawable content rect |
| // should not have been extended by the children. |
| ASSERT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(0, GetRenderSurfaceImpl(root)->num_contributors()); |
| EXPECT_EQ(1U, GetRenderSurfaceList().size()); |
| EXPECT_EQ(static_cast<viz::CompositorRenderPassId>(root->id()), |
| GetRenderSurfaceList().at(0)->render_pass_id()); |
| EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_drawable_content_rect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, |
| RenderSurfaceListForTransparentChildWithBackdropFilter) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto render_surface1 = Layer::Create(); |
| root->AddChild(render_surface1); |
| auto child = Layer::Create(); |
| render_surface1->AddChild(child); |
| |
| root->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetForceRenderSurfaceForTesting(true); |
| render_surface1->SetOpacity(0.f); |
| render_surface1->SetIsDrawable(true); |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateBlurFilter(1.5f)); |
| render_surface1->SetBackdropFilters(filters); |
| child->SetBounds(gfx::Size(10, 10)); |
| child->SetIsDrawable(true); |
| host()->SetElementIdsForTesting(); |
| |
| CommitAndActivate(); |
| EXPECT_EQ(2U, GetRenderSurfaceList().size()); |
| |
| // The layer is fully transparent, but has a backdrop filter, so it |
| // shouldn't be skipped and should be drawn. |
| ASSERT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(1, GetRenderSurfaceImpl(root)->num_contributors()); |
| EXPECT_EQ(gfx::RectF(0, 0, 10, 10), |
| GetRenderSurfaceImpl(root)->DrawableContentRect()); |
| EXPECT_TRUE(GetEffectNode(ImplOf(render_surface1))->is_drawn); |
| |
| // When root is transparent, the layer should not be drawn. |
| host_impl()->active_tree()->SetOpacityMutated(root->element_id(), 0.f); |
| host_impl()->active_tree()->SetOpacityMutated(render_surface1->element_id(), |
| 1.f); |
| ImplOf(render_surface1)->set_visible_layer_rect(gfx::Rect()); |
| UpdateActiveTreeDrawProperties(); |
| |
| EXPECT_FALSE(GetEffectNode(ImplOf(render_surface1))->is_drawn); |
| EXPECT_EQ(gfx::Rect(), ImplOf(render_surface1)->visible_layer_rect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForFilter) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto parent = Layer::Create(); |
| root->AddChild(parent); |
| auto child1 = Layer::Create(); |
| parent->AddChild(child1); |
| auto child2 = Layer::Create(); |
| parent->AddChild(child2); |
| |
| gfx::Transform scale_matrix; |
| scale_matrix.Scale(2.0f, 2.0f); |
| |
| root->SetBounds(gfx::Size(100, 100)); |
| parent->SetTransform(scale_matrix); |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateBlurFilter(10.0f)); |
| parent->SetFilters(filters); |
| parent->SetForceRenderSurfaceForTesting(true); |
| child1->SetBounds(gfx::Size(25, 25)); |
| child1->SetIsDrawable(true); |
| child1->SetForceRenderSurfaceForTesting(true); |
| child2->SetPosition(gfx::PointF(25, 25)); |
| child2->SetBounds(gfx::Size(25, 25)); |
| child2->SetIsDrawable(true); |
| child2->SetForceRenderSurfaceForTesting(true); |
| |
| CommitAndActivate(); |
| |
| ASSERT_TRUE(GetRenderSurfaceImpl(parent)); |
| EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors()); |
| EXPECT_EQ(4U, GetRenderSurfaceList().size()); |
| |
| // The rectangle enclosing child1 and child2 (0,0 50x50), expanded for the |
| // blur (-30,-30 110x110), and then scaled by the scale matrix |
| // (-60,-60 220x220). |
| EXPECT_EQ(gfx::RectF(-60, -60, 220, 220), |
| GetRenderSurfaceImpl(parent)->DrawableContentRect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, ForceRenderSurface) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto render_surface1 = Layer::Create(); |
| root->AddChild(render_surface1); |
| auto child = Layer::Create(); |
| render_surface1->AddChild(child); |
| |
| root->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetBounds(gfx::Size(10, 10)); |
| render_surface1->SetForceRenderSurfaceForTesting(true); |
| child->SetBounds(gfx::Size(10, 10)); |
| child->SetIsDrawable(true); |
| |
| CommitAndActivate(); |
| |
| // The root layer always creates a render surface |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_NE(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root)); |
| |
| render_surface1->SetForceRenderSurfaceForTesting(false); |
| CommitAndActivate(); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root)); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, VisibleRectWithClippingAndFilters) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto clip = Layer::Create(); |
| root->AddChild(clip); |
| auto filter = Layer::Create(); |
| clip->AddChild(filter); |
| auto filter_child = Layer::Create(); |
| filter->AddChild(filter_child); |
| |
| root->SetBounds(gfx::Size(100, 100)); |
| clip->SetBounds(gfx::Size(10, 10)); |
| filter->SetForceRenderSurfaceForTesting(true); |
| filter_child->SetBounds(gfx::Size(2000, 2000)); |
| filter_child->SetPosition(gfx::PointF(-50, -50)); |
| filter_child->SetIsDrawable(true); |
| |
| clip->SetMasksToBounds(true); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(50, 50, 10, 10), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(10, 10), GetRenderSurfaceImpl(filter)->content_rect()); |
| |
| FilterOperations blur_filter; |
| blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); |
| filter->SetFilters(blur_filter); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(38, 38, 34, 34), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(-12, -12, 34, 34), |
| GetRenderSurfaceImpl(filter)->content_rect()); |
| |
| gfx::Transform vertical_flip; |
| vertical_flip.Scale(1, -1); |
| sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( |
| vertical_flip.matrix().asM33(), PaintFlags::FilterQuality::kLow, nullptr); |
| FilterOperations reflection_filter; |
| reflection_filter.Append( |
| FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( |
| SkBlendMode::kSrcOver, std::move(flip_filter), nullptr))); |
| filter->SetFilters(reflection_filter); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(50, 40, 10, 20), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(0, -10, 10, 20), |
| GetRenderSurfaceImpl(filter)->content_rect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, VisibleRectWithScalingClippingAndFilters) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto scale = Layer::Create(); |
| root->AddChild(scale); |
| auto clip = Layer::Create(); |
| scale->AddChild(clip); |
| auto filter = Layer::Create(); |
| clip->AddChild(filter); |
| auto filter_child = Layer::Create(); |
| filter->AddChild(filter_child); |
| |
| root->SetBounds(gfx::Size(100, 100)); |
| clip->SetBounds(gfx::Size(10, 10)); |
| filter->SetForceRenderSurfaceForTesting(true); |
| filter_child->SetBounds(gfx::Size(2000, 2000)); |
| filter_child->SetPosition(gfx::PointF(-50, -50)); |
| filter_child->SetIsDrawable(true); |
| |
| clip->SetMasksToBounds(true); |
| |
| gfx::Transform scale_transform; |
| scale_transform.Scale(3, 3); |
| scale->SetTransform(scale_transform); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(50, 50, 10, 10), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(30, 30), GetRenderSurfaceImpl(filter)->content_rect()); |
| |
| FilterOperations blur_filter; |
| blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); |
| filter->SetFilters(blur_filter); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(38, 38, 34, 34), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(-36, -36, 102, 102), |
| GetRenderSurfaceImpl(filter)->content_rect()); |
| |
| gfx::Transform vertical_flip; |
| vertical_flip.Scale(1, -1); |
| sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( |
| vertical_flip.matrix().asM33(), PaintFlags::FilterQuality::kLow, nullptr); |
| FilterOperations reflection_filter; |
| reflection_filter.Append( |
| FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( |
| SkBlendMode::kSrcOver, std::move(flip_filter), nullptr))); |
| filter->SetFilters(reflection_filter); |
| |
| CommitAndActivate(); |
| |
| EXPECT_EQ(gfx::Rect(50, 40, 10, 20), |
| ImplOf(filter_child)->visible_layer_rect()); |
| EXPECT_EQ(gfx::Rect(0, -30, 30, 60), |
| GetRenderSurfaceImpl(filter)->content_rect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, TextureLayerSnapping) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto child = TextureLayer::CreateForMailbox(nullptr); |
| root->AddChild(child); |
| |
| root->SetBounds(gfx::Size(100, 100)); |
| child->SetBounds(gfx::Size(100, 100)); |
| child->SetIsDrawable(true); |
| gfx::Transform fractional_translate; |
| fractional_translate.Translate(10.5f, 20.3f); |
| child->SetTransform(fractional_translate); |
| |
| CommitAndActivate(); |
| |
| auto child_screen_space_transform = ImplOf(child)->ScreenSpaceTransform(); |
| EXPECT_NE(child_screen_space_transform, fractional_translate); |
| fractional_translate.RoundTranslationComponents(); |
| EXPECT_TRANSFORM_EQ(child_screen_space_transform, fractional_translate); |
| gfx::RectF layer_bounds_in_screen_space = MathUtil::MapClippedRect( |
| child_screen_space_transform, gfx::RectF(gfx::SizeF(child->bounds()))); |
| EXPECT_EQ(layer_bounds_in_screen_space, gfx::RectF(11.f, 20.f, 100.f, 100.f)); |
| } |
| |
| // Verify that having animated opacity but current opacity 1 still creates |
| // a render surface. |
| TEST_F(PropertyTreeBuilderTest, AnimatedOpacityCreatesRenderSurface) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto child = Layer::Create(); |
| root->AddChild(child); |
| auto grandchild = Layer::Create(); |
| child->AddChild(grandchild); |
| |
| root->SetBounds(gfx::Size(50, 50)); |
| child->SetBounds(gfx::Size(50, 50)); |
| child->SetIsDrawable(true); |
| grandchild->SetBounds(gfx::Size(50, 50)); |
| grandchild->SetIsDrawable(true); |
| |
| host()->SetElementIdsForTesting(); |
| AddOpacityTransitionToElementWithAnimation(child->element_id(), timeline(), |
| 10.0, 1.f, 0.2f, false); |
| CommitAndActivate(); |
| |
| EXPECT_EQ(1.f, ImplOf(child)->Opacity()); |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); |
| } |
| |
| static bool FilterIsAnimating(LayerImpl* layer) { |
| MutatorHost* host = layer->layer_tree_impl()->mutator_host(); |
| return host->IsAnimatingProperty(layer->element_id(), |
| layer->GetElementTypeForAnimation(), |
| TargetProperty::FILTER); |
| } |
| |
| // Verify that having an animated filter (but no current filter, as these |
| // are mutually exclusive) correctly creates a render surface. |
| TEST_F(PropertyTreeBuilderTest, AnimatedFilterCreatesRenderSurface) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto child = Layer::Create(); |
| root->AddChild(child); |
| auto grandchild = Layer::Create(); |
| child->AddChild(grandchild); |
| |
| root->SetBounds(gfx::Size(50, 50)); |
| child->SetBounds(gfx::Size(50, 50)); |
| grandchild->SetBounds(gfx::Size(50, 50)); |
| |
| host()->SetElementIdsForTesting(); |
| AddAnimatedFilterToElementWithAnimation(child->element_id(), timeline(), 10.0, |
| 0.1f, 0.2f); |
| CommitAndActivate(); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty()); |
| |
| EXPECT_FALSE(FilterIsAnimating(ImplOf(root))); |
| EXPECT_TRUE(FilterIsAnimating(ImplOf(child))); |
| EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild))); |
| } |
| |
| bool HasPotentiallyRunningFilterAnimation(const LayerImpl& layer) { |
| MutatorHost* host = layer.layer_tree_impl()->mutator_host(); |
| return host->HasPotentiallyRunningAnimationForProperty( |
| layer.element_id(), layer.GetElementTypeForAnimation(), |
| TargetProperty::FILTER); |
| } |
| |
| // Verify that having a filter animation with a delayed start time creates a |
| // render surface. |
| TEST_F(PropertyTreeBuilderTest, DelayedFilterAnimationCreatesRenderSurface) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto child = Layer::Create(); |
| root->AddChild(child); |
| auto grandchild = Layer::Create(); |
| child->AddChild(grandchild); |
| |
| root->SetBounds(gfx::Size(50, 50)); |
| child->SetBounds(gfx::Size(50, 50)); |
| grandchild->SetBounds(gfx::Size(50, 50)); |
| |
| host()->SetElementIdsForTesting(); |
| |
| std::unique_ptr<KeyframedFilterAnimationCurve> curve( |
| KeyframedFilterAnimationCurve::Create()); |
| FilterOperations start_filters; |
| start_filters.Append(FilterOperation::CreateBrightnessFilter(0.1f)); |
| FilterOperations end_filters; |
| end_filters.Append(FilterOperation::CreateBrightnessFilter(0.3f)); |
| curve->AddKeyframe( |
| FilterKeyframe::Create(base::TimeDelta(), start_filters, nullptr)); |
| curve->AddKeyframe( |
| FilterKeyframe::Create(base::Milliseconds(100), end_filters, nullptr)); |
| std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( |
| std::move(curve), 0, 1, |
| KeyframeModel::TargetPropertyId(TargetProperty::FILTER)); |
| keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); |
| keyframe_model->set_time_offset(base::Milliseconds(-1000)); |
| |
| AddKeyframeModelToElementWithAnimation(child->element_id(), timeline(), |
| std::move(keyframe_model)); |
| CommitAndActivate(); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty()); |
| EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty()); |
| |
| EXPECT_FALSE(FilterIsAnimating(ImplOf(root))); |
| EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(root))); |
| EXPECT_FALSE(FilterIsAnimating(ImplOf(child))); |
| EXPECT_TRUE(HasPotentiallyRunningFilterAnimation(*ImplOf(child))); |
| EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild))); |
| EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(grandchild))); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, ChangingAxisAlignmentTriggersRebuild) { |
| gfx::Transform translate; |
| gfx::Transform rotate; |
| |
| translate.Translate(10, 10); |
| rotate.Rotate(45); |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(800, 800)); |
| |
| host()->SetRootLayer(root); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_FALSE(host()->property_trees()->needs_rebuild()); |
| |
| root->SetTransform(translate); |
| EXPECT_FALSE(host()->property_trees()->needs_rebuild()); |
| |
| root->SetTransform(rotate); |
| EXPECT_TRUE(host()->property_trees()->needs_rebuild()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, ResetPropertyTreeIndices) { |
| scoped_refptr<Layer> root = Layer::Create(); |
| root->SetBounds(gfx::Size(800, 800)); |
| |
| gfx::Transform translate_z; |
| translate_z.Translate3d(0, 0, 10); |
| |
| scoped_refptr<Layer> child = Layer::Create(); |
| child->SetTransform(translate_z); |
| child->SetBounds(gfx::Size(100, 100)); |
| |
| root->AddChild(child); |
| |
| host()->SetRootLayer(root); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_NE(-1, child->transform_tree_index()); |
| |
| child->RemoveFromParent(); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_EQ(-1, child->transform_tree_index()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RenderSurfaceClipsSubtree) { |
| // Ensure that a Clip Node is added when a render surface applies clip. |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto significant_transform = Layer::Create(); |
| root->AddChild(significant_transform); |
| auto layer_clips_subtree = Layer::Create(); |
| significant_transform->AddChild(layer_clips_subtree); |
| auto render_surface = Layer::Create(); |
| layer_clips_subtree->AddChild(render_surface); |
| auto test_layer = Layer::Create(); |
| render_surface->AddChild(test_layer); |
| |
| // This transform should be a significant one so that a transform node is |
| // formed for it. |
| gfx::Transform transform1; |
| transform1.RotateAboutYAxis(45); |
| transform1.RotateAboutXAxis(30); |
| // This transform should be a 3d transform as we want the render surface |
| // to flatten the transform |
| gfx::Transform transform2; |
| transform2.Translate3d(10, 10, 10); |
| |
| root->SetBounds(gfx::Size(30, 30)); |
| significant_transform->SetTransform(transform1); |
| significant_transform->SetBounds(gfx::Size(30, 30)); |
| layer_clips_subtree->SetBounds(gfx::Size(30, 30)); |
| layer_clips_subtree->SetMasksToBounds(true); |
| layer_clips_subtree->SetForceRenderSurfaceForTesting(true); |
| render_surface->SetTransform(transform2); |
| render_surface->SetBounds(gfx::Size(30, 30)); |
| render_surface->SetForceRenderSurfaceForTesting(true); |
| test_layer->SetBounds(gfx::Size(30, 30)); |
| test_layer->SetIsDrawable(true); |
| |
| CommitAndActivate(); |
| |
| EXPECT_TRUE(GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(significant_transform), |
| GetRenderSurfaceImpl(root)); |
| EXPECT_TRUE(GetRenderSurfaceImpl(layer_clips_subtree)); |
| EXPECT_NE(GetRenderSurfaceImpl(render_surface), GetRenderSurfaceImpl(root)); |
| EXPECT_EQ(GetRenderSurfaceImpl(test_layer), |
| GetRenderSurfaceImpl(render_surface)); |
| |
| EXPECT_EQ(gfx::Rect(30, 20), ImplOf(test_layer)->visible_layer_rect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, PropertyTreesRebuildWithOpacityChanges) { |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> child = Layer::Create(); |
| child->SetIsDrawable(true); |
| root->AddChild(child); |
| host()->SetRootLayer(root); |
| |
| root->SetBounds(gfx::Size(100, 100)); |
| child->SetBounds(gfx::Size(20, 20)); |
| UpdateMainDrawProperties(); |
| |
| // Changing the opacity from 1 to non-1 value should trigger rebuild of |
| // property trees as a new effect node will be created. |
| child->SetOpacity(0.5f); |
| PropertyTrees* property_trees = host()->property_trees(); |
| EXPECT_TRUE(property_trees->needs_rebuild()); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_NE(child->effect_tree_index(), root->effect_tree_index()); |
| |
| // child already has an effect node. Changing its opacity shouldn't trigger |
| // a property trees rebuild. |
| child->SetOpacity(0.8f); |
| property_trees = host()->property_trees(); |
| EXPECT_FALSE(property_trees->needs_rebuild()); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_NE(child->effect_tree_index(), root->effect_tree_index()); |
| |
| // Changing the opacity from non-1 value to 1 should trigger a rebuild of |
| // property trees as the effect node may no longer be needed. |
| child->SetOpacity(1.f); |
| property_trees = host()->property_trees(); |
| EXPECT_TRUE(property_trees->needs_rebuild()); |
| |
| UpdateMainDrawProperties(); |
| EXPECT_EQ(child->effect_tree_index(), root->effect_tree_index()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTrilinearFiltering) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| auto parent = Layer::Create(); |
| root->AddChild(parent); |
| auto child1 = Layer::Create(); |
| parent->AddChild(child1); |
| auto child2 = Layer::Create(); |
| parent->AddChild(child2); |
| |
| gfx::Transform scale_matrix; |
| scale_matrix.Scale(.25f, .25f); |
| |
| root->SetBounds(gfx::Size(200, 200)); |
| parent->SetTransform(scale_matrix); |
| parent->SetTrilinearFiltering(true); |
| child1->SetBounds(gfx::Size(50, 50)); |
| child1->SetIsDrawable(true); |
| child1->SetForceRenderSurfaceForTesting(true); |
| child2->SetPosition(gfx::PointF(50, 50)); |
| child2->SetBounds(gfx::Size(50, 50)); |
| child2->SetIsDrawable(true); |
| child2->SetForceRenderSurfaceForTesting(true); |
| |
| CommitAndActivate(); |
| |
| ASSERT_TRUE(GetRenderSurfaceImpl(parent)); |
| EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors()); |
| EXPECT_EQ(4U, GetRenderSurfaceList().size()); |
| |
| // The rectangle enclosing child1 and child2 (0,0 100x100), scaled by the |
| // scale matrix to (0,0 25x25). |
| EXPECT_EQ(gfx::RectF(0, 0, 25, 25), |
| GetRenderSurfaceImpl(parent)->DrawableContentRect()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, GradientMask) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| root->SetBounds(gfx::Size(200, 200)); |
| root->SetIsDrawable(true); |
| |
| auto child1 = Layer::Create(); |
| root->AddChild(child1); |
| child1->SetBounds(gfx::Size(100, 100)); |
| child1->SetIsDrawable(true); |
| |
| gfx::LinearGradient gradient_mask(45); |
| gradient_mask.AddStep(50, 0x50); |
| child1->SetGradientMask(gradient_mask); |
| |
| // Without render surface. |
| CommitAndActivate(); |
| { |
| auto* effect_node1 = GetEffectNode(child1.get()); |
| EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners()); |
| EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds()); |
| EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask()); |
| EXPECT_FALSE(effect_node1->HasRenderSurface()); |
| auto* layer_impl1 = GetLayerImpl(child1); |
| EXPECT_TRUE( |
| layer_impl1->draw_properties().mask_filter_info.HasGradientMask()); |
| } |
| |
| // Scale and translate should work. |
| gfx::Transform scale_and_translate_transform; |
| scale_and_translate_transform.Translate({10.f, 10.f}); |
| scale_and_translate_transform.Scale(3.f, 2.f); |
| child1->SetTransform(scale_and_translate_transform); |
| CommitAndActivate(); |
| { |
| // |mask_info| is in the coordinate space of the transform node associated |
| // with this effect node. |
| auto* effect_node1 = GetEffectNode(child1.get()); |
| EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners()); |
| EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds()); |
| EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask()); |
| EXPECT_FALSE(effect_node1->HasRenderSurface()); |
| |
| // |mask_info| coordinates are in the target space of the layer. |
| auto* layer_impl1 = GetLayerImpl(child1); |
| EXPECT_FALSE(layer_impl1->draw_properties().mask_filter_info.IsEmpty()); |
| EXPECT_FALSE( |
| layer_impl1->draw_properties().mask_filter_info.HasRoundedCorners()); |
| EXPECT_TRUE( |
| layer_impl1->draw_properties().mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(10, 10, 300, 200), |
| layer_impl1->draw_properties().mask_filter_info.bounds()); |
| // |angle| is updated by the scale transform. |
| EXPECT_EQ(33, layer_impl1->draw_properties() |
| .mask_filter_info.gradient_mask() |
| .angle()); |
| EXPECT_EQ(gradient_mask.steps(), layer_impl1->draw_properties() |
| .mask_filter_info.gradient_mask() |
| .steps()); |
| } |
| |
| // Rotate transform eliminates gradient mask. |
| gfx::Transform rotate_transform; |
| rotate_transform.Rotate(45); |
| child1->SetTransform(rotate_transform); |
| CommitAndActivate(); |
| { |
| auto* layer_impl1 = GetLayerImpl(child1); |
| EXPECT_EQ(gfx::RRectF::Type::kEmpty, |
| layer_impl1->draw_properties() |
| .mask_filter_info.rounded_corner_bounds() |
| .GetType()); |
| EXPECT_FALSE( |
| layer_impl1->draw_properties().mask_filter_info.HasGradientMask()); |
| } |
| |
| // Reset transform |
| child1->SetTransform(gfx::Transform()); |
| |
| // A child layer will create a render surface. |
| auto grand_child1 = Layer::Create(); |
| child1->AddChild(grand_child1); |
| grand_child1->SetBounds(gfx::Size(100, 100)); |
| grand_child1->SetIsDrawable(true); |
| CommitAndActivate(); |
| EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface()); |
| { |
| auto* effect_node1 = GetEffectNode(child1.get()); |
| EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask()); |
| EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners()); |
| EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds()); |
| EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask()); |
| EXPECT_TRUE(effect_node1->HasRenderSurface()); |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().IsEmpty()); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasRoundedCorners()); |
| EXPECT_TRUE(render_surface_impl1->mask_filter_info().HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(100, 100), |
| render_surface_impl1->mask_filter_info().bounds()); |
| EXPECT_EQ(gradient_mask, |
| render_surface_impl1->mask_filter_info().gradient_mask()); |
| } |
| |
| child1->SetTransform(scale_and_translate_transform); |
| CommitAndActivate(); |
| { |
| // |mask_info| is in the coordinate space of the transform node associated |
| // with this effect node. |
| auto* effect_node1 = GetEffectNode(child1.get()); |
| EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask()); |
| EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners()); |
| EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds()); |
| EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask()); |
| EXPECT_TRUE(effect_node1->HasRenderSurface()); |
| |
| // |mask_info| coordinates are in the target space of the render surface's |
| // layer. |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().IsEmpty()); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasRoundedCorners()); |
| EXPECT_TRUE(render_surface_impl1->mask_filter_info().HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(10, 10, 300, 200), |
| render_surface_impl1->mask_filter_info().bounds()); |
| // |angle| is updated by the scale transform. |
| EXPECT_EQ(33, |
| render_surface_impl1->mask_filter_info().gradient_mask().angle()); |
| EXPECT_EQ(gradient_mask.steps(), |
| render_surface_impl1->mask_filter_info().gradient_mask().steps()); |
| } |
| |
| // Rotate transform eliminates gradient mask. |
| child1->SetTransform(rotate_transform); |
| CommitAndActivate(); |
| { |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_EQ(gfx::RRectF::Type::kEmpty, |
| render_surface_impl1->mask_filter_info() |
| .rounded_corner_bounds() |
| .GetType()); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasGradientMask()); |
| } |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, NestedGradientMask) { |
| auto root = Layer::Create(); |
| host()->SetRootLayer(root); |
| root->SetBounds(gfx::Size(200, 200)); |
| root->SetIsDrawable(true); |
| |
| auto child1 = Layer::Create(); |
| root->AddChild(child1); |
| child1->SetBounds(gfx::Size(100, 100)); |
| child1->SetIsDrawable(true); |
| |
| auto grand_child1 = Layer::Create(); |
| child1->AddChild(grand_child1); |
| grand_child1->SetBounds(gfx::Size(50, 50)); |
| grand_child1->SetIsDrawable(true); |
| |
| gfx::LinearGradient gradient_mask1(30); |
| gradient_mask1.AddStep(50, 0x50); |
| child1->SetGradientMask(gradient_mask1); |
| |
| gfx::LinearGradient gradient_mask2(45); |
| gradient_mask2.AddStep(0, 0xFF); |
| gradient_mask2.AddStep(100, 0x0); |
| grand_child1->SetGradientMask(gradient_mask2); |
| |
| CommitAndActivate(); |
| EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface()); |
| { |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_EQ(gradient_mask1, |
| render_surface_impl1->mask_filter_info().gradient_mask()); |
| |
| auto* effect_node2 = GetEffectNode(grand_child1.get()); |
| EXPECT_FALSE(effect_node2->mask_filter_info.IsEmpty()); |
| EXPECT_FALSE(effect_node2->mask_filter_info.HasRoundedCorners()); |
| EXPECT_TRUE(effect_node2->mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(50, 50), effect_node2->mask_filter_info.bounds()); |
| EXPECT_EQ(gradient_mask2, effect_node2->mask_filter_info.gradient_mask()); |
| EXPECT_FALSE(effect_node2->HasRenderSurface()); |
| auto& draw_properties2 = GetLayerImpl(grand_child1)->draw_properties(); |
| EXPECT_FALSE(draw_properties2.mask_filter_info.IsEmpty()); |
| EXPECT_FALSE(draw_properties2.mask_filter_info.HasRoundedCorners()); |
| EXPECT_TRUE(draw_properties2.mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(50, 50), draw_properties2.mask_filter_info.bounds()); |
| EXPECT_EQ(gradient_mask2, |
| draw_properties2.mask_filter_info.gradient_mask()); |
| } |
| |
| gfx::Transform scale_and_translate_transform1; |
| scale_and_translate_transform1.Translate({10.f, 10.f}); |
| scale_and_translate_transform1.Scale(3.f, 2.f); |
| child1->SetTransform(scale_and_translate_transform1); |
| gfx::Transform scale_and_translate_transform2; |
| scale_and_translate_transform2.Translate({10.f, 5.f}); |
| scale_and_translate_transform2.Scale(2.f, 1.5f); |
| grand_child1->SetTransform(scale_and_translate_transform2); |
| |
| CommitAndActivate(); |
| EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface()); |
| { |
| // |mask_info| coordinates are in the target space of the render surface's |
| // layer. |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_EQ(gradient_mask1.steps(), |
| render_surface_impl1->mask_filter_info().gradient_mask().steps()); |
| // |angle| is updated by the scale transform. |
| EXPECT_EQ(21, |
| render_surface_impl1->mask_filter_info().gradient_mask().angle()); |
| |
| // |mask_info| is in the coordinate space of the transform node associated |
| // with this effect node. |
| auto* effect_node2 = GetEffectNode(grand_child1.get()); |
| EXPECT_FALSE(effect_node2->HasRenderSurface()); |
| |
| // |mask_info| coordinates are in the target space of the layer. |
| auto& draw_properties2 = GetLayerImpl(grand_child1)->draw_properties(); |
| EXPECT_FALSE(draw_properties2.mask_filter_info.IsEmpty()); |
| EXPECT_FALSE(draw_properties2.mask_filter_info.HasRoundedCorners()); |
| EXPECT_TRUE(draw_properties2.mask_filter_info.HasGradientMask()); |
| EXPECT_EQ(gfx::RectF(30, 10, 300, 150), |
| draw_properties2.mask_filter_info.bounds()); |
| // |angle| is updated by the scale transform. |
| EXPECT_EQ(26, draw_properties2.mask_filter_info.gradient_mask().angle()); |
| EXPECT_EQ(gradient_mask2.steps(), |
| draw_properties2.mask_filter_info.gradient_mask().steps()); |
| } |
| |
| gfx::Transform rotate_transform; |
| rotate_transform.Rotate(45); |
| child1->SetTransform(rotate_transform); |
| CommitAndActivate(); |
| { |
| auto* render_surface_impl1 = GetRenderSurfaceImpl(child1); |
| EXPECT_EQ(gfx::RRectF::Type::kEmpty, |
| render_surface_impl1->mask_filter_info() |
| .rounded_corner_bounds() |
| .GetType()); |
| EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasGradientMask()); |
| } |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RoundedCornerBounds) { |
| // Layer Tree: |
| // +root |
| // +--render surface |
| // +----rounded corner layer 1 [should trigger render surface] |
| // +----layer 1 |
| // +--rounded corner layer 2 [should trigger render surface] |
| // +----layer 2 |
| // +------rounded corner layer 3 [should trigger render surface] |
| // +--------rounded corner layer 4 [should trigger render surface] |
| |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| constexpr int kRoundedCorner3Radius = 1; |
| constexpr int kRoundedCorner4Radius = 1; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(15.f, 15.f, 20.f, 20.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f); |
| constexpr gfx::RectF kRoundedCornerLayer3Bound(0.f, 15.f, 5.f, 5.f); |
| constexpr gfx::RectF kRoundedCornerLayer4Bound(1.f, 1.f, 3.f, 3.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> render_surface = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> layer_1 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); |
| scoped_refptr<Layer> layer_2 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(render_surface); |
| root->AddChild(rounded_corner_layer_2); |
| |
| render_surface->AddChild(rounded_corner_layer_1); |
| render_surface->AddChild(layer_1); |
| |
| rounded_corner_layer_2->AddChild(layer_2); |
| |
| layer_2->AddChild(rounded_corner_layer_3); |
| |
| rounded_corner_layer_3->AddChild(rounded_corner_layer_4); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| render_surface->SetPosition(gfx::PointF(0, 0)); |
| rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| layer_1->SetPosition(gfx::PointF(10.f, 10.f)); |
| rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| layer_2->SetPosition(gfx::PointF(30.f, 30.f)); |
| rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin()); |
| rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin()); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| render_surface->SetBounds(gfx::Size(50, 50)); |
| rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| layer_1->SetBounds(gfx::Size(10, 10)); |
| rounded_corner_layer_2->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| layer_2->SetBounds(gfx::Size(25, 25)); |
| rounded_corner_layer_3->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); |
| rounded_corner_layer_4->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); |
| |
| // Add Layer transforms. |
| gfx::Transform layer_2_transform; |
| constexpr gfx::Vector2dF kLayer2Translation(10.f, 10.f); |
| layer_2_transform.Translate(kLayer2Translation); |
| layer_2->SetTransform(layer_2_transform); |
| |
| gfx::Transform rounded_corner_layer_3_transform; |
| constexpr float kRoundedCorner3Scale = 2.f; |
| rounded_corner_layer_3_transform.Scale(kRoundedCorner3Scale, |
| kRoundedCorner3Scale); |
| rounded_corner_layer_3->SetTransform(rounded_corner_layer_3_transform); |
| |
| // Set the layer properties |
| render_surface->SetForceRenderSurfaceForTesting(true); |
| |
| root->SetIsDrawable(true); |
| render_surface->SetIsDrawable(true); |
| rounded_corner_layer_1->SetIsDrawable(true); |
| layer_1->SetIsDrawable(true); |
| rounded_corner_layer_2->SetIsDrawable(true); |
| layer_2->SetIsDrawable(true); |
| rounded_corner_layer_3->SetIsDrawable(true); |
| rounded_corner_layer_4->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| rounded_corner_layer_2->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| rounded_corner_layer_3->SetRoundedCorner( |
| {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, |
| kRoundedCorner3Radius}); |
| rounded_corner_layer_4->SetRoundedCorner( |
| {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, |
| kRoundedCorner4Radius}); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this effect node has no descendants that draw and no descendant that |
| // has a rounded corner, it does not need a render surface. |
| const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this node has descendants with roudned corners, it needs a render |
| // surface. It also has 2 descendants that draw. |
| effect_node = GetEffectNode(rounded_corner_layer_2.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| // Since this node has a descendant that has a rounded corner, it will trigger |
| // the creation of a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_3.get()); |
| gfx::RRectF rounded_corner_bounds_3 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), |
| kRoundedCorner3Radius); |
| EXPECT_EQ(rounded_corner_bounds_3.rect(), |
| gfx::RectF(kRoundedCornerLayer3Bound.size())); |
| |
| // Since this node has no descendants that draw nor any descendant that has a |
| // rounded corner, it does not need a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_4.get()); |
| gfx::RRectF rounded_corner_bounds_4 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), |
| kRoundedCorner4Radius); |
| EXPECT_EQ(rounded_corner_bounds_4.rect(), |
| gfx::RectF(kRoundedCornerLayer4Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* rounded_corner_layer_1_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_1->id()); |
| LayerImpl* rounded_corner_layer_2_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_2->id()); |
| LayerImpl* rounded_corner_layer_3_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_3->id()); |
| LayerImpl* rounded_corner_layer_4_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_4->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Rounded corner layer 1 |
| // The render target for this layer is |render_surface|, hence its target |
| // bounds are relative to |render_surface|. |
| // The offset from the origin of the render target is [15, 15] and the device |
| // scale factor is 1.6 thus giving the target space origin of [24, 24]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_rrect_1 = |
| rounded_corner_layer_1_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Rounded corner layer 2 |
| // The render target for this layer is |root|. |
| // The offset from the origin of the render target is [40, 40] and the device |
| // scale factor is 1.6 thus giving the target space origin of [64, 64]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_2 = |
| rounded_corner_layer_2_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_2.IsEmpty()); |
| |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_2 = |
| rounded_corner_layer_2_impl->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| |
| // Rounded corner layer 3 |
| // The render target for this layer is |rounded_corner_2|. |
| // The net offset from the origin of the render target is [40, 55] and the |
| // device scale factor is 1.6 thus giving the target space origin of [64, 88]. |
| // The corner radius is also scaled by a factor of 1.6 * transform scale. |
| const gfx::RRectF actual_self_rrect_3 = |
| rounded_corner_layer_3_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); |
| |
| bounds_in_target_space = kRoundedCornerLayer3Bound; |
| bounds_in_target_space += |
| layer_2->position().OffsetFromOrigin() + kLayer2Translation; |
| bounds_in_target_space.Scale(kDeviceScale); |
| gfx::SizeF transformed_size = bounds_in_target_space.size(); |
| transformed_size.Scale(kRoundedCorner3Scale); |
| bounds_in_target_space.set_size(transformed_size); |
| |
| const gfx::RRectF actual_render_target_rrect_3 = |
| rounded_corner_layer_3_impl->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), |
| kRoundedCorner3Radius * kDeviceScale * kRoundedCorner3Scale); |
| |
| // Rounded corner layer 4 |
| // The render target for this layer is |rounded_corner_3|. |
| // The net offset from the origin of the render target is [1, 1] and the |
| // net scale is 1.6 * transform scale = 3.2 thus giving the target space o |
| // rigin of [3.2, 3.2]. |
| // The corner radius is also scaled by a factor of 3.2. |
| const gfx::RRectF actual_rrect_4 = |
| rounded_corner_layer_4_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer4Bound; |
| bounds_in_target_space.Scale(kDeviceScale * kRoundedCorner3Scale); |
| EXPECT_EQ(actual_rrect_4.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_4.GetSimpleRadius(), |
| kRoundedCorner4Radius * kDeviceScale * kRoundedCorner3Scale); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsInterveningRenderTarget) { |
| // Layer Tree: |
| // +root |
| // +--rounded corner layer 1 [should not trigger render surface] |
| // +----render surface [Does not draw] |
| // +------rounded corner layer 2 [should not trigger render surface] |
| |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(60.f, 0.f, 40.f, 30.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 30.f, 20.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> render_surface = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(rounded_corner_layer_1); |
| rounded_corner_layer_1->AddChild(render_surface); |
| render_surface->AddChild(rounded_corner_layer_2); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| render_surface->SetPosition(gfx::PointF(0, 0)); |
| rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| render_surface->SetBounds(gfx::Size(30, 30)); |
| rounded_corner_layer_2->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| |
| // Set the layer properties |
| render_surface->SetForceRenderSurfaceForTesting(true); |
| |
| root->SetIsDrawable(true); |
| rounded_corner_layer_1->SetIsDrawable(true); |
| rounded_corner_layer_2->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| rounded_corner_layer_2->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this effect node has only 1 descendant that draws and no descendant |
| // that has a rounded corner before the render surface, it does not need a |
| // render surface. |
| const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this effect node has no descendants that draw and no descendant that |
| // has a rounded corner, it does not need a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_2.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* rounded_corner_layer_1_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_1->id()); |
| LayerImpl* rounded_corner_layer_2_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_2->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Rounded corner layer 1 |
| // The render target for this layer is |root|, hence its target |
| // bounds are relative to |root|. |
| // The offset from the origin of the render target is [60, 0] and the device |
| // scale factor is 1.6 thus giving the target space origin of [96, 0]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_rrect_1 = |
| rounded_corner_layer_1_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Rounded corner layer 2 |
| // The render target for this layer is |render_surface|. |
| // The offset from the origin of the render target is [0, 0]. |
| const gfx::RRectF actual_rrect_2 = |
| rounded_corner_layer_2_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsSiblingRenderTarget) { |
| // Layer Tree: |
| // +root |
| // +--rounded corner layer 1 [should trigger render surface] |
| // +----render surface [Does not draw] |
| // +----rounded corner layer 2 [should not trigger render surface] |
| |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 60.f, 30.f, 40.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 20.f, 30.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> render_surface = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(rounded_corner_layer_1); |
| rounded_corner_layer_1->AddChild(render_surface); |
| rounded_corner_layer_1->AddChild(rounded_corner_layer_2); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| render_surface->SetPosition(gfx::PointF(0, 0)); |
| rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| render_surface->SetBounds(gfx::Size(30, 30)); |
| rounded_corner_layer_2->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| |
| // Set the layer properties |
| render_surface->SetForceRenderSurfaceForTesting(true); |
| |
| root->SetIsDrawable(true); |
| rounded_corner_layer_1->SetIsDrawable(true); |
| rounded_corner_layer_2->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| rounded_corner_layer_2->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this effect node has 1 descendant with a rounded corner without a |
| // render surface along the chain, it need a render surface. |
| const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this effect node has no descendants that draw and no descendant that |
| // has a rounded corner, it does not need a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_2.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* rounded_corner_layer_1_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_1->id()); |
| LayerImpl* rounded_corner_layer_2_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer_2->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Rounded corner layer 1 |
| // The render target for this layer is |root|, hence its target |
| // bounds are relative to |root|. |
| // The offset from the origin of the render target is [0, 60] and the device |
| // scale factor is 1.6 thus giving the target space origin of [0, 96]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_1 = |
| rounded_corner_layer_1_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); |
| |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_1 = |
| rounded_corner_layer_1_impl->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Rounded corner layer 2 |
| // The render target for this layer is |render_surface|. |
| // The offset from the origin of the render target is [0, 0]. |
| const gfx::RRectF actual_rrect_2 = |
| rounded_corner_layer_2_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, FastRoundedCornerDoesNotTriggerRenderSurface) { |
| // Layer Tree: |
| // +root |
| // +--fast rounded corner layer [should not trigger render surface] |
| // +----layer 1 |
| // +----layer 2 |
| // +--rounded corner layer [should trigger render surface] |
| // +----layer 3 |
| // +----layer 4 |
| |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 0.f, 50.f, 50.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> fast_rounded_corner_layer = Layer::Create(); |
| scoped_refptr<Layer> layer_1 = Layer::Create(); |
| scoped_refptr<Layer> layer_2 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer = Layer::Create(); |
| scoped_refptr<Layer> layer_3 = Layer::Create(); |
| scoped_refptr<Layer> layer_4 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(fast_rounded_corner_layer); |
| root->AddChild(rounded_corner_layer); |
| |
| fast_rounded_corner_layer->AddChild(layer_1); |
| fast_rounded_corner_layer->AddChild(layer_2); |
| |
| rounded_corner_layer->AddChild(layer_3); |
| rounded_corner_layer->AddChild(layer_4); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| fast_rounded_corner_layer->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| layer_1->SetPosition(gfx::PointF(0.f, 0.f)); |
| layer_2->SetPosition(gfx::PointF(25.f, 0.f)); |
| rounded_corner_layer->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| layer_3->SetPosition(gfx::PointF(0.f, 0.f)); |
| layer_4->SetPosition(gfx::PointF(30.f, 0.f)); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| fast_rounded_corner_layer->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| layer_1->SetBounds(gfx::Size(25, 25)); |
| layer_2->SetBounds(gfx::Size(25, 25)); |
| rounded_corner_layer->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| layer_3->SetBounds(gfx::Size(30, 60)); |
| layer_4->SetBounds(gfx::Size(30, 60)); |
| |
| root->SetIsDrawable(true); |
| fast_rounded_corner_layer->SetIsDrawable(true); |
| layer_1->SetIsDrawable(true); |
| layer_2->SetIsDrawable(true); |
| rounded_corner_layer->SetIsDrawable(true); |
| layer_3->SetIsDrawable(true); |
| layer_4->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| fast_rounded_corner_layer->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| rounded_corner_layer->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| |
| fast_rounded_corner_layer->SetIsFastRoundedCorner(true); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this layer has a fast rounded corner, it should not have a render |
| // surface even though it has 2 layers in the subtree that draws content. |
| const EffectNode* effect_node = |
| GetEffectNode(fast_rounded_corner_layer.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_TRUE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this node has 2 descendants that draw, it will have a rounded corner. |
| effect_node = GetEffectNode(rounded_corner_layer.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* fast_rounded_corner_layer_impl = |
| layer_tree_impl->LayerById(fast_rounded_corner_layer->id()); |
| LayerImpl* layer_1_impl = layer_tree_impl->LayerById(layer_1->id()); |
| LayerImpl* layer_2_impl = layer_tree_impl->LayerById(layer_2->id()); |
| LayerImpl* rounded_corner_layer_impl = |
| layer_tree_impl->LayerById(rounded_corner_layer->id()); |
| LayerImpl* layer_3_impl = layer_tree_impl->LayerById(layer_3->id()); |
| LayerImpl* layer_4_impl = layer_tree_impl->LayerById(layer_4->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Fast rounded corner layer. |
| // The render target for this layer is |root|, hence its target bounds are |
| // relative to |root|. |
| // The offset from the origin of the render target is [0, 0] and the device |
| // scale factor is 1.6. |
| const gfx::RRectF actual_rrect_1 = |
| fast_rounded_corner_layer_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Layer 1 and layer 2 rounded corner bounds |
| // This should have the same rounded corner boudns as fast rounded corner |
| // layer. |
| const gfx::RRectF layer_1_rrect = |
| layer_1_impl->draw_properties().mask_filter_info.rounded_corner_bounds(); |
| const gfx::RRectF layer_2_rrect = |
| layer_2_impl->draw_properties().mask_filter_info.rounded_corner_bounds(); |
| EXPECT_EQ(actual_rrect_1, layer_1_rrect); |
| EXPECT_EQ(actual_rrect_1, layer_2_rrect); |
| |
| // Rounded corner layer |
| // The render target for this layer is |root|. |
| // The offset from the origin of the render target is [40, 40] and the device |
| // scale factor is 1.6 thus giving the target space origin of [64, 64]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_2 = |
| rounded_corner_layer_impl->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_2.IsEmpty()); |
| |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_2 = |
| rounded_corner_layer_impl->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| |
| // Layer 3 and layer 4 should have no rounded corner bounds set as their |
| // parent is a render surface. |
| const gfx::RRectF layer_3_rrect = |
| layer_3_impl->draw_properties().mask_filter_info.rounded_corner_bounds(); |
| const gfx::RRectF layer_4_rrect = |
| layer_4_impl->draw_properties().mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(layer_3_rrect.IsEmpty()); |
| EXPECT_TRUE(layer_4_rrect.IsEmpty()); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, |
| FastRoundedCornerTriggersRenderSurfaceInAncestor) { |
| // Layer Tree: |
| // +root |
| // +--rounded corner layer [1] [should trigger render surface] |
| // +----fast rounded corner layer [2] [should not trigger render surface] |
| // +--rounded corner layer [3] [should trigger render surface] |
| // +----rounded corner layer [4] [should not trigger render surface] |
| |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| constexpr int kRoundedCorner3Radius = 1; |
| constexpr int kRoundedCorner4Radius = 3; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(5.f, 5.f, 50.f, 50.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 25.f, 25.f); |
| constexpr gfx::RectF kRoundedCornerLayer3Bound(40.f, 40.f, 60.f, 60.f); |
| constexpr gfx::RectF kRoundedCornerLayer4Bound(30.f, 0.f, 30.f, 60.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> fast_rounded_corner_layer_2 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(rounded_corner_layer_1); |
| root->AddChild(rounded_corner_layer_3); |
| |
| rounded_corner_layer_1->AddChild(fast_rounded_corner_layer_2); |
| |
| rounded_corner_layer_3->AddChild(rounded_corner_layer_4); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| fast_rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin()); |
| rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin()); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| fast_rounded_corner_layer_2->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| rounded_corner_layer_3->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); |
| rounded_corner_layer_4->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); |
| |
| root->SetIsDrawable(true); |
| rounded_corner_layer_1->SetIsDrawable(true); |
| fast_rounded_corner_layer_2->SetIsDrawable(true); |
| rounded_corner_layer_3->SetIsDrawable(true); |
| rounded_corner_layer_4->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| fast_rounded_corner_layer_2->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| rounded_corner_layer_3->SetRoundedCorner( |
| {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, |
| kRoundedCorner3Radius}); |
| rounded_corner_layer_4->SetRoundedCorner( |
| {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, |
| kRoundedCorner4Radius}); |
| |
| fast_rounded_corner_layer_2->SetIsFastRoundedCorner(true); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this layer has a descendant that has rounded corner, this node will |
| // require a render surface. |
| const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this layer has no descendant with rounded corner or drawable, it will |
| // not have a render surface. |
| effect_node = GetEffectNode(fast_rounded_corner_layer_2.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_TRUE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| // Since this layer has 1 descendant with a rounded corner, it should have a |
| // render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_3.get()); |
| gfx::RRectF rounded_corner_bounds_3 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), |
| kRoundedCorner3Radius); |
| EXPECT_EQ(rounded_corner_bounds_3.rect(), |
| gfx::RectF(kRoundedCornerLayer3Bound.size())); |
| |
| // Since this layer no descendants, it would no thave a render pass. |
| effect_node = GetEffectNode(rounded_corner_layer_4.get()); |
| gfx::RRectF rounded_corner_bounds_4 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), |
| kRoundedCorner4Radius); |
| EXPECT_EQ(rounded_corner_bounds_4.rect(), |
| gfx::RectF(kRoundedCornerLayer4Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* rounded_corner_layer_impl_1 = |
| layer_tree_impl->LayerById(rounded_corner_layer_1->id()); |
| LayerImpl* fast_rounded_corner_layer_impl_2 = |
| layer_tree_impl->LayerById(fast_rounded_corner_layer_2->id()); |
| LayerImpl* rounded_corner_layer_impl_3 = |
| layer_tree_impl->LayerById(rounded_corner_layer_3->id()); |
| LayerImpl* rounded_corner_layer_impl_4 = |
| layer_tree_impl->LayerById(rounded_corner_layer_4->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Rounded corner layer 1. |
| // The render target for this layer is |root|, hence its target bounds are |
| // relative to |root|. |
| // The offset from the origin of the render target is [5, 5] and the device |
| // scale factor is 1.6 giving a total offset of [8, 8]. |
| const gfx::RRectF actual_self_rrect_1 = |
| rounded_corner_layer_impl_1->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); |
| |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_1 = |
| rounded_corner_layer_impl_1->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Fast rounded corner layer 2 |
| // The render target for this layer is |rounded_corner_layer_1|. |
| // The offset from the origin of the render target is [0, 0] and the device |
| // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_2 = |
| fast_rounded_corner_layer_impl_2->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| |
| // Rounded corner layer 3 |
| // The render target for this layer is |root|. |
| // The offset from the origin of the render target is [40, 40] and the device |
| // scale factor is 1.6 thus giving the target space origin of [64, 64]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_3 = |
| rounded_corner_layer_impl_3->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); |
| |
| bounds_in_target_space = kRoundedCornerLayer3Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_3 = |
| rounded_corner_layer_impl_3->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), |
| kRoundedCorner3Radius * kDeviceScale); |
| |
| // Rounded corner layer 4 |
| // The render target for this layer is |rounded_corner_layer_3|. |
| // The offset from the origin of the render target is [30, 0] and the device |
| // scale factor is 1.6 thus giving the target space origin of [48, 0]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_4 = |
| rounded_corner_layer_impl_4->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer4Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(), |
| kRoundedCorner4Radius * kDeviceScale); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, |
| FastRoundedCornerDoesNotTriggerRenderSurfaceFromSubtree) { |
| // Layer Tree: |
| // +root |
| // +--fast rounded corner layer 1 [should trigger render surface] |
| // +----rounded corner layer 1 [should not trigger render surface] |
| // +--rounded corner layer 2 [should trigger render surface] |
| // +----rounded corner layer 3 [should not trigger render surface] |
| constexpr int kRoundedCorner1Radius = 2; |
| constexpr int kRoundedCorner2Radius = 5; |
| constexpr int kRoundedCorner3Radius = 4; |
| constexpr int kRoundedCorner4Radius = 5; |
| |
| constexpr gfx::RectF kRoundedCornerLayer1Bound(10.f, 5.f, 45.f, 50.f); |
| constexpr gfx::RectF kRoundedCornerLayer2Bound(5.f, 5.f, 20.f, 20.f); |
| constexpr gfx::RectF kRoundedCornerLayer3Bound(60.f, 5.f, 40.f, 25.f); |
| constexpr gfx::RectF kRoundedCornerLayer4Bound(0.f, 10.f, 10.f, 20.f); |
| |
| constexpr float kDeviceScale = 1.6f; |
| |
| scoped_refptr<Layer> root = Layer::Create(); |
| scoped_refptr<Layer> fast_rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); |
| scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); |
| |
| // Set up layer tree |
| root->AddChild(fast_rounded_corner_layer_1); |
| root->AddChild(rounded_corner_layer_2); |
| |
| fast_rounded_corner_layer_1->AddChild(rounded_corner_layer_1); |
| rounded_corner_layer_2->AddChild(rounded_corner_layer_3); |
| |
| // Set the root layer on host. |
| host()->SetRootLayer(root); |
| |
| // Set layer positions. |
| fast_rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); |
| rounded_corner_layer_1->SetPosition(kRoundedCornerLayer2Bound.origin()); |
| rounded_corner_layer_2->SetPosition(kRoundedCornerLayer3Bound.origin()); |
| rounded_corner_layer_3->SetPosition(kRoundedCornerLayer4Bound.origin()); |
| |
| // Set up layer bounds. |
| root->SetBounds(gfx::Size(100, 100)); |
| fast_rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); |
| rounded_corner_layer_1->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); |
| rounded_corner_layer_2->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); |
| rounded_corner_layer_3->SetBounds( |
| gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); |
| |
| root->SetIsDrawable(true); |
| fast_rounded_corner_layer_1->SetIsDrawable(true); |
| rounded_corner_layer_1->SetIsDrawable(true); |
| rounded_corner_layer_2->SetIsDrawable(true); |
| rounded_corner_layer_3->SetIsDrawable(true); |
| |
| // Set Rounded corners |
| fast_rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, |
| kRoundedCorner1Radius}); |
| rounded_corner_layer_1->SetRoundedCorner( |
| {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, |
| kRoundedCorner2Radius}); |
| rounded_corner_layer_2->SetRoundedCorner( |
| {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, |
| kRoundedCorner3Radius}); |
| rounded_corner_layer_3->SetRoundedCorner( |
| {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, |
| kRoundedCorner4Radius}); |
| |
| fast_rounded_corner_layer_1->SetIsFastRoundedCorner(true); |
| |
| UpdateMainDrawProperties(kDeviceScale); |
| |
| // Since this layer has a descendant with rounded corner, it needs a render |
| // surface. |
| const EffectNode* effect_node = |
| GetEffectNode(fast_rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_1 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_TRUE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), |
| kRoundedCorner1Radius); |
| EXPECT_EQ(rounded_corner_bounds_1.rect(), |
| gfx::RectF(kRoundedCornerLayer1Bound.size())); |
| |
| // Since this layer has no descendant with rounded corner or drawable, it will |
| // not have a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_1.get()); |
| gfx::RRectF rounded_corner_bounds_2 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), |
| kRoundedCorner2Radius); |
| EXPECT_EQ(rounded_corner_bounds_2.rect(), |
| gfx::RectF(kRoundedCornerLayer2Bound.size())); |
| |
| // Since this layer has a descendant with rounded corner, it should have a |
| // render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_2.get()); |
| gfx::RRectF rounded_corner_bounds_3 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), |
| kRoundedCorner3Radius); |
| EXPECT_EQ(rounded_corner_bounds_3.rect(), |
| gfx::RectF(kRoundedCornerLayer3Bound.size())); |
| |
| // Since this layer has no descendant, it does not need a render surface. |
| effect_node = GetEffectNode(rounded_corner_layer_3.get()); |
| gfx::RRectF rounded_corner_bounds_4 = |
| effect_node->mask_filter_info.rounded_corner_bounds(); |
| EXPECT_FALSE(effect_node->HasRenderSurface()); |
| EXPECT_FALSE(effect_node->is_fast_rounded_corner); |
| EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), |
| kRoundedCorner4Radius); |
| EXPECT_EQ(rounded_corner_bounds_4.rect(), |
| gfx::RectF(kRoundedCornerLayer4Bound.size())); |
| |
| CommitAndActivate(kDeviceScale); |
| LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); |
| |
| // Get the layer impl for each Layer. |
| LayerImpl* fast_rounded_corner_layer_impl_1 = |
| layer_tree_impl->LayerById(fast_rounded_corner_layer_1->id()); |
| LayerImpl* rounded_corner_layer_impl_1 = |
| layer_tree_impl->LayerById(rounded_corner_layer_1->id()); |
| LayerImpl* rounded_corner_layer_impl_2 = |
| layer_tree_impl->LayerById(rounded_corner_layer_2->id()); |
| LayerImpl* rounded_corner_layer_impl_3 = |
| layer_tree_impl->LayerById(rounded_corner_layer_3->id()); |
| |
| EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); |
| |
| // Fast rounded corner layer 1. |
| // The render target for this layer is |root|, hence its target bounds are |
| // relative to |root|. |
| // The offset from the origin of the render target is [5, 5] and the device |
| // scale factor is 1.6. |
| const gfx::RRectF actual_self_rrect_1 = |
| fast_rounded_corner_layer_impl_1->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); |
| |
| gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_1 = |
| fast_rounded_corner_layer_impl_1->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), |
| kRoundedCorner1Radius * kDeviceScale); |
| |
| // Rounded corner layer 1 |
| // The render target for this layer is |fast_rounded_corner_layer_1|. |
| // The offset from the origin of the render target is [0, 0] and the device |
| // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_2 = |
| rounded_corner_layer_impl_1->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer2Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(), |
| kRoundedCorner2Radius * kDeviceScale); |
| |
| // Rounded corner layer 3 |
| // The render target for this layer is |root|. |
| // The offset from the origin of the render target is [5, 5] and the device |
| // scale factor is 1.6 thus giving the target space origin of [8, 8]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_3 = |
| rounded_corner_layer_impl_2->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); |
| |
| bounds_in_target_space = kRoundedCornerLayer3Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| const gfx::RRectF actual_render_target_rrect_3 = |
| rounded_corner_layer_impl_2->render_target() |
| ->mask_filter_info() |
| .rounded_corner_bounds(); |
| EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), |
| kRoundedCorner3Radius * kDeviceScale); |
| |
| // Rounded corner layer 4 |
| // The render target for this layer is |rounded_corner_layer_2|. |
| // The offset from the origin of the render target is [0, 5] and the device |
| // scale factor is 1.6 thus giving the target space origin of [0, 8]. The |
| // corner radius is also scaled by a factor of 1.6. |
| const gfx::RRectF actual_self_rrect_4 = |
| rounded_corner_layer_impl_3->draw_properties() |
| .mask_filter_info.rounded_corner_bounds(); |
| bounds_in_target_space = kRoundedCornerLayer4Bound; |
| bounds_in_target_space.Scale(kDeviceScale); |
| EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space); |
| EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(), |
| kRoundedCorner4Radius * kDeviceScale); |
| } |
| |
| TEST_F(PropertyTreeBuilderTest, SubtreeSize) { |
| constexpr viz::SubtreeCaptureId kCaptureId{42}; |
| |
| auto parent = Layer::Create(); |
| host()->SetRootLayer(parent); |
| auto child = Layer::Create(); |
| parent->AddChild(child); |
| child->SetSubtreeCaptureId(kCaptureId); |
| |
| // Layer has empty bounds. |
| Commit(1.1f); |
| EffectNode* node = GetEffectNode(child.get()); |
| EXPECT_EQ((gfx::Size{}), node->subtree_size); |
| EXPECT_EQ(kCaptureId, node->subtree_capture_id); |
| |
| // Layer has bounds, scaling is 1. |
| child->SetBounds(gfx::Size{1280, 720}); |
| Commit(1.0f); |
| node = GetEffectNode(child.get()); |
| EXPECT_EQ((gfx::Size{1280, 720}), node->subtree_size); |
| |
| // Layer has bounds, scaling is 2. |
| Commit(2.0f); |
| node = GetEffectNode(child.get()); |
| EXPECT_EQ((gfx::Size{2560, 1440}), node->subtree_size); |
| } |
| |
| } // namespace |
| } // namespace cc |