Commit 8f1a40b9 authored by calamity@chromium.org's avatar calamity@chromium.org

Implement new bookmark app icon generation.

This CL changes the generation of bookmark apps to create an icon that
consists of the favicon's dominant color as a background with the first
letter of the app's domain painted over it.

The letter is either black or white depending on the luminance of the
background.

This CL also turns on generation of 2x icons for 32 and 48 px icons.

BUG=315632

Review URL: https://codereview.chromium.org/246333006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269745 0039d316-1c4b-4281-b951-d872f2087c98
parent 31b2ea59
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/browser/extensions/bookmark_app_helper.h" #include "chrome/browser/extensions/bookmark_app_helper.h"
#include <cctype>
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/crx_installer.h"
...@@ -19,15 +21,81 @@ ...@@ -19,15 +21,81 @@
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/icons_handler.h" #include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/url_pattern.h" #include "extensions/common/url_pattern.h"
#include "grit/platform_locale_settings.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "skia/ext/image_operations.h" #include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h" #include "skia/ext/platform_canvas.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_analysis.h" #include "ui/gfx/color_analysis.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h" #include "ui/gfx/image/image_family.h"
#include "ui/gfx/rect.h"
namespace { namespace {
// Overlays a shortcut icon over the bottom left corner of a given image.
class GeneratedIconImageSource : public gfx::CanvasImageSource {
public:
explicit GeneratedIconImageSource(char letter, SkColor color, int output_size)
: gfx::CanvasImageSource(gfx::Size(output_size, output_size), false),
letter_(letter),
color_(color),
output_size_(output_size) {}
virtual ~GeneratedIconImageSource() {}
private:
// gfx::CanvasImageSource overrides:
virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
const unsigned char kLuminanceThreshold = 190;
const int icon_size = output_size_ * 3 / 4;
const int icon_inset = output_size_ / 8;
const size_t border_radius = output_size_ / 16;
const size_t font_size = output_size_ * 7 / 16;
std::string font_name =
l10n_util::GetStringUTF8(IDS_SANS_SERIF_FONT_FAMILY);
#if defined(OS_CHROMEOS)
const std::string kChromeOSFontFamily = "Noto Sans";
font_name = kChromeOSFontFamily;
#endif
// Draw a rounded rect of the given |color|.
SkPaint background_paint;
background_paint.setFlags(SkPaint::kAntiAlias_Flag);
background_paint.setColor(color_);
gfx::Rect icon_rect(icon_inset, icon_inset, icon_size, icon_size);
canvas->DrawRoundRect(icon_rect, border_radius, background_paint);
// The text rect's size needs to be odd to center the text correctly.
gfx::Rect text_rect(icon_inset, icon_inset, icon_size + 1, icon_size + 1);
// Draw the letter onto the rounded rect. The letter's color depends on the
// luminance of |color|.
unsigned char luminance = color_utils::GetLuminanceForColor(color_);
canvas->DrawStringRectWithFlags(
base::string16(1, std::toupper(letter_)),
gfx::FontList(gfx::Font(font_name, font_size)),
luminance > kLuminanceThreshold ? SK_ColorBLACK : SK_ColorWHITE,
text_rect,
gfx::Canvas::TEXT_ALIGN_CENTER);
}
char letter_;
SkColor color_;
int output_size_;
DISALLOW_COPY_AND_ASSIGN(GeneratedIconImageSource);
};
void OnIconsLoaded( void OnIconsLoaded(
WebApplicationInfo web_app_info, WebApplicationInfo web_app_info,
const base::Callback<void(const WebApplicationInfo&)> callback, const base::Callback<void(const WebApplicationInfo&)> callback,
...@@ -85,86 +153,18 @@ std::map<int, SkBitmap> BookmarkAppHelper::ConstrainBitmapsToSizes( ...@@ -85,86 +153,18 @@ std::map<int, SkBitmap> BookmarkAppHelper::ConstrainBitmapsToSizes(
} }
// static // static
void BookmarkAppHelper::GenerateContainerIcon(std::map<int, SkBitmap>* bitmaps, void BookmarkAppHelper::GenerateIcon(std::map<int, SkBitmap>* bitmaps,
int output_size) { int output_size,
std::map<int, SkBitmap>::const_iterator it = SkColor color,
bitmaps->lower_bound(output_size); char letter) {
// Do nothing if there is no icon smaller than the desired size or there is // Do nothing if there is already an icon of |output_size|.
// already an icon of |output_size|. if (bitmaps->count(output_size))
if (it == bitmaps->begin() || bitmaps->count(output_size))
return; return;
--it; gfx::ImageSkia icon_image(
// This is the biggest icon smaller than |output_size|. new GeneratedIconImageSource(letter, color, output_size),
const SkBitmap& base_icon = it->second; gfx::Size(output_size, output_size));
icon_image.bitmap()->deepCopyTo(&(*bitmaps)[output_size]);
const size_t kBorderRadius = 5;
const size_t kColorStripHeight = 3;
const SkColor kBorderColor = 0xFFD5D5D5;
const SkColor kBackgroundColor = 0xFFFFFFFF;
// Create a separate canvas for the color strip.
scoped_ptr<SkCanvas> color_strip_canvas(
skia::CreateBitmapCanvas(output_size, output_size, false));
DCHECK(color_strip_canvas);
// Draw a rounded rect of the |base_icon|'s dominant color.
SkPaint color_strip_paint;
color_utils::GridSampler sampler;
color_strip_paint.setFlags(SkPaint::kAntiAlias_Flag);
color_strip_paint.setColor(color_utils::CalculateKMeanColorOfPNG(
gfx::Image::CreateFrom1xBitmap(base_icon).As1xPNGBytes(),
100,
665,
&sampler));
color_strip_canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
kBorderRadius,
kBorderRadius,
color_strip_paint);
// Erase the top of the rounded rect to leave a color strip.
SkPaint clear_paint;
clear_paint.setColor(SK_ColorTRANSPARENT);
clear_paint.setXfermodeMode(SkXfermode::kSrc_Mode);
color_strip_canvas->drawRect(
SkRect::MakeWH(output_size, output_size - kColorStripHeight),
clear_paint);
// Draw each element to an output canvas.
scoped_ptr<SkCanvas> canvas(
skia::CreateBitmapCanvas(output_size, output_size, false));
DCHECK(canvas);
// Draw the background.
SkPaint background_paint;
background_paint.setColor(kBackgroundColor);
background_paint.setFlags(SkPaint::kAntiAlias_Flag);
canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
kBorderRadius,
kBorderRadius,
background_paint);
// Draw the color strip.
canvas->drawBitmap(
color_strip_canvas->getDevice()->accessBitmap(false), 0, 0);
// Draw the border.
SkPaint border_paint;
border_paint.setColor(kBorderColor);
border_paint.setStyle(SkPaint::kStroke_Style);
border_paint.setFlags(SkPaint::kAntiAlias_Flag);
canvas->drawRoundRect(SkRect::MakeWH(output_size, output_size),
kBorderRadius,
kBorderRadius,
border_paint);
// Draw the centered base icon to the output canvas.
canvas->drawBitmap(base_icon,
(output_size - base_icon.width()) / 2,
(output_size - base_icon.height()) / 2);
const SkBitmap& generated_icon = canvas->getDevice()->accessBitmap(false);
generated_icon.deepCopyTo(&(*bitmaps)[output_size]);
} }
BookmarkAppHelper::BookmarkAppHelper(ExtensionService* service, BookmarkAppHelper::BookmarkAppHelper(ExtensionService* service,
...@@ -253,16 +253,42 @@ void BookmarkAppHelper::OnIconsDownloaded( ...@@ -253,16 +253,42 @@ void BookmarkAppHelper::OnIconsDownloaded(
// Only generate icons if larger icons don't exist. This means the app // Only generate icons if larger icons don't exist. This means the app
// launcher and the taskbar will do their best downsizing large icons and // launcher and the taskbar will do their best downsizing large icons and
// these container icons are only generated as a last resort against upscaling // these icons are only generated as a last resort against upscaling a smaller
// a smaller icon. // icon.
if (resized_bitmaps.lower_bound(*generate_sizes.rbegin()) == if (resized_bitmaps.lower_bound(*generate_sizes.rbegin()) ==
resized_bitmaps.end()) { resized_bitmaps.end()) {
// Generate these from biggest to smallest so we don't end up with GURL app_url = web_app_info_.app_url;
// concentric container icons.
for (std::set<int>::const_reverse_iterator it = generate_sizes.rbegin(); // The letter that will be painted on the generated icon.
it != generate_sizes.rend(); char icon_letter = ' ';
std::string domain_and_registry(
net::registry_controlled_domains::GetDomainAndRegistry(
app_url,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
if (!domain_and_registry.empty()) {
icon_letter = domain_and_registry[0];
} else if (!app_url.host().empty()) {
icon_letter = app_url.host()[0];
}
// The color that will be used for the icon's background.
SkColor background_color = SK_ColorBLACK;
if (resized_bitmaps.size()) {
color_utils::GridSampler sampler;
background_color = color_utils::CalculateKMeanColorOfPNG(
gfx::Image::CreateFrom1xBitmap(resized_bitmaps.begin()->second)
.As1xPNGBytes(),
100,
568,
&sampler);
}
for (std::set<int>::const_iterator it = generate_sizes.begin();
it != generate_sizes.end();
++it) { ++it) {
GenerateContainerIcon(&resized_bitmaps, *it); GenerateIcon(&resized_bitmaps, *it, background_color, icon_letter);
// Also generate the 2x resource for this size.
GenerateIcon(&resized_bitmaps, *it * 2, background_color, icon_letter);
} }
} }
......
...@@ -52,11 +52,12 @@ class BookmarkAppHelper : public content::NotificationObserver { ...@@ -52,11 +52,12 @@ class BookmarkAppHelper : public content::NotificationObserver {
const std::set<int>& sizes); const std::set<int>& sizes);
// Adds a square container icon of |output_size| pixels to |bitmaps| by // Adds a square container icon of |output_size| pixels to |bitmaps| by
// centering the biggest smaller icon in |bitmaps| and drawing a rounded // drawing the given |letter| into a rounded background of |color|.
// rectangle with strip of the that icon's dominant color at the bottom.
// Does nothing if an icon of |output_size| already exists in |bitmaps|. // Does nothing if an icon of |output_size| already exists in |bitmaps|.
static void GenerateContainerIcon(std::map<int, SkBitmap>* bitmaps, static void GenerateIcon(std::map<int, SkBitmap>* bitmaps,
int output_size); int output_size,
SkColor color,
char letter);
// Begins the asynchronous bookmark app creation. // Begins the asynchronous bookmark app creation.
void Create(const CreateBookmarkAppCallback& callback); void Create(const CreateBookmarkAppCallback& callback);
......
...@@ -269,30 +269,6 @@ TEST_F(BookmarkAppHelperTest, ConstrainBitmapsToSizes) { ...@@ -269,30 +269,6 @@ TEST_F(BookmarkAppHelperTest, ConstrainBitmapsToSizes) {
} }
} }
TEST_F(BookmarkAppHelperTest, GenerateIcons) {
{
// The 32x32 icon should be generated from the 16x16 icon.
std::map<int, SkBitmap> bitmaps;
bitmaps[16] = CreateSquareBitmapWithColor(16, SK_ColorRED);
BookmarkAppHelper::GenerateContainerIcon(&bitmaps, 32);
EXPECT_EQ(1u, bitmaps.count(32));
EXPECT_EQ(32, bitmaps[32].width());
}
{
// The 32x32 icon should not be generated because no smaller icon exists.
std::map<int, SkBitmap> bitmaps;
bitmaps[48] = CreateSquareBitmapWithColor(48, SK_ColorRED);
BookmarkAppHelper::GenerateContainerIcon(&bitmaps, 32);
EXPECT_EQ(0u, bitmaps.count(32));
}
{
// The 32x32 icon should not be generated with no base icons.
std::map<int, SkBitmap> bitmaps;
BookmarkAppHelper::GenerateContainerIcon(&bitmaps, 32);
EXPECT_EQ(0u, bitmaps.count(32));
}
}
TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) { TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) {
EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://www.chromium.org"))); EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://www.chromium.org")));
EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("http://www.chromium.org/path"))); EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("http://www.chromium.org/path")));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment