| // Copyright 2016 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 <stddef.h> |
| |
| #include "build/build_config.h" |
| #include "cc/input/scrollbar.h" |
| #include "cc/layers/painted_overlay_scrollbar_layer.h" |
| #include "cc/layers/painted_scrollbar_layer.h" |
| #include "cc/layers/solid_color_layer.h" |
| #include "cc/paint/paint_canvas.h" |
| #include "cc/paint/paint_flags.h" |
| #include "cc/test/fake_scrollbar.h" |
| #include "cc/test/layer_tree_pixel_test.h" |
| #include "cc/test/pixel_comparator.h" |
| #include "cc/trees/layer_tree_impl.h" |
| #include "components/viz/test/test_in_process_context_provider.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| |
| namespace cc { |
| namespace { |
| |
| class LayerTreeHostScrollbarsPixelTest |
| : public LayerTreePixelTest, |
| public ::testing::WithParamInterface<viz::RendererType> { |
| protected: |
| LayerTreeHostScrollbarsPixelTest() : LayerTreePixelTest(renderer_type()) {} |
| |
| viz::RendererType renderer_type() const { return GetParam(); } |
| |
| void SetupTree() override { |
| SetInitialDeviceScaleFactor(device_scale_factor_); |
| LayerTreePixelTest::SetupTree(); |
| } |
| |
| float device_scale_factor_ = 1.f; |
| }; |
| |
| class PaintedScrollbar : public FakeScrollbar { |
| public: |
| explicit PaintedScrollbar(const gfx::Size& size) { |
| set_should_paint(true); |
| set_has_thumb(false); |
| set_track_rect(gfx::Rect(size)); |
| } |
| |
| void PaintPart(PaintCanvas* canvas, |
| ScrollbarPart part, |
| const gfx::Rect& rect) override { |
| PaintFlags flags; |
| flags.setStyle(PaintFlags::kStroke_Style); |
| flags.setStrokeWidth(SkIntToScalar(paint_scale_)); |
| flags.setColor(color_); |
| gfx::Rect inset_rect = rect; |
| while (!inset_rect.IsEmpty()) { |
| int big = paint_scale_ + 2; |
| int small = paint_scale_; |
| inset_rect.Inset(gfx::Insets::TLBR(big, big, small, small)); |
| canvas->drawRect(RectToSkRect(inset_rect), flags); |
| inset_rect.Inset(gfx::Insets::TLBR(big, big, small, small)); |
| } |
| } |
| |
| void set_paint_scale(int scale) { paint_scale_ = scale; } |
| |
| private: |
| ~PaintedScrollbar() override = default; |
| |
| int paint_scale_ = 4; |
| SkColor color_ = SK_ColorGREEN; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| LayerTreeHostScrollbarsPixelTest, |
| ::testing::ValuesIn(viz::GetGpuRendererTypes()), |
| ::testing::PrintToStringParamName()); |
| |
| // viz::GetGpuRendererTypes() can return an empty list on some platforms. |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LayerTreeHostScrollbarsPixelTest); |
| |
| TEST_P(LayerTreeHostScrollbarsPixelTest, NoScale) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedScrollbar>(gfx::Size(200, 200)); |
| scoped_refptr<PaintedScrollbarLayer> layer = |
| PaintedScrollbarLayer::Create(std::move(scrollbar)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(200, 200)); |
| background->AddChild(layer); |
| |
| RunPixelTest(background, base::FilePath(FILE_PATH_LITERAL("spiral.png"))); |
| } |
| |
| TEST_P(LayerTreeHostScrollbarsPixelTest, DeviceScaleFactor) { |
| // With a device scale of 2, the scrollbar should still be rendered |
| // pixel-perfect, not show scaling artifacts |
| device_scale_factor_ = 2.f; |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedScrollbar>(gfx::Size(100, 100)); |
| scoped_refptr<PaintedScrollbarLayer> layer = |
| PaintedScrollbarLayer::Create(std::move(scrollbar)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(100, 100)); |
| background->AddChild(layer); |
| |
| RunPixelTest(background, |
| base::FilePath(FILE_PATH_LITERAL("spiral_double_scale.png"))); |
| } |
| |
| TEST_P(LayerTreeHostScrollbarsPixelTest, TransformScale) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedScrollbar>(gfx::Size(100, 100)); |
| scoped_refptr<PaintedScrollbarLayer> layer = |
| PaintedScrollbarLayer::Create(std::move(scrollbar)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(100, 100)); |
| background->AddChild(layer); |
| |
| // This has a scale of 2, it should still be rendered pixel-perfect, not show |
| // scaling artifacts |
| gfx::Transform scale_transform; |
| scale_transform.Scale(2.0, 2.0); |
| layer->SetTransform(scale_transform); |
| |
| RunPixelTest(background, |
| base::FilePath(FILE_PATH_LITERAL("spiral_double_scale.png"))); |
| } |
| |
| // Disabled on TSan due to frequent timeouts. crbug.com/848994 |
| #if defined(THREAD_SANITIZER) |
| #define MAYBE_HugeTransformScale DISABLED_HugeTransformScale |
| #else |
| #define MAYBE_HugeTransformScale HugeTransformScale |
| #endif |
| TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedScrollbar>(gfx::Size(10, 400)); |
| scrollbar->set_paint_scale(1); |
| scoped_refptr<PaintedScrollbarLayer> layer = |
| PaintedScrollbarLayer::Create(std::move(scrollbar)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(10, 400)); |
| background->AddChild(layer); |
| |
| auto context = base::MakeRefCounted<viz::TestInProcessContextProvider>( |
| viz::TestContextType::kGLES2, /*support_locking=*/false); |
| gpu::ContextResult result = context->BindToCurrentThread(); |
| DCHECK_EQ(result, gpu::ContextResult::kSuccess); |
| int max_texture_size = 0; |
| context->ContextGL()->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); |
| |
| // We want a scale that creates a texture taller than the max texture size. If |
| // there's no clamping, the texture will be invalid and we'll just get black. |
| double scale = 64.0; |
| ASSERT_GT(scale * layer->bounds().height(), max_texture_size); |
| |
| // Let's show the bottom right of the layer, so we know the texture wasn't |
| // just cut off. |
| layer->SetPosition( |
| gfx::PointF(-10.f * scale + 400.f, -400.f * scale + 400.f)); |
| |
| gfx::Transform scale_transform; |
| scale_transform.Scale(scale, scale); |
| layer->SetTransform(scale_transform); |
| |
| pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); |
| |
| RunPixelTest(background, |
| base::FilePath(use_skia_vulkan() |
| ? FILE_PATH_LITERAL("spiral_64_scale_vk.png") |
| : FILE_PATH_LITERAL("spiral_64_scale.png"))); |
| } |
| |
| class LayerTreeHostOverlayScrollbarsPixelTest |
| : public LayerTreeHostScrollbarsPixelTest { |
| protected: |
| LayerTreeHostOverlayScrollbarsPixelTest() = default; |
| |
| void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { |
| LayerImpl* layer = host_impl->active_tree()->LayerById(scrollbar_layer_id_); |
| ToScrollbarLayer(layer)->SetThumbThicknessScaleFactor(thickness_scale_); |
| } |
| |
| int scrollbar_layer_id_; |
| float thickness_scale_; |
| }; |
| |
| class PaintedOverlayScrollbar : public FakeScrollbar { |
| public: |
| PaintedOverlayScrollbar() { |
| set_should_paint(true); |
| set_has_thumb(true); |
| set_orientation(ScrollbarOrientation::VERTICAL); |
| set_is_overlay(true); |
| set_thumb_size(gfx::Size(15, 50)); |
| set_track_rect(gfx::Rect(0, 0, 15, 400)); |
| } |
| |
| void PaintPart(PaintCanvas* canvas, |
| ScrollbarPart part, |
| const gfx::Rect& rect) override { |
| // The outside of the rect will be painted with a 1 pixel black, red, then |
| // blue border. The inside will be solid blue. This will allow the test to |
| // ensure that scaling the thumb doesn't scale the border at all. Note |
| // that the inside of the border must be the same color as the center tile |
| // to prevent an interpolation from being applied. |
| PaintFlags flags; |
| flags.setStyle(PaintFlags::kFill_Style); |
| flags.setStrokeWidth(SkIntToScalar(1)); |
| flags.setColor(SK_ColorBLACK); |
| |
| gfx::Rect inset_rect = rect; |
| canvas->drawRect(RectToSkRect(inset_rect), flags); |
| |
| flags.setColor(SK_ColorRED); |
| inset_rect.Inset(1); |
| canvas->drawRect(RectToSkRect(inset_rect), flags); |
| |
| flags.setColor(SK_ColorBLUE); |
| inset_rect.Inset(1); |
| canvas->drawRect(RectToSkRect(inset_rect), flags); |
| } |
| |
| bool UsesNinePatchThumbResource() const override { return true; } |
| gfx::Size NinePatchThumbCanvasSize() const override { |
| return gfx::Size(7, 7); |
| } |
| gfx::Rect NinePatchThumbAperture() const override { |
| return gfx::Rect(3, 3, 1, 1); |
| } |
| |
| private: |
| ~PaintedOverlayScrollbar() override = default; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| LayerTreeHostOverlayScrollbarsPixelTest, |
| ::testing::ValuesIn(viz::GetGpuRendererTypes()), |
| ::testing::PrintToStringParamName()); |
| |
| // viz::GetGpuRendererTypes() can return an empty list on some platforms. |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( |
| LayerTreeHostOverlayScrollbarsPixelTest); |
| |
| // Simulate increasing the thickness of a painted overlay scrollbar. Ensure that |
| // the scrollbar border remains crisp. |
| TEST_P(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledUp) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedOverlayScrollbar>(); |
| scoped_refptr<PaintedOverlayScrollbarLayer> layer = |
| PaintedOverlayScrollbarLayer::Create(std::move(scrollbar)); |
| |
| scrollbar_layer_id_ = layer->id(); |
| thickness_scale_ = 5.f; |
| |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(10, 300)); |
| background->AddChild(layer); |
| |
| layer->SetPosition(gfx::PointF(185, 10)); |
| |
| RunPixelTest( |
| background, |
| base::FilePath(FILE_PATH_LITERAL("overlay_scrollbar_scaled_up.png"))); |
| } |
| |
| // Simulate decreasing the thickness of a painted overlay scrollbar. Ensure that |
| // the scrollbar border remains crisp. |
| TEST_P(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledDown) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); |
| |
| auto scrollbar = base::MakeRefCounted<PaintedOverlayScrollbar>(); |
| scoped_refptr<PaintedOverlayScrollbarLayer> layer = |
| PaintedOverlayScrollbarLayer::Create(std::move(scrollbar)); |
| |
| scrollbar_layer_id_ = layer->id(); |
| thickness_scale_ = 0.4f; |
| |
| layer->SetIsDrawable(true); |
| layer->SetBounds(gfx::Size(10, 300)); |
| background->AddChild(layer); |
| |
| layer->SetPosition(gfx::PointF(185, 10)); |
| |
| RunPixelTest( |
| background, |
| base::FilePath(FILE_PATH_LITERAL("overlay_scrollbar_scaled_down.png"))); |
| } |
| |
| } // namespace |
| } // namespace cc |
| |
| #endif // BUILDFLAG(IS_ANDROID) |