Skip to content

Commit

Permalink
[Extensions] Added new function to evaluate icon visibility.
Browse files Browse the repository at this point in the history
The new functions draw the icon into another bitmap, combining it with the background color to determine the visibility of the resulting icon.

Bug: 805600
Change-Id: I592ed5342d6eb3bfb6c36a4da7148280ed2a0f85
Reviewed-on: https://chromium-review.googlesource.com/c/1297398
Commit-Queue: David Bertoni <dbertoni@chromium.org>
Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602523}
  • Loading branch information
David Bertoni authored and Commit Bot committed Oct 24, 2018
1 parent ebc3045 commit ca53cbe
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
48 changes: 46 additions & 2 deletions extensions/common/image_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#include "base/strings/stringprintf.h"
#include "third_party/re2/src/re2/re2.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/utils/SkParse.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_utils.h"
Expand Down Expand Up @@ -194,9 +196,51 @@ bool IsIconAtPathSufficientlyVisible(const base::FilePath& path) {
SkBitmap icon;
if (!LoadPngFromFile(path, &icon)) {
return false;
} else {
return image_util::IsIconSufficientlyVisible(icon);
}
return IsIconSufficientlyVisible(icon);
}

bool IsRenderedIconSufficientlyVisible(const SkBitmap& icon,
SkColor background_color) {
// If any of a pixel's RGB values is greater than this number, the pixel is
// considered visible.
constexpr unsigned int kThreshold = 15;
// The minimum "percent" of pixels that must be visible for the icon to be
// considered OK.
constexpr double kMinPercentVisiblePixels = 0.05;
const int total_pixels = icon.height() * icon.width();

// Draw the icon onto a canvas, then draw the background color onto the
// resulting bitmap, using SkBlendMode::kDifference. Then, check the RGB
// values against the threshold. Any pixel with a value greater than the
// threshold is considered visible.
SkBitmap bitmap;
bitmap.allocN32Pixels(icon.width(), icon.height());
bitmap.eraseColor(background_color);
SkCanvas offscreen(bitmap);
offscreen.drawImage(SkImage::MakeFromBitmap(icon), 0, 0);
offscreen.drawColor(background_color, SkBlendMode::kDifference);
int visible_pixels = 0;
for (int x = 0; x < icon.width(); ++x) {
for (int y = 0; y < icon.height(); ++y) {
SkColor pixel = bitmap.getColor(x, y);
if (SkColorGetR(pixel) > kThreshold || SkColorGetB(pixel) > kThreshold ||
SkColorGetG(pixel) > kThreshold) {
++visible_pixels;
}
}
}
return static_cast<double>(visible_pixels) / total_pixels >=
kMinPercentVisiblePixels;
}

bool IsRenderedIconAtPathSufficientlyVisible(const base::FilePath& path,
SkColor background_color) {
SkBitmap icon;
if (!LoadPngFromFile(path, &icon)) {
return false;
}
return IsRenderedIconSufficientlyVisible(icon, background_color);
}

bool LoadPngFromFile(const base::FilePath& path, SkBitmap* dst) {
Expand Down
11 changes: 11 additions & 0 deletions extensions/common/image_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ bool IsIconSufficientlyVisible(const SkBitmap& bitmap);
// context.
bool IsIconAtPathSufficientlyVisible(const base::FilePath& path);

// Renders the icon bitmap onto another bitmap, combining it with the specified
// background color, then determines whether the rendered icon is sufficiently
// visible against the background.
bool IsRenderedIconSufficientlyVisible(const SkBitmap& bitmap,
SkColor background_color);

// Returns whether an icon image is considered to be visible in its display
// context, according to the previous function.
bool IsRenderedIconAtPathSufficientlyVisible(const base::FilePath& path,
SkColor background_color);

// Load a PNG image from a file into the destination bitmap.
bool LoadPngFromFile(const base::FilePath& path, SkBitmap* dst);

Expand Down
32 changes: 32 additions & 0 deletions extensions/common/image_util_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,34 +190,66 @@ TEST(ImageUtilTest, IsIconSufficientlyVisible) {
SkBitmap transparent_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &transparent_icon));
EXPECT_FALSE(image_util::IsIconSufficientlyVisible(transparent_icon));
EXPECT_FALSE(image_util::IsRenderedIconSufficientlyVisible(transparent_icon,
SK_ColorWHITE));
}
{
// Test with an icon that has one opaque pixel.
icon_path = test_dir.AppendASCII("one_pixel_opaque_icon.png");
SkBitmap visible_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
EXPECT_FALSE(image_util::IsIconSufficientlyVisible(visible_icon));
EXPECT_FALSE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
SK_ColorWHITE));
}
{
// Test with an icon that has one transparent pixel.
icon_path = test_dir.AppendASCII("one_pixel_transparent_icon.png");
SkBitmap visible_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
SK_ColorWHITE));
}
{
// Test with an icon that is completely opaque.
icon_path = test_dir.AppendASCII("opaque_icon.png");
SkBitmap visible_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
SK_ColorWHITE));
}
{
// Test with an icon that is rectangular.
icon_path = test_dir.AppendASCII("rectangle.png");
SkBitmap visible_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
SK_ColorWHITE));
}
{
// Test with a solid color icon that is completely opaque. Use the icon's
// color as the background color in the call to analyze its visibility.
// It should be invisible in this case.
icon_path = test_dir.AppendASCII("grey_21x21.png");
SkBitmap solid_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &solid_icon));
const SkColor pixel_color = solid_icon.getColor(0, 0);
EXPECT_FALSE(
image_util::IsRenderedIconSufficientlyVisible(solid_icon, pixel_color));
}
{
// Test with a two-color icon that is completely opaque. Use one of the
// icon's colors as the background color in the call to analyze its
// visibility. It should be visible in this case.
icon_path = test_dir.AppendASCII("two_color_21x21.png");
SkBitmap two_color_icon;
ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &two_color_icon));
const SkColor pixel_color = two_color_icon.getColor(0, 0);
EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(two_color_icon,
pixel_color));
}
}

Expand Down
Binary file added extensions/test/data/grey_21x21.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added extensions/test/data/two_color_21x21.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ca53cbe

Please sign in to comment.