// Copyright 2012 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 #include "cc/layers/append_quads_data.h" #include "cc/layers/nine_patch_layer_impl.h" #include "cc/resources/ui_resource_bitmap.h" #include "cc/resources/ui_resource_client.h" #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_frame_sink.h" #include "cc/test/fake_ui_resource_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_test_common.h" #include "cc/trees/single_thread_proxy.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/transform.h" namespace cc { namespace { gfx::Rect ToRoundedIntRect(const gfx::RectF& rect_f) { return gfx::Rect(gfx::ToRoundedInt(rect_f.x()), gfx::ToRoundedInt(rect_f.y()), gfx::ToRoundedInt(rect_f.width()), gfx::ToRoundedInt(rect_f.height())); } void NinePatchLayerLayoutTest(const gfx::Size& bitmap_size, const gfx::Rect& aperture_rect, const gfx::Size& layer_size, const gfx::Rect& border, bool fill_center, size_t expected_quad_size) { std::unique_ptr render_pass = viz::RenderPass::Create(); gfx::Rect visible_layer_rect(layer_size); gfx::Rect expected_remaining(border.x(), border.y(), layer_size.width() - border.width(), layer_size.height() - border.height()); FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; std::unique_ptr layer_tree_frame_sink = FakeLayerTreeFrameSink::Create3d(); FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); layer->draw_properties().visible_layer_rect = visible_layer_rect; layer->SetBounds(layer_size); layer->test_properties()->force_render_surface = true; UIResourceId uid = 1; bool is_opaque = false; UIResourceBitmap bitmap(bitmap_size, is_opaque); host_impl.CreateUIResource(uid, bitmap); layer->SetUIResourceId(uid); layer->SetImageBounds(bitmap_size); layer->SetLayout(aperture_rect, border, gfx::Rect(), fill_center, false); host_impl.active_tree()->SetRootLayerForTesting(std::move(layer)); host_impl.active_tree()->BuildPropertyTreesForTesting(); AppendQuadsData data; host_impl.active_tree()->root_layer_for_testing()->AppendQuads( render_pass.get(), &data); // Verify quad rects const auto& quads = render_pass->quad_list; EXPECT_EQ(expected_quad_size, quads.size()); Region layer_remaining(visible_layer_rect); for (auto iter = quads.cbegin(); iter != quads.cend(); ++iter) { gfx::Rect quad_rect = iter->rect; EXPECT_TRUE(visible_layer_rect.Contains(quad_rect)) << iter.index(); EXPECT_TRUE(layer_remaining.Contains(quad_rect)) << iter.index(); EXPECT_EQ(iter->needs_blending, !iter->shared_quad_state->are_contents_opaque); layer_remaining.Subtract(Region(quad_rect)); } // Check if the left-over quad is the same size as the mapped aperture quad in // layer space. if (!fill_center) { EXPECT_EQ(expected_remaining, layer_remaining.bounds()); } else { EXPECT_TRUE(layer_remaining.bounds().IsEmpty()); } // Verify UV rects gfx::Rect bitmap_rect(bitmap_size); Region tex_remaining(bitmap_rect); for (auto* quad : quads) { const viz::TextureDrawQuad* tex_quad = viz::TextureDrawQuad::MaterialCast(quad); gfx::RectF tex_rect = gfx::BoundingRect(tex_quad->uv_top_left, tex_quad->uv_bottom_right); tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); } if (!fill_center) { EXPECT_EQ(aperture_rect, tex_remaining.bounds()); Region aperture_region(aperture_rect); EXPECT_EQ(aperture_region, tex_remaining); } else { EXPECT_TRUE(layer_remaining.bounds().IsEmpty()); } host_impl.DeleteUIResource(uid); } void NinePatchLayerLayoutTestWithOcclusion(const gfx::Size& bitmap_size, const gfx::Rect& aperture_rect, const gfx::Size& layer_size, const gfx::Rect& border, const gfx::Rect& occlusion, bool fill_center, size_t expected_quad_size) { std::unique_ptr render_pass = viz::RenderPass::Create(); gfx::Rect visible_layer_rect(layer_size); int border_left = std::min(border.x(), occlusion.x()), border_top = std::min(border.y(), occlusion.y()), border_right = std::min(border.width() - border.x(), layer_size.width() - occlusion.right()), border_bottom = std::min(border.height() - border.y(), layer_size.height() - occlusion.bottom()); gfx::Rect expected_layer_remaining( border_left, border_top, layer_size.width() - border_left - border_right, layer_size.height() - border_top - border_bottom); float ratio_left = border_left == 0 ? 0 : (aperture_rect.x() / border.x()), ratio_top = border_top == 0 ? 0 : (aperture_rect.y() / border.y()), ratio_right = border_right == 0 ? 0 : ((bitmap_size.width() - aperture_rect.right()) / (border.width() - border.x())), ratio_bottom = border_bottom == 0 ? 0 : ((bitmap_size.height() - aperture_rect.bottom()) / (border.height() - border.y())); int image_remaining_left = border_left * ratio_left, image_remaining_top = border_top * ratio_top, image_remaining_right = border_right * ratio_right, image_remaining_bottom = border_bottom * ratio_bottom; gfx::Rect expected_tex_remaining( image_remaining_left, image_remaining_top, bitmap_size.width() - image_remaining_right - image_remaining_left, bitmap_size.height() - image_remaining_bottom - image_remaining_top); FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; std::unique_ptr layer_tree_frame_sink = FakeLayerTreeFrameSink::Create3d(); FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); layer->draw_properties().visible_layer_rect = visible_layer_rect; layer->SetBounds(layer_size); layer->test_properties()->force_render_surface = true; UIResourceId uid = 1; bool is_opaque = false; UIResourceBitmap bitmap(bitmap_size, is_opaque); host_impl.CreateUIResource(uid, bitmap); layer->SetUIResourceId(uid); layer->SetImageBounds(bitmap_size); layer->SetLayout(aperture_rect, border, occlusion, false, false); host_impl.active_tree()->SetRootLayerForTesting(std::move(layer)); host_impl.active_tree()->BuildPropertyTreesForTesting(); AppendQuadsData data; host_impl.active_tree()->root_layer_for_testing()->AppendQuads( render_pass.get(), &data); // Verify quad rects const auto& quads = render_pass->quad_list; EXPECT_EQ(expected_quad_size, quads.size()); Region layer_remaining(visible_layer_rect); for (auto iter = quads.cbegin(); iter != quads.cend(); ++iter) { gfx::Rect quad_rect = iter->rect; EXPECT_TRUE(visible_layer_rect.Contains(quad_rect)) << iter.index(); EXPECT_TRUE(layer_remaining.Contains(quad_rect)) << iter.index(); EXPECT_EQ(iter->needs_blending, !iter->shared_quad_state->are_contents_opaque); layer_remaining.Subtract(Region(quad_rect)); } // Check if the left-over quad is the same size as the mapped aperture quad in // layer space. EXPECT_EQ(expected_layer_remaining, layer_remaining.bounds()); // Verify UV rects gfx::Rect bitmap_rect(bitmap_size); Region tex_remaining(bitmap_rect); for (auto* quad : quads) { const viz::TextureDrawQuad* tex_quad = viz::TextureDrawQuad::MaterialCast(quad); gfx::RectF tex_rect = gfx::BoundingRect(tex_quad->uv_top_left, tex_quad->uv_bottom_right); tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); } EXPECT_EQ(expected_tex_remaining, tex_remaining.bounds()); Region aperture_region(expected_tex_remaining); EXPECT_EQ(aperture_region, tex_remaining); host_impl.DeleteUIResource(uid); } TEST(NinePatchLayerImplTest, VerifyDrawQuads) { // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30. // The bounds of the layer are set to 400x400. gfx::Size bitmap_size(100, 100); gfx::Size layer_size(400, 500); gfx::Rect aperture_rect(20, 30, 40, 50); gfx::Rect border(40, 40, 80, 80); bool fill_center = false; size_t expected_quad_size = 8; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); // The bounds of the layer are set to less than the bitmap size. bitmap_size = gfx::Size(100, 100); layer_size = gfx::Size(40, 50); aperture_rect = gfx::Rect(20, 30, 40, 50); border = gfx::Rect(10, 10, 25, 15); fill_center = true; expected_quad_size = 9; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); // Layer and image sizes are equal. bitmap_size = gfx::Size(100, 100); layer_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(20, 30, 40, 50); border = gfx::Rect(20, 30, 40, 50); fill_center = true; expected_quad_size = 9; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); } TEST(NinePatchLayerImplTest, VerifyDrawQuadsWithOcclusion) { // Occlusion removed part of the border and leaves us with 12 patches. gfx::Size bitmap_size(100, 100); gfx::Rect aperture_rect(30, 30, 40, 40); gfx::Size layer_size(400, 400); gfx::Rect occlusion(20, 20, 360, 360); gfx::Rect border(30, 30, 60, 60); size_t expected_quad_size = 12; NinePatchLayerLayoutTestWithOcclusion(bitmap_size, aperture_rect, layer_size, border, occlusion, false, expected_quad_size); bitmap_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(20, 30, 60, 40); layer_size = gfx::Size(400, 400); occlusion = gfx::Rect(10, 10, 380, 380); border = gfx::Rect(20, 30, 40, 60); expected_quad_size = 12; NinePatchLayerLayoutTestWithOcclusion(bitmap_size, aperture_rect, layer_size, border, occlusion, false, expected_quad_size); // All borders are empty, so nothing should be drawn. bitmap_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(0, 0, 100, 100); layer_size = gfx::Size(400, 400); occlusion = gfx::Rect(0, 0, 400, 400); border = gfx::Rect(0, 0, 0, 0); expected_quad_size = 0; NinePatchLayerLayoutTestWithOcclusion(bitmap_size, aperture_rect, layer_size, border, occlusion, false, expected_quad_size); // Right border is empty, we should have no quads on the right side. bitmap_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(20, 30, 80, 40); layer_size = gfx::Size(400, 400); occlusion = gfx::Rect(10, 10, 390, 380); border = gfx::Rect(20, 30, 20, 60); expected_quad_size = 7; NinePatchLayerLayoutTestWithOcclusion(bitmap_size, aperture_rect, layer_size, border, occlusion, false, expected_quad_size); } TEST(NinePatchLayerImplTest, VerifyDrawQuadsWithEmptyPatches) { // The top component of the 9-patch is empty, so there should be no quads for // the top three components. gfx::Size bitmap_size(100, 100); gfx::Size layer_size(100, 100); gfx::Rect aperture_rect(10, 0, 80, 90); gfx::Rect border(10, 0, 20, 10); bool fill_center = false; size_t expected_quad_size = 5; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); // The top and left components of the 9-patch are empty, so there should be no // quads for the left and top components. bitmap_size = gfx::Size(100, 100); layer_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(0, 0, 90, 90); border = gfx::Rect(0, 0, 10, 10); fill_center = false; expected_quad_size = 3; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); // The aperture is the size of the bitmap and the center doesn't draw. bitmap_size = gfx::Size(100, 100); layer_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(0, 0, 100, 100); border = gfx::Rect(0, 0, 0, 0); fill_center = false; expected_quad_size = 0; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); // The aperture is the size of the bitmap and the center does draw. bitmap_size = gfx::Size(100, 100); layer_size = gfx::Size(100, 100); aperture_rect = gfx::Rect(0, 0, 100, 100); border = gfx::Rect(0, 0, 0, 0); fill_center = true; expected_quad_size = 1; NinePatchLayerLayoutTest(bitmap_size, aperture_rect, layer_size, border, fill_center, expected_quad_size); } TEST(NinePatchLayerImplTest, Occlusion) { gfx::Size layer_size(1000, 1000); gfx::Size viewport_size(1000, 1000); LayerTestCommon::LayerImplTest impl; SkBitmap sk_bitmap; sk_bitmap.allocN32Pixels(10, 10); sk_bitmap.setImmutable(); UIResourceId uid = 5; UIResourceBitmap bitmap(sk_bitmap); impl.host_impl()->CreateUIResource(uid, bitmap); NinePatchLayerImpl* nine_patch_layer_impl = impl.AddChildToRoot(); nine_patch_layer_impl->SetBounds(layer_size); nine_patch_layer_impl->SetDrawsContent(true); nine_patch_layer_impl->SetUIResourceId(uid); nine_patch_layer_impl->SetImageBounds(gfx::Size(10, 10)); gfx::Rect aperture = gfx::Rect(3, 3, 4, 4); gfx::Rect border = gfx::Rect(300, 300, 400, 400); nine_patch_layer_impl->SetLayout(aperture, border, gfx::Rect(), true, false); impl.CalcDrawProps(viewport_size); { SCOPED_TRACE("No occlusion"); gfx::Rect occluded; impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, occluded); LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect(layer_size)); EXPECT_EQ(9u, impl.quad_list().size()); } { SCOPED_TRACE("Full occlusion"); gfx::Rect occluded(nine_patch_layer_impl->visible_layer_rect()); impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, occluded); LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect()); EXPECT_EQ(impl.quad_list().size(), 0u); } { SCOPED_TRACE("Partial occlusion"); gfx::Rect occluded(0, 0, 500, 1000); impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, occluded); size_t partially_occluded_count = 0; LayerTestCommon::VerifyQuadsAreOccluded( impl.quad_list(), occluded, &partially_occluded_count); // The layer outputs nine quads, three of which are partially occluded, and // three fully occluded. EXPECT_EQ(6u, impl.quad_list().size()); EXPECT_EQ(3u, partially_occluded_count); } } TEST(NinePatchLayerImplTest, OpaqueRect) { gfx::Size layer_size(1000, 1000); gfx::Size viewport_size(1000, 1000); LayerTestCommon::LayerImplTest impl; SkBitmap sk_bitmap_opaque; sk_bitmap_opaque.allocN32Pixels(10, 10); sk_bitmap_opaque.setImmutable(); sk_bitmap_opaque.setAlphaType(kOpaque_SkAlphaType); UIResourceId uid_opaque = 6; UIResourceBitmap bitmap_opaque(sk_bitmap_opaque); impl.host_impl()->CreateUIResource(uid_opaque, bitmap_opaque); SkBitmap sk_bitmap_alpha; sk_bitmap_alpha.allocN32Pixels(10, 10); sk_bitmap_alpha.setImmutable(); sk_bitmap_alpha.setAlphaType(kUnpremul_SkAlphaType); UIResourceId uid_alpha = 7; UIResourceBitmap bitmap_alpha(sk_bitmap_alpha); impl.host_impl()->CreateUIResource(uid_alpha, bitmap_alpha); NinePatchLayerImpl *nine_patch_layer_impl = impl.AddChildToRoot(); nine_patch_layer_impl->SetBounds(layer_size); nine_patch_layer_impl->SetDrawsContent(true); impl.CalcDrawProps(viewport_size); { SCOPED_TRACE("Use opaque image"); nine_patch_layer_impl->SetUIResourceId(uid_opaque); nine_patch_layer_impl->SetImageBounds(gfx::Size(10, 10)); gfx::Rect aperture = gfx::Rect(3, 3, 4, 4); gfx::Rect border = gfx::Rect(300, 300, 400, 400); nine_patch_layer_impl->SetLayout(aperture, border, gfx::Rect(), true, false); impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, gfx::Rect()); const auto& quad_list = impl.quad_list(); for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); ++it) EXPECT_FALSE(it->ShouldDrawWithBlending()); } { SCOPED_TRACE("Use tranparent image"); nine_patch_layer_impl->SetUIResourceId(uid_alpha); impl.AppendQuadsWithOcclusion(nine_patch_layer_impl, gfx::Rect()); const auto& quad_list = impl.quad_list(); for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); ++it) EXPECT_TRUE(it->ShouldDrawWithBlending()); } } } // namespace } // namespace cc