| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/apps/icon_standardizer.h" |
| |
| #include "base/compiler_specific.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| |
| namespace { |
| |
| constexpr int kIconSize = 64; |
| |
| constexpr float kMaxCircleIconSize = 176.0f / 192.0f; |
| constexpr float kStandardCircleRadius = kIconSize * kMaxCircleIconSize / 2.0f; |
| |
| // Returns whether the bitmaps are equal. |
| bool AreBitmapsEqual(const SkBitmap& first_bitmap, |
| const SkBitmap& second_bitmap) { |
| if (first_bitmap.width() != second_bitmap.width() || |
| first_bitmap.height() != second_bitmap.height() || |
| first_bitmap.colorType() != second_bitmap.colorType()) { |
| return false; |
| } |
| |
| for (int y = 0; y < first_bitmap.height(); ++y) { |
| for (int x = 0; x < first_bitmap.width(); ++x) { |
| if (first_bitmap.getColor(x, y) != second_bitmap.getColor(x, y)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool DoesIconHaveWhiteBackgroundCircle(const SkBitmap& bitmap) { |
| const int y = kIconSize / 2; |
| for (int x = 0; x < bitmap.width(); ++x) { |
| if (bitmap.getColor(x, y) == SK_ColorWHITE) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| using CreateStandardIconTest = testing::Test; |
| |
| // Test that a square icon gets scaled down and drawn on top of a circular |
| // background when converted to a standard icon. |
| TEST_F(CreateStandardIconTest, SquareIconToStandardIcon) { |
| SkBitmap square_icon_bitmap; |
| square_icon_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| square_icon_bitmap.eraseColor(SK_ColorRED); |
| |
| gfx::ImageSkia standard_icon = apps::CreateStandardIconImage( |
| gfx::ImageSkia::CreateFrom1xBitmap(square_icon_bitmap)); |
| |
| // Create |test_standard_bitmap| which will be a manually created standard |
| // icon, with background circle and a scaled down square icon inside. |
| SkBitmap test_standard_bitmap; |
| test_standard_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| test_standard_bitmap.eraseColor(SK_ColorTRANSPARENT); |
| |
| SkCanvas canvas(test_standard_bitmap); |
| |
| SkPaint paint_background_circle; |
| paint_background_circle.setAntiAlias(true); |
| paint_background_circle.setColor(SK_ColorWHITE); |
| paint_background_circle.setStyle(SkPaint::kFill_Style); |
| canvas.drawCircle( |
| SkPoint::Make((kIconSize - 1) / 2.0f, (kIconSize - 1) / 2.0f), |
| kStandardCircleRadius, paint_background_circle); |
| |
| const SkBitmap scaled_bitmap = skia::ImageOperations::Resize( |
| square_icon_bitmap, skia::ImageOperations::RESIZE_BEST, 36, 36); |
| canvas.drawImage(scaled_bitmap.asImage(), 14, 14); |
| |
| EXPECT_TRUE(DoesIconHaveWhiteBackgroundCircle(*standard_icon.bitmap())); |
| EXPECT_TRUE(AreBitmapsEqual(*standard_icon.bitmap(), test_standard_bitmap)); |
| } |
| |
| // Test that a large circular icon gets scaled down when converted to a standard |
| // icon. |
| TEST_F(CreateStandardIconTest, CircularIconToStandardIcon) { |
| // Create a bitmap for drawing a red circle as a placeholder circular icon. |
| SkBitmap circle_icon_bitmap; |
| circle_icon_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| circle_icon_bitmap.eraseColor(SK_ColorTRANSPARENT); |
| |
| SkCanvas canvas(circle_icon_bitmap); |
| SkPaint paint_circle_icon; |
| paint_circle_icon.setAntiAlias(true); |
| paint_circle_icon.setColor(SK_ColorRED); |
| paint_circle_icon.setStyle(SkPaint::kFill_Style); |
| canvas.drawCircle(SkPoint::Make(kIconSize / 2, kIconSize / 2), kIconSize / 2, |
| paint_circle_icon); |
| |
| // Get the standard icon version of the red circle icon. |
| gfx::ImageSkia generated_standard_icon = apps::CreateStandardIconImage( |
| gfx::ImageSkia::CreateFromBitmap(circle_icon_bitmap, 2.0f)); |
| |
| // Scale the bitmap to fit the size of a standardized circle icon. |
| SkBitmap scaled_bitmap = skia::ImageOperations::Resize( |
| circle_icon_bitmap, skia::ImageOperations::RESIZE_BEST, 58, 58); |
| |
| // Draw the |scaled_bitmap| to |manually_scaled_bitmap| to ensure the size |
| // of the bitmap is the same as the |generated_standard_icon| for comparison. |
| SkBitmap manually_scaled_bitmap; |
| manually_scaled_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| manually_scaled_bitmap.eraseColor(SK_ColorTRANSPARENT); |
| SkCanvas canvas2(manually_scaled_bitmap); |
| canvas2.drawImage(scaled_bitmap.asImage(), 3, 3); |
| |
| EXPECT_FALSE( |
| DoesIconHaveWhiteBackgroundCircle(*generated_standard_icon.bitmap())); |
| EXPECT_TRUE(AreBitmapsEqual(*generated_standard_icon.bitmap(), |
| manually_scaled_bitmap)); |
| } |
| |
| // Test that a circle icon that is already standard size, keeps the same size |
| // when standardized. |
| TEST_F(CreateStandardIconTest, StandardCircularIconToStandardIcon) { |
| // Create a bitmap with a red circle as a placeholder circular icon. |
| SkBitmap circle_icon_bitmap; |
| circle_icon_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| circle_icon_bitmap.eraseColor(SK_ColorTRANSPARENT); |
| |
| SkCanvas canvas(circle_icon_bitmap); |
| SkPaint paint_circle_icon; |
| paint_circle_icon.setAntiAlias(true); |
| paint_circle_icon.setColor(SK_ColorRED); |
| paint_circle_icon.setStyle(SkPaint::kFill_Style); |
| canvas.drawCircle(SkPoint::Make(kIconSize / 2.0f, kIconSize / 2.0f), |
| kStandardCircleRadius, paint_circle_icon); |
| |
| // Get the standard icon version of the red circle icon. |
| gfx::ImageSkia standard_icon = apps::CreateStandardIconImage( |
| gfx::ImageSkia::CreateFromBitmap(circle_icon_bitmap, 2.0f)); |
| |
| EXPECT_FALSE(DoesIconHaveWhiteBackgroundCircle(*standard_icon.bitmap())); |
| EXPECT_TRUE(AreBitmapsEqual(*standard_icon.bitmap(), circle_icon_bitmap)); |
| } |
| |
| // Test that a circle icon that has an extra opaque area near the outside of the |
| // circle will have a background circle added when standardized. |
| TEST_F(CreateStandardIconTest, AlmostCircularIconToStandardIcon) { |
| // Create a bitmap with a red circle as a placeholder circular icon. |
| SkBitmap almost_circle_icon_bitmap; |
| almost_circle_icon_bitmap.allocN32Pixels(kIconSize, kIconSize); |
| almost_circle_icon_bitmap.eraseColor(SK_ColorTRANSPARENT); |
| |
| SkCanvas canvas(almost_circle_icon_bitmap); |
| SkPaint paint_flags; |
| paint_flags.setAntiAlias(true); |
| paint_flags.setColor(SK_ColorRED); |
| paint_flags.setStyle(SkPaint::kFill_Style); |
| canvas.drawCircle(SkPoint::Make(kIconSize / 2.0f, kIconSize / 2.0f), |
| kStandardCircleRadius, paint_flags); |
| |
| // Draw a small square partially outside of the main red circle. |
| canvas.drawRect(SkRect::MakeXYWH(6, 6, 15, 15), paint_flags); |
| |
| // Get the standard icon version of the almost red circle icon. |
| gfx::ImageSkia standard_icon = apps::CreateStandardIconImage( |
| gfx::ImageSkia::CreateFromBitmap(almost_circle_icon_bitmap, 2.0f)); |
| |
| EXPECT_TRUE(DoesIconHaveWhiteBackgroundCircle(*standard_icon.bitmap())); |
| EXPECT_FALSE( |
| AreBitmapsEqual(*standard_icon.bitmap(), almost_circle_icon_bitmap)); |
| } |