[go: nahoru, domu]

Reland "[Rounded Display] Adding RoundedDisplayProvider class."

This is a reland of commit 5009a3fd61c6ee3a67995f6eea20adb5a2092386

Reason for revert: It was reverted because host_window was leaking.
It was leaking because it was removed from the window hierarchy.
We've fixed it by leaving it attached to the window hierarchy.

Original change's description:
> [Rounded Display] Adding RoundedDisplayProvider class.
>
> This class provides an API through which we can add software
> rounded corner to a display.
>
> Bug: 1262970
> Test: Covered by unittests
> Change-Id: I11368af7b6899aefc196f11138a4bab9a9957c5a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3900676
> Reviewed-by: Sean Kau <skau@chromium.org>
> Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
> Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1112427}

Bug: 1262970
Change-Id: I582b311ca5d7bd81f7c1484dcc7f83454fa7f7bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4309922
Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
Reviewed-by: Sean Kau <skau@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1113545}
diff --git a/ash/rounded_display/rounded_display_provider_unittest.cc b/ash/rounded_display/rounded_display_provider_unittest.cc
new file mode 100644
index 0000000..efab93a
--- /dev/null
+++ b/ash/rounded_display/rounded_display_provider_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/rounded_display/rounded_display_provider.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ash/rounded_display/rounded_display_gutter.h"
+#include "ash/rounded_display/rounded_display_provider_test_api.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
+
+namespace ash {
+namespace {
+
+using Gutters = std::vector<RoundedDisplayGutter*>;
+using RoundedCorner = RoundedDisplayGutter::RoundedCorner;
+using RoundedCornerPosition = RoundedDisplayGutter::RoundedCorner::Position;
+using ProviderStrategy = RoundedDisplayProvider::Strategy;
+
+constexpr ProviderStrategy kDefaultTestStrategy = ProviderStrategy::kScanout;
+
+gfx::RoundedCornersF CreateHorizontallyUniformRadii(int radius_a,
+                                                    int radius_b) {
+  return gfx::RoundedCornersF(radius_a, radius_a, radius_b, radius_b);
+}
+
+// The matcher matches a RoundedDisplayGutter that has the rounded corners of
+// `positions`.
+template <typename... Matchers>
+auto GutterWithMatchingCorners(Matchers&&... positions) {
+  return testing::ResultOf(
+      "positions",
+      [](const RoundedDisplayGutter* gutter) {
+        std::vector<RoundedCornerPosition> positions;
+        const std::vector<RoundedCorner>& corners = gutter->GetGutterCorners();
+        base::ranges::transform(
+            corners.begin(), corners.end(), std::back_inserter(positions),
+            [](const RoundedCorner& corner) { return corner.position(); });
+        return positions;
+      },
+      testing::UnorderedElementsAre(positions...));
+}
+
+class RoundedDisplayProviderTest : public AshTestBase {
+ public:
+  RoundedDisplayProviderTest() = default;
+
+  RoundedDisplayProviderTest(const RoundedDisplayProviderTest&) = delete;
+  RoundedDisplayProviderTest& operator=(const RoundedDisplayProviderTest&) =
+      delete;
+
+  ~RoundedDisplayProviderTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    // Create the rounded display provider for the primary display.
+    provider_ = RoundedDisplayProvider::Create(GetPrimaryDisplay().id());
+  }
+
+  void TearDown() override {
+    provider_.reset();
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  const display::Display& GetDisplay(int64_t display_id) {
+    return display_manager()->GetDisplayForId(display_id);
+  }
+
+  std::unique_ptr<RoundedDisplayProvider> provider_;
+};
+
+TEST_F(RoundedDisplayProviderTest, InitializeWithNonUniformRadii) {
+  RoundedDisplayProviderTestApi test_api(provider_.get());
+
+  const gfx::RoundedCornersF not_valid_radii(10, 11, 12, 12);
+
+  EXPECT_DCHECK_DEATH(
+      { provider_->Init(not_valid_radii, kDefaultTestStrategy); });
+}
+
+TEST_F(RoundedDisplayProviderTest, CorrectNumberOfGuttersAreProvided) {
+  RoundedDisplayProviderTestApi test_api(provider_.get());
+  const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+  // We expect 4 non-overlays and 2 overlay gutters to be created.
+  provider_->Init(radii, kDefaultTestStrategy);
+  EXPECT_EQ(test_api.GetGutters().size(), 6u);
+}
+
+TEST_F(RoundedDisplayProviderTest,
+       CorrectGutterCreatedForStrategy_ScanoutDirection) {
+  RoundedDisplayProviderTestApi test_api(provider_.get());
+  const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+  provider_->Init(radii, ProviderStrategy::kScanout);
+
+  const auto& gutters = test_api.GetGutters();
+
+  EXPECT_EQ(gutters.size(), 6u);
+
+  // Check that we have two overlay gutters that in the scanout direction.
+  EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+                           RoundedCornerPosition::kUpperLeft,
+                           RoundedCornerPosition::kUpperRight)));
+  EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+                           RoundedCornerPosition::kLowerLeft,
+                           RoundedCornerPosition::kLowerRight)));
+}
+
+TEST_F(RoundedDisplayProviderTest,
+       CorrectGutterCreatedForStrategy_OtherDirection) {
+  RoundedDisplayProviderTestApi test_api(provider_.get());
+  const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+  provider_->Init(radii, ProviderStrategy::kOther);
+
+  const auto& gutters = test_api.GetGutters();
+
+  EXPECT_EQ(gutters.size(), 6u);
+
+  // Check that we have two overlay gutters that in the scanout direction.
+  EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+                           RoundedCornerPosition::kUpperLeft,
+                           RoundedCornerPosition::kLowerLeft)));
+  // Right overlay gutter.
+  EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+                           RoundedCornerPosition::kUpperRight,
+                           RoundedCornerPosition::kLowerRight)));
+}
+
+class RoundedDisplayProviderSurfaceUpdateTest
+    : public RoundedDisplayProviderTest,
+      public ::testing::WithParamInterface<
+          std::tuple<std::string, std::string, bool>> {
+ public:
+  RoundedDisplayProviderSurfaceUpdateTest()
+      : initial_display_spec_(std::get<0>(GetParam())),
+        updated_display_spec_(std::get<1>(GetParam())),
+        expect_update_(std::get<2>(GetParam())) {}
+
+  RoundedDisplayProviderSurfaceUpdateTest(
+      const RoundedDisplayProviderSurfaceUpdateTest&) = delete;
+  RoundedDisplayProviderSurfaceUpdateTest& operator=(
+      const RoundedDisplayProviderSurfaceUpdateTest&) = delete;
+
+  ~RoundedDisplayProviderSurfaceUpdateTest() override = default;
+
+ protected:
+  std::string initial_display_spec_;
+  std::string updated_display_spec_;
+  bool expect_update_;
+};
+
+TEST_P(RoundedDisplayProviderSurfaceUpdateTest,
+       UpdatesSurfaceOnlyWhenNecessary) {
+  RoundedDisplayProviderTestApi test_api(provider_.get());
+
+  display::Display primary_display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+
+  auto display_id = primary_display.id();
+
+  UpdateDisplay(initial_display_spec_);
+
+  gfx::RoundedCornersF radii =
+      display_manager()->GetDisplayInfo(display_id).rounded_corners_radii();
+
+  provider_->Init(radii, kDefaultTestStrategy);
+
+  ASSERT_TRUE(provider_->UpdateRoundedDisplaySurface());
+
+  UpdateDisplay(updated_display_spec_);
+
+  Gutters before_update_gutters = test_api.GetGutters();
+  ASSERT_EQ(provider_->UpdateRoundedDisplaySurface(), expect_update_);
+  Gutters after_update_gutters = test_api.GetGutters();
+
+  // Confirm that we did not change gutters.
+  EXPECT_EQ(before_update_gutters, after_update_gutters);
+}
+
+const std::string kInitialDisplaySpec = "500x400~15";
+const std::string kInitialDisplaySpecWithRotation = "500x400/r~15";
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    RoundedDisplayProviderSurfaceUpdateTest,
+    testing::Values(
+        // If nothing changes, we should skip surface update.
+        std::make_tuple(kInitialDisplaySpec,
+                        "500x400~15",
+                        /*expect_update=*/false),
+        // Change in device scale factor, should only cause a surface update.
+        std::make_tuple(kInitialDisplaySpec,
+                        "500x400*2~15",
+                        /*expect_update=*/true),
+        // Further display rotation, should only cause a surface update.
+        std::make_tuple(kInitialDisplaySpecWithRotation,
+                        "500x400/u~15",
+                        /*expect_update=*/true),
+        // Multiple spec changes, should appropriately cause a surface update.
+        std::make_tuple(kInitialDisplaySpec,
+                        "500x400*2~15",
+                        /*expect_update=*/true)));
+
+}  // namespace
+}  // namespace ash