0

Add additional tests for the software cursor

Follow-up from https://crrev.com/c/4310794. Test that the software
cursor representation, size, hotspot and window are correct.

Bug: 1229231
Change-Id: If113795abefd6ccde62946d1b1985eb58a6385ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4255949
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Commit-Queue: Henrique Ferreiro <hferreiro@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1119833}
This commit is contained in:
Henrique Ferreiro
2023-03-21 09:47:56 +00:00
committed by Chromium LUCI CQ
parent 0bcb7b459b
commit 35f4dce600
2 changed files with 114 additions and 20 deletions

@ -485,6 +485,10 @@ void CursorWindowController::UpdateCursorImage() {
image =
*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
}
// Use `gfx::ToFlooredPoint` as `ImageSkiaRep::GetWidth` is implemented as
// `return static_cast<int>(pixel_width() / scale());`.
hot_point_ = gfx::ToFlooredPoint(
gfx::ConvertPointToDips(hot_point_in_physical_pixels, cursor_scale));
gfx::ImageSkia resized = image;
@ -499,8 +503,7 @@ void CursorWindowController::UpdateCursorImage() {
resized = gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::ResizeMethod::RESIZE_BEST,
gfx::ScaleToCeiledSize(image.size(), rescale));
hot_point_in_physical_pixels =
gfx::ScaleToCeiledPoint(hot_point_in_physical_pixels, rescale);
hot_point_ = gfx::ScaleToCeiledPoint(hot_point_, rescale);
}
if (cursor_color_ != kDefaultCursorColor) {
@ -511,10 +514,6 @@ void CursorWindowController::UpdateCursorImage() {
delegate_->SetCursorImage(resized.size(), resized);
// TODO(danakj): Should this be rounded? Or kept as a floating point?
hot_point_ = gfx::ToFlooredPoint(
gfx::ConvertPointToDips(hot_point_in_physical_pixels, cursor_scale));
if (cursor_view_widget_) {
static_cast<cursor::CursorView*>(cursor_view_widget_->GetContentsView())
->SetCursorImage(delegate_->cursor_image(), delegate_->size(),

@ -4,6 +4,8 @@
#include "ash/display/cursor_window_controller.h"
#include <utility>
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/constants/ash_constants.h"
#include "ash/constants/ash_pref_names.h"
@ -14,7 +16,9 @@
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "components/prefs/pref_service.h"
#include "ui/aura/client/cursor_shape_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
@ -22,19 +26,28 @@
#include "ui/base/layout.h"
#include "ui/base/resource/mock_resource_bundle_delegate.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/resource_scale_factor.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/cursor_manager.h"
namespace ash {
using ::ui::mojom::CursorType;
class CursorWindowControllerTest : public AshTestBase {
public:
CursorWindowControllerTest() = default;
@ -57,7 +70,7 @@ class CursorWindowControllerTest : public AshTestBase {
SetCursorCompositionEnabled(true);
}
ui::mojom::CursorType GetCursorType() const {
CursorType GetCursorType() const {
return cursor_window_controller_->cursor_.type();
}
@ -116,7 +129,7 @@ TEST_F(CursorWindowControllerTest, MoveToDifferentDisplay) {
EXPECT_TRUE(primary_root->Contains(GetCursorWindow()));
EXPECT_EQ(primary_display_id, GetCursorDisplayId());
EXPECT_EQ(ui::mojom::CursorType::kNull, GetCursorType());
EXPECT_EQ(CursorType::kNull, GetCursorType());
gfx::Point hot_point = GetCursorHotPoint();
EXPECT_EQ(gfx::Point(4, 4), hot_point);
gfx::Rect cursor_bounds = GetCursorWindow()->GetBoundsInScreen();
@ -140,7 +153,7 @@ TEST_F(CursorWindowControllerTest, MoveToDifferentDisplay) {
EXPECT_TRUE(secondary_root->Contains(GetCursorWindow()));
EXPECT_EQ(secondary_display_id, GetCursorDisplayId());
EXPECT_EQ(ui::mojom::CursorType::kNull, GetCursorType());
EXPECT_EQ(CursorType::kNull, GetCursorType());
hot_point = GetCursorHotPoint();
EXPECT_EQ(gfx::Point(3, 3), hot_point);
cursor_bounds = GetCursorWindow()->GetBoundsInScreen();
@ -251,6 +264,92 @@ TEST_F(CursorWindowControllerTest, ScaleUsesCorrectAssets) {
ui::ResourceBundle::SwapSharedInstanceForTesting(original);
}
// Test different properties of the composited cursor with different device
// scale factors and zoom levels.
TEST_F(CursorWindowControllerTest, DSF) {
auto* const cursor_shape_client = aura::client::GetCursorShapeClient();
DCHECK(cursor_shape_client);
auto cursor_test = [&](ui::Cursor cursor, float size, float cursor_scale) {
const float dsf =
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
SCOPED_TRACE(testing::Message() << cursor.type() << " at scale " << dsf
<< " and size " << size);
cursor_window_controller()->SetCursor(cursor);
const absl::optional<ui::CursorData> cursor_data =
cursor_shape_client->GetCursorData(cursor);
DCHECK(cursor_data);
// Software cursors look blurry if they are resized by the window they are
// rendered in, instead of by `ImageSkia`. Make sure
// `CursorWindowController` creates the cursor in a way that a
// representation for the display's device scale factor can be directly
// obtained.
const gfx::ImageSkiaRep& rep = GetCursorImage().GetRepresentation(dsf);
EXPECT_EQ(rep.scale(), dsf);
const gfx::Size kOriginalCursorSize =
// ImageSkiaRep::GetWidth() uses static_cast<int>.
gfx::ToFlooredSize(gfx::ConvertSizeToDips(
gfx::SkISizeToSize(cursor_data->bitmaps[0].dimensions()),
cursor_scale));
const gfx::Size kCursorSize =
size != 0 ? gfx::Size(size, size) : kOriginalCursorSize;
EXPECT_EQ(GetCursorImage().size(), kCursorSize);
// TODO(hferreiro): the cursor hotspot for non-custom cursors cannot be
// checked, since the software cursor uses `ImageSkia::MapToResourceScale`,
// and `CursorLoader::GetCursorData` uses
// `ui::GetSupportedResourceScaleFactor`, and 2x cursor hotspots are not
// just twice the 1x hotspots.
if (cursor.type() == CursorType::kCustom) {
const gfx::Point kHotspot = gfx::ToFlooredPoint(
gfx::ConvertPointToDips(cursor_data->hotspot, cursor_scale));
const float rescale =
static_cast<float>(kCursorSize.width()) / kOriginalCursorSize.width();
EXPECT_EQ(GetCursorHotPoint(),
gfx::ScaleToCeiledPoint(kHotspot, rescale));
}
// The cursor window should have the same size as the cursor.
EXPECT_EQ(GetCursorWindow()->bounds().size(), GetCursorImage().size());
};
auto* const cursor_manager = Shell::Get()->cursor_manager();
DCHECK(cursor_manager);
for (const float device_scale_factor : {1.0f, 1.5f, 2.0f, 2.5f}) {
for (const float zoom : {0.8f, 1.0f, 1.25f}) {
UpdateDisplay(
base::StringPrintf("1000x500*%f@%f", device_scale_factor, zoom));
const float dsf = display::Screen::GetScreen()
->GetPrimaryDisplay()
.device_scale_factor();
for (const int size : {0, 32, 64}) {
cursor_manager->SetCursorSize(size == 0 ? ui::CursorSize::kNormal
: ui::CursorSize::kLarge);
Shell::Get()->SetLargeCursorSizeInDip(size);
// Default cursor.
cursor_test(CursorType::kPointer, size,
// Use the nearest resource scale factor.
ui::GetScaleForResourceScaleFactor(
ui::GetSupportedResourceScaleFactor(dsf)));
// Custom cursor.
SkBitmap bitmap;
bitmap.allocN32Pixels(20, 20);
// Custom cursors are always scaled at the device scale factor. See
// `WebCursor::GetNativeCursor`.
cursor_test(ui::Cursor::NewCustom(bitmap, gfx::Point(10, 10), dsf),
size, dsf);
}
}
}
}
// Test that cursor compositing is enabled if at least one of the features that
// use it is enabled.
TEST_F(CursorWindowControllerTest, ShouldEnableCursorCompositing) {
@ -282,23 +381,19 @@ TEST_F(CursorWindowControllerTest, CursorColoringSpotCheck) {
gfx::NativeCursor cursor;
} kTestCases[] = {
// Cursors should still have white.
{SK_ColorMAGENTA, SK_ColorBLUE, SK_ColorWHITE,
ui::mojom::CursorType::kHand},
{SK_ColorBLUE, SK_ColorMAGENTA, SK_ColorWHITE,
ui::mojom::CursorType::kCell},
{SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE,
ui::mojom::CursorType::kNoDrop},
{SK_ColorMAGENTA, SK_ColorBLUE, SK_ColorWHITE, CursorType::kHand},
{SK_ColorBLUE, SK_ColorMAGENTA, SK_ColorWHITE, CursorType::kCell},
{SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, CursorType::kNoDrop},
// Also cursors should still have transparent.
{SK_ColorRED, SK_ColorGREEN, SK_ColorTRANSPARENT,
ui::mojom::CursorType::kPointer},
{SK_ColorRED, SK_ColorGREEN, SK_ColorTRANSPARENT, CursorType::kPointer},
// The no drop cursor has red in it, check it's still there:
// Most of the cursor should be colored, but the red part shouldn't be
// re-colored.
{SK_ColorBLUE, SK_ColorGREEN, SkColorSetRGB(173, 8, 8),
ui::mojom::CursorType::kNoDrop},
CursorType::kNoDrop},
// Similarly, the copy cursor has green in it.
{SK_ColorBLUE, SK_ColorRED, SkColorSetRGB(19, 137, 16),
ui::mojom::CursorType::kCopy},
CursorType::kCopy},
};
for (const auto& test : kTestCases) {