Commit b7884874 authored by Malay Keshav's avatar Malay Keshav Committed by Commit Bot

Introduce API in ui::Layers for setting rounded corners

This patch adds an API to ui::Layers that lets clients set a rounded
corner radius on the layer. The change is propagated to the cc layer
and its effect node as well.

Adds unit test for the change.

Bug: 903486
Change-Id: Ib0d36af9e76b9f6701e0cb674e855e29f5844c7f
Component: ui layer, cc layer, effect node
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1497851
Commit-Queue: Malay Keshav <malaykeshav@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarenne <enne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#637572}
parent d4280dfa
......@@ -54,6 +54,7 @@ Layer::Inputs::Inputs(int layer_id)
use_parent_backface_visibility(false),
background_color(0),
backdrop_filter_quality(1.0f),
corner_radii({0, 0, 0, 0}),
scrollable(false),
is_scrollbar(false),
user_scrollable_horizontal(true),
......@@ -303,9 +304,10 @@ void Layer::SetBounds(const gfx::Size& size) {
if (!layer_tree_host_)
return;
// Both bounds clipping and mask clipping can result in new areas of subtrees
// being exposed on a bounds change. Ensure the damaged areas are updated.
if (masks_to_bounds() || inputs_.mask_layer.get()) {
// Rounded corner clipping, bounds clipping and mask clipping can result in
// new areas of subtrees being exposed on a bounds change. Ensure the damaged
// areas are updated.
if (masks_to_bounds() || inputs_.mask_layer.get() || HasRoundedCorner()) {
SetSubtreePropertyChanged();
SetPropertyTreesNeedRebuild();
}
......@@ -589,6 +591,17 @@ void Layer::SetFiltersOrigin(const gfx::PointF& filters_origin) {
SetNeedsCommit();
}
void Layer::SetRoundedCorner(const std::array<uint32_t, 4>& corner_radii) {
DCHECK(IsPropertyChangeAllowed());
if (inputs_.corner_radii == corner_radii)
return;
inputs_.corner_radii = corner_radii;
SetSubtreePropertyChanged();
SetNeedsCommit();
SetPropertyTreesNeedRebuild();
}
void Layer::SetOpacity(float opacity) {
DCHECK(IsPropertyChangeAllowed());
DCHECK_GE(opacity, 0.f);
......
......@@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <set>
#include <string>
#include <unordered_map>
......@@ -238,6 +239,15 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
// SetNeedsDisplay() that have not been committed to the compositor thread.
const gfx::Rect& update_rect() const { return inputs_.update_rect; }
// Set or get the rounded corner rrect which is applied to the layer and it
// its subtree (as if they are together as a single composited entity) when
// blitting into their target. If this is set, then the layer should be masked
// to bounds.
void SetRoundedCorner(const std::array<uint32_t, 4>& corner_radii);
const std::array<uint32_t, 4>& corner_radii() const {
return inputs_.corner_radii;
}
// Set or get the opacity which should be applied to the contents of the layer
// and its subtree (together as a single composited entity) when blending them
// into their target. Note that this does not speak to the contents of this
......@@ -881,6 +891,12 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
// they are marked as needing to be rebuilt.
void UpdateScrollOffset(const gfx::ScrollOffset&);
// Returns true if any of the corner has a non-zero radius set.
bool HasRoundedCorner() const {
return corner_radii()[0] + corner_radii()[1] + corner_radii()[2] +
corner_radii()[3];
}
// Encapsulates all data, callbacks or interfaces received from the embedder.
struct Inputs {
explicit Inputs(int layer_id);
......@@ -933,6 +949,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
gfx::PointF filters_origin;
float backdrop_filter_quality;
// Corner clip radius for the 4 corners of the layer in the following order:
// top left, top right, bottom right, bottom left
std::array<uint32_t, 4> corner_radii;
gfx::ScrollOffset scroll_offset;
// Size of the scroll container that this layer scrolls in.
......
......@@ -350,6 +350,23 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) {
child2->PushPropertiesTo(child2_impl.get());
grand_child->PushPropertiesTo(grand_child_impl.get()));
EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
const std::array<uint32_t, 4> radii{1, 2, 3, 4};
EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner(radii));
EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
root->PushPropertiesTo(root_impl.get());
child->PushPropertiesTo(child_impl.get());
child2->PushPropertiesTo(child2_impl.get());
grand_child->PushPropertiesTo(grand_child_impl.get()));
EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner({0, 0, 0, 0}));
EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
root->PushPropertiesTo(root_impl.get());
child->PushPropertiesTo(child_impl.get());
child2->PushPropertiesTo(child2_impl.get());
grand_child->PushPropertiesTo(grand_child_impl.get()));
EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetDoubleSided(false));
EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
......@@ -920,6 +937,7 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) {
1, test_layer->SetTransformOrigin(gfx::Point3F(1.23f, 4.56f, 0.f)));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBackgroundColor(SK_ColorLTGRAY));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetMasksToBounds(true));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetRoundedCorner({1, 2, 3, 4}));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.5f));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendMode(SkBlendMode::kHue));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsRootForIsolatedGroup(true));
......@@ -1009,6 +1027,26 @@ TEST_F(LayerTest, PushPropertiesCausesLayerPropertyChangedForTransform) {
EXPECT_TRUE(impl_layer->LayerPropertyChangedNotFromPropertyTrees());
}
TEST_F(LayerTest, PushPropertiesCausesLayerPropertyChangedForRoundCorner) {
scoped_refptr<Layer> test_layer = Layer::Create();
test_layer->SetMasksToBounds(true);
std::unique_ptr<LayerImpl> impl_layer =
LayerImpl::Create(host_impl_.active_tree(), 1);
EXPECT_SET_NEEDS_FULL_TREE_SYNC(1,
layer_tree_host_->SetRootLayer(test_layer));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetRoundedCorner({1, 2, 3, 4}));
EXPECT_FALSE(impl_layer->LayerPropertyChanged());
test_layer->PushPropertiesTo(impl_layer.get());
EXPECT_TRUE(impl_layer->LayerPropertyChanged());
EXPECT_FALSE(impl_layer->LayerPropertyChangedFromPropertyTrees());
EXPECT_TRUE(impl_layer->LayerPropertyChangedNotFromPropertyTrees());
}
TEST_F(LayerTest, PushPropertiesCausesLayerPropertyChangedForOpacity) {
scoped_refptr<Layer> test_layer = Layer::Create();
std::unique_ptr<LayerImpl> impl_layer =
......
......@@ -571,6 +571,12 @@ bool Layer::ShouldDraw() const {
return type_ != LAYER_NOT_DRAWN && GetCombinedOpacity() > 0.0f;
}
void Layer::SetRoundedCornerRadius(
const std::array<uint32_t, 4>& corner_radii) {
cc_layer_->SetRoundedCorner(corner_radii);
ScheduleDraw();
}
// static
void Layer::ConvertPointToLayer(const Layer* source,
const Layer* target,
......
......@@ -7,6 +7,7 @@
#include <stddef.h>
#include <array>
#include <memory>
#include <string>
#include <vector>
......@@ -268,6 +269,14 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate,
// and is not completely obscured by a child.
bool ShouldDraw() const;
// Sets a rounded corner clip radius on the layer. This will clip the layer to
// bounds. The ordering for the array is:
// top left, top right, bottom right, bottom left
void SetRoundedCornerRadius(const std::array<uint32_t, 4>& corner_radii);
const std::array<uint32_t, 4>& rounded_corner_radii() const {
return cc_layer_->corner_radii();
}
// Converts a point from the coordinates of |source| to the coordinates of
// |target|. Necessarily, |source| and |target| must inhabit the same Layer
// tree.
......
......@@ -1222,6 +1222,29 @@ TEST_F(LayerWithNullDelegateTest, MirroringVisibility) {
EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree());
}
TEST_F(LayerWithDelegateTest, RoundedCorner) {
gfx::Rect layer_bounds(10, 20, 100, 100);
const std::array<uint32_t, 4> radii = {5, 10, 15, 20};
const std::array<uint32_t, 4> kEmpty = {0, 0, 0, 0};
std::unique_ptr<Layer> layer(new Layer(LAYER_TEXTURED));
NullLayerDelegate delegate;
layer->set_delegate(&delegate);
layer->SetVisible(true);
layer->SetBounds(layer_bounds);
layer->SetMasksToBounds(true);
compositor()->SetRootLayer(layer.get());
Draw();
EXPECT_EQ(layer->rounded_corner_radii(), kEmpty);
// Setting a rounded corner radius should set an rrect with bounds same as the
// layer.
layer->SetRoundedCornerRadius(radii);
EXPECT_EQ(layer->rounded_corner_radii(), radii);
}
// Checks that stacking-related methods behave as advertised.
TEST_F(LayerWithNullDelegateTest, Stacking) {
std::unique_ptr<Layer> root(new Layer(LAYER_NOT_DRAWN));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment