Commit 623b7070 authored by Alexey Baskakov's avatar Alexey Baskakov Committed by Commit Bot

WebApp: Extract BookmarkAppHelper's gfx stuff into a separate file.

Extract all the low-level skia/gfx/color_util stuff as
ResizeIconsAndGenerateMissing function.

This is a cut-and-paste CL, no behavior changes.

Notes:
- WebApplicationInfo::IconInfo becomes IconInfo. TODO for next CL:
Merge IconInfo and BitmapAndSource - they are essentially the same.

- BookmarkAppHelperExtensionServiceTest.LinkedAppIconsAreNotChanged test
was always broken (a bug in ValidateAllIconsWithURLsArePresent helper)
https://codereview.chromium.org/1066623008/patch/80001/90005

This is a re-land of:
https://chromium-review.googlesource.com/c/chromium/src/+/1166751
MSan issue solved, ouput parameters initialized everywhere:
SkColor generated_icon_color = SK_ColorTRANSPARENT;

TBR=ortuno@chromium.org

Bug: 860581
Change-Id: Ib26c765083e67e3a1c938a49994a5f5d5ecc8265
Reviewed-on: https://chromium-review.googlesource.com/1170665
Commit-Queue: Alexey Baskakov <loyso@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582491}
parent f708c466
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include <stddef.h> #include <stddef.h>
#include <cctype>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -46,7 +45,6 @@ ...@@ -46,7 +45,6 @@
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/origin_trials/chrome_origin_trial_policy.h" #include "chrome/common/origin_trials/chrome_origin_trial_policy.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "chrome/grit/platform_locale_settings.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
...@@ -61,21 +59,9 @@ ...@@ -61,21 +59,9 @@
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/url_pattern.h" #include "extensions/common/url_pattern.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/blink/public/common/manifest/web_display_mode.h" #include "third_party/blink/public/common/manifest/web_display_mode.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/gfx/canvas.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/geometry/rect.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image.h"
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut_mac.h" #include "chrome/browser/web_applications/extensions/web_app_extension_shortcut_mac.h"
...@@ -92,62 +78,6 @@ namespace extensions { ...@@ -92,62 +78,6 @@ namespace extensions {
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) {}
~GeneratedIconImageSource() override {}
private:
// gfx::CanvasImageSource overrides:
void Draw(gfx::Canvas* canvas) override {
const uint8_t kLumaThreshold = 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|.
cc::PaintFlags background_flags;
background_flags.setAntiAlias(true);
background_flags.setColor(color_);
gfx::Rect icon_rect(icon_inset, icon_inset, icon_size, icon_size);
canvas->DrawRoundRect(icon_rect, border_radius, background_flags);
// 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
// luma of |color|.
const uint8_t luma = color_utils::GetLuma(color_);
canvas->DrawStringRectWithFlags(
base::string16(1, std::toupper(letter_)),
gfx::FontList(gfx::Font(font_name, font_size)),
(luma > kLumaThreshold) ? SK_ColorBLACK : SK_ColorWHITE,
text_rect,
gfx::Canvas::TEXT_ALIGN_CENTER);
}
char letter_;
SkColor color_;
int output_size_;
DISALLOW_COPY_AND_ASSIGN(GeneratedIconImageSource);
};
std::set<int> SizesToGenerate() { std::set<int> SizesToGenerate() {
// Generate container icons from smaller icons. // Generate container icons from smaller icons.
const int kIconSizesToGenerate[] = { const int kIconSizesToGenerate[] = {
...@@ -162,48 +92,7 @@ std::set<int> SizesToGenerate() { ...@@ -162,48 +92,7 @@ std::set<int> SizesToGenerate() {
kIconSizesToGenerate + arraysize(kIconSizesToGenerate)); kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
} }
void GenerateIcons( void ReplaceWebAppIcons(std::map<int, web_app::BitmapAndSource> bitmap_map,
std::set<int> generate_sizes,
const GURL& app_url,
SkColor generated_icon_color,
std::map<int, BookmarkAppHelper::BitmapAndSource>* bitmap_map) {
// The letter that will be painted on the generated icon.
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.has_host()) {
icon_letter = app_url.host_piece()[0];
}
// If no color has been specified, use a dark gray so it will stand out on the
// black shelf.
if (generated_icon_color == SK_ColorTRANSPARENT)
generated_icon_color = SK_ColorDKGRAY;
for (int size : generate_sizes) {
BookmarkAppHelper::GenerateIcon(bitmap_map, size, generated_icon_color,
icon_letter);
}
}
SkBitmap GenerateBitmap(int output_size, SkColor color, char letter) {
gfx::ImageSkia icon_image(
std::make_unique<GeneratedIconImageSource>(letter, color, output_size),
gfx::Size(output_size, output_size));
SkBitmap dst;
if (dst.tryAllocPixels(icon_image.bitmap()->info())) {
icon_image.bitmap()->readPixels(dst.info(), dst.getPixels(), dst.rowBytes(),
0, 0);
}
return dst;
}
void ReplaceWebAppIcons(
std::map<int, BookmarkAppHelper::BitmapAndSource> bitmap_map,
WebApplicationInfo* web_app_info) { WebApplicationInfo* web_app_info) {
web_app_info->icons.clear(); web_app_info->icons.clear();
...@@ -297,7 +186,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>, ...@@ -297,7 +186,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>,
continue; continue;
downloaded_bitmaps_.push_back( downloaded_bitmaps_.push_back(
BookmarkAppHelper::BitmapAndSource(url_bitmaps.first, bitmap)); web_app::BitmapAndSource(url_bitmaps.first, bitmap));
} }
} }
} }
...@@ -319,7 +208,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>, ...@@ -319,7 +208,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>,
for (const auto& icon : web_app_info_.icons) for (const auto& icon : web_app_info_.icons)
sizes_to_generate.insert(icon.width); sizes_to_generate.insert(icon.width);
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map = std::map<int, web_app::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing( BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded_bitmaps_, sizes_to_generate, &web_app_info_); downloaded_bitmaps_, sizes_to_generate, &web_app_info_);
BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(size_map, BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(size_map,
...@@ -335,7 +224,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>, ...@@ -335,7 +224,7 @@ class BookmarkAppInstaller : public base::RefCounted<BookmarkAppInstaller>,
std::unique_ptr<content::WebContents> web_contents_; std::unique_ptr<content::WebContents> web_contents_;
std::unique_ptr<WebAppIconDownloader> web_app_icon_downloader_; std::unique_ptr<WebAppIconDownloader> web_app_icon_downloader_;
std::vector<GURL> urls_to_download_; std::vector<GURL> urls_to_download_;
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded_bitmaps_; std::vector<web_app::BitmapAndSource> downloaded_bitmaps_;
}; };
} // namespace } // namespace
...@@ -382,54 +271,6 @@ void BookmarkAppHelper::UpdateWebAppInfoFromManifest( ...@@ -382,54 +271,6 @@ void BookmarkAppHelper::UpdateWebAppInfoFromManifest(
} }
} }
// static
std::map<int, BookmarkAppHelper::BitmapAndSource>
BookmarkAppHelper::ConstrainBitmapsToSizes(
const std::vector<BookmarkAppHelper::BitmapAndSource>& bitmaps,
const std::set<int>& sizes) {
std::map<int, BitmapAndSource> output_bitmaps;
std::map<int, BitmapAndSource> ordered_bitmaps;
for (const BitmapAndSource& bitmap_and_source : bitmaps) {
const SkBitmap& bitmap = bitmap_and_source.bitmap;
DCHECK(bitmap.width() == bitmap.height());
ordered_bitmaps[bitmap.width()] = bitmap_and_source;
}
if (ordered_bitmaps.size() > 0) {
for (const auto& size : sizes) {
// Find the closest not-smaller bitmap, or failing that use the largest
// icon available.
auto bitmaps_it = ordered_bitmaps.lower_bound(size);
if (bitmaps_it != ordered_bitmaps.end())
output_bitmaps[size] = bitmaps_it->second;
else
output_bitmaps[size] = ordered_bitmaps.rbegin()->second;
// Resize the bitmap if it does not exactly match the desired size.
if (output_bitmaps[size].bitmap.width() != size) {
output_bitmaps[size].bitmap = skia::ImageOperations::Resize(
output_bitmaps[size].bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
size, size);
}
}
}
return output_bitmaps;
}
// static
void BookmarkAppHelper::GenerateIcon(
std::map<int, BookmarkAppHelper::BitmapAndSource>* bitmaps,
int output_size,
SkColor color,
char letter) {
// Do nothing if there is already an icon of |output_size|.
if (bitmaps->count(output_size))
return;
(*bitmaps)[output_size].bitmap = GenerateBitmap(output_size, color, letter);
}
// static // static
WebApplicationInfo::IconInfo BookmarkAppHelper::GenerateIconInfo( WebApplicationInfo::IconInfo BookmarkAppHelper::GenerateIconInfo(
int output_size, int output_size,
...@@ -438,7 +279,7 @@ WebApplicationInfo::IconInfo BookmarkAppHelper::GenerateIconInfo( ...@@ -438,7 +279,7 @@ WebApplicationInfo::IconInfo BookmarkAppHelper::GenerateIconInfo(
WebApplicationInfo::IconInfo icon_info; WebApplicationInfo::IconInfo icon_info;
icon_info.width = output_size; icon_info.width = output_size;
icon_info.height = output_size; icon_info.height = output_size;
icon_info.data = GenerateBitmap(output_size, color, letter); icon_info.data = web_app::GenerateBitmap(output_size, color, letter);
return icon_info; return icon_info;
} }
...@@ -464,47 +305,25 @@ bool BookmarkAppHelper::BookmarkOrHostedAppInstalled( ...@@ -464,47 +305,25 @@ bool BookmarkAppHelper::BookmarkOrHostedAppInstalled(
} }
// static // static
std::map<int, BookmarkAppHelper::BitmapAndSource> std::map<int, web_app::BitmapAndSource>
BookmarkAppHelper::ResizeIconsAndGenerateMissing( BookmarkAppHelper::ResizeIconsAndGenerateMissing(
std::vector<BookmarkAppHelper::BitmapAndSource> icons, std::vector<web_app::BitmapAndSource> icons,
std::set<int> sizes_to_generate, std::set<int> sizes_to_generate,
WebApplicationInfo* web_app_info) { WebApplicationInfo* web_app_info) {
// Resize provided icons to make sure we have versions for each size in SkColor generated_icon_color = SK_ColorTRANSPARENT;
// |sizes_to_generate|.
std::map<int, BitmapAndSource> resized_bitmaps(
ConstrainBitmapsToSizes(icons, sizes_to_generate));
// Also add all provided icon sizes. std::map<int, web_app::BitmapAndSource> resized_bitmaps =
for (const BitmapAndSource& icon : icons) { web_app::ResizeIconsAndGenerateMissing(icons, sizes_to_generate,
if (resized_bitmaps.find(icon.bitmap.width()) == resized_bitmaps.end()) web_app_info->app_url,
resized_bitmaps.insert(std::make_pair(icon.bitmap.width(), icon)); &generated_icon_color);
}
// Determine the color that will be used for the icon's background. For this
// the dominant color of the first icon found is used.
if (resized_bitmaps.size()) {
color_utils::GridSampler sampler;
web_app_info->generated_icon_color =
color_utils::CalculateKMeanColorOfBitmap(
resized_bitmaps.begin()->second.bitmap);
}
// Work out what icons we need to generate here. Icons are only generated if
// there is no icon in the required size.
std::set<int> generate_sizes;
for (int size : sizes_to_generate) {
if (resized_bitmaps.find(size) == resized_bitmaps.end())
generate_sizes.insert(size);
}
GenerateIcons(generate_sizes, web_app_info->app_url,
web_app_info->generated_icon_color, &resized_bitmaps);
web_app_info->generated_icon_color = generated_icon_color;
return resized_bitmaps; return resized_bitmaps;
} }
// static // static
void BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks( void BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(
std::map<int, BookmarkAppHelper::BitmapAndSource> bitmap_map, std::map<int, web_app::BitmapAndSource> bitmap_map,
WebApplicationInfo* web_app_info) { WebApplicationInfo* web_app_info) {
// First add in the icon data that have urls with the url / size data from the // First add in the icon data that have urls with the url / size data from the
// original web app info, and the data from the new icons (if any). // original web app info, and the data from the new icons (if any).
...@@ -528,17 +347,6 @@ void BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks( ...@@ -528,17 +347,6 @@ void BookmarkAppHelper::UpdateWebAppIconsWithoutChangingLinks(
} }
} }
BookmarkAppHelper::BitmapAndSource::BitmapAndSource() {
}
BookmarkAppHelper::BitmapAndSource::BitmapAndSource(const GURL& source_url_p,
const SkBitmap& bitmap_p)
: source_url(source_url_p),
bitmap(bitmap_p) {
}
BookmarkAppHelper::BitmapAndSource::~BitmapAndSource() {
}
BookmarkAppHelper::BookmarkAppHelper(Profile* profile, BookmarkAppHelper::BookmarkAppHelper(Profile* profile,
WebApplicationInfo web_app_info, WebApplicationInfo web_app_info,
...@@ -676,13 +484,14 @@ void BookmarkAppHelper::OnIconsDownloaded( ...@@ -676,13 +484,14 @@ void BookmarkAppHelper::OnIconsDownloaded(
return; return;
} }
std::vector<BitmapAndSource> downloaded_icons; std::vector<web_app::BitmapAndSource> downloaded_icons;
for (const std::pair<GURL, std::vector<SkBitmap>>& url_bitmap : bitmaps) { for (const std::pair<GURL, std::vector<SkBitmap>>& url_bitmap : bitmaps) {
for (const SkBitmap& bitmap : url_bitmap.second) { for (const SkBitmap& bitmap : url_bitmap.second) {
if (bitmap.empty() || bitmap.width() != bitmap.height()) if (bitmap.empty() || bitmap.width() != bitmap.height())
continue; continue;
downloaded_icons.push_back(BitmapAndSource(url_bitmap.first, bitmap)); downloaded_icons.push_back(
web_app::BitmapAndSource(url_bitmap.first, bitmap));
} }
} }
...@@ -690,7 +499,7 @@ void BookmarkAppHelper::OnIconsDownloaded( ...@@ -690,7 +499,7 @@ void BookmarkAppHelper::OnIconsDownloaded(
for (const WebApplicationInfo::IconInfo& icon_info : web_app_info_.icons) { for (const WebApplicationInfo::IconInfo& icon_info : web_app_info_.icons) {
const SkBitmap& icon = icon_info.data; const SkBitmap& icon = icon_info.data;
if (!icon.drawsNothing() && icon.width() == icon.height()) { if (!icon.drawsNothing() && icon.width() == icon.height()) {
downloaded_icons.push_back(BitmapAndSource(icon_info.url, icon)); downloaded_icons.push_back(web_app::BitmapAndSource(icon_info.url, icon));
} }
} }
...@@ -698,8 +507,9 @@ void BookmarkAppHelper::OnIconsDownloaded( ...@@ -698,8 +507,9 @@ void BookmarkAppHelper::OnIconsDownloaded(
// icons down to smaller sizes, and generating icons for sizes where resizing // icons down to smaller sizes, and generating icons for sizes where resizing
// is not possible. // is not possible.
web_app_info_.generated_icon_color = SK_ColorTRANSPARENT; web_app_info_.generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, BitmapAndSource> size_to_icons = ResizeIconsAndGenerateMissing( std::map<int, web_app::BitmapAndSource> size_to_icons =
downloaded_icons, SizesToGenerate(), &web_app_info_); ResizeIconsAndGenerateMissing(downloaded_icons, SizesToGenerate(),
&web_app_info_);
ReplaceWebAppIcons(size_to_icons, &web_app_info_); ReplaceWebAppIcons(size_to_icons, &web_app_info_);
web_app_icon_downloader_.reset(); web_app_icon_downloader_.reset();
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/installable/installable_metrics.h" #include "chrome/browser/installable/installable_metrics.h"
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include "chrome/common/web_application_info.h" #include "chrome/common/web_application_info.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
...@@ -45,15 +46,6 @@ class BookmarkAppHelper : public content::NotificationObserver { ...@@ -45,15 +46,6 @@ class BookmarkAppHelper : public content::NotificationObserver {
kUnknown, kUnknown,
}; };
struct BitmapAndSource {
BitmapAndSource();
BitmapAndSource(const GURL& source_url_p, const SkBitmap& bitmap_p);
~BitmapAndSource();
GURL source_url;
SkBitmap bitmap;
};
typedef base::Callback<void(const Extension*, const WebApplicationInfo&)> typedef base::Callback<void(const Extension*, const WebApplicationInfo&)>
CreateBookmarkAppCallback; CreateBookmarkAppCallback;
...@@ -75,24 +67,10 @@ class BookmarkAppHelper : public content::NotificationObserver { ...@@ -75,24 +67,10 @@ class BookmarkAppHelper : public content::NotificationObserver {
WebApplicationInfo* web_app_info, WebApplicationInfo* web_app_info,
ForInstallableSite installable_site); ForInstallableSite installable_site);
// This finds the closest not-smaller bitmap in |bitmaps| for each size in
// |sizes| and resizes it to that size. This returns a map of sizes to bitmaps
// which contains only bitmaps of a size in |sizes| and at most one bitmap of
// each size.
static std::map<int, BitmapAndSource> ConstrainBitmapsToSizes(
const std::vector<BitmapAndSource>& bitmaps,
const std::set<int>& sizes);
// Adds a square container icon of |output_size| and 2 * |output_size| pixels // Adds a square container icon of |output_size| and 2 * |output_size| pixels
// to |bitmaps| by drawing the given |letter| into a rounded background of // to |bitmaps| by drawing the given |letter| into a rounded background of
// |color|. For each size, if an icon of the requested size already exists in // |color|. For each size, if an icon of the requested size already exists in
// |bitmaps|, nothing will happen. // |bitmaps|, nothing will happen. The generated icon is returned in a
static void GenerateIcon(std::map<int, BitmapAndSource>* bitmaps,
int output_size,
SkColor color,
char letter);
// Same as above, but the generated icon is returned in a
// `WebApplicationInfo::IconInfo`. // `WebApplicationInfo::IconInfo`.
static WebApplicationInfo::IconInfo GenerateIconInfo(int output_size, static WebApplicationInfo::IconInfo GenerateIconInfo(int output_size,
SkColor color, SkColor color,
...@@ -105,8 +83,8 @@ class BookmarkAppHelper : public content::NotificationObserver { ...@@ -105,8 +83,8 @@ class BookmarkAppHelper : public content::NotificationObserver {
// Resize icons to the accepted sizes, and generate any that are missing. Does // Resize icons to the accepted sizes, and generate any that are missing. Does
// not update |web_app_info| except to update |generated_icon_color|. // not update |web_app_info| except to update |generated_icon_color|.
static std::map<int, BitmapAndSource> ResizeIconsAndGenerateMissing( static std::map<int, web_app::BitmapAndSource> ResizeIconsAndGenerateMissing(
std::vector<BitmapAndSource> icons, std::vector<web_app::BitmapAndSource> icons,
std::set<int> sizes_to_generate, std::set<int> sizes_to_generate,
WebApplicationInfo* web_app_info); WebApplicationInfo* web_app_info);
...@@ -118,7 +96,7 @@ class BookmarkAppHelper : public content::NotificationObserver { ...@@ -118,7 +96,7 @@ class BookmarkAppHelper : public content::NotificationObserver {
// |bitmap_map| that has a URL and size matching that in |web_app_info|, as // |bitmap_map| that has a URL and size matching that in |web_app_info|, as
// well as adding any new images from |bitmap_map| that have no URL. // well as adding any new images from |bitmap_map| that have no URL.
static void UpdateWebAppIconsWithoutChangingLinks( static void UpdateWebAppIconsWithoutChangingLinks(
std::map<int, BookmarkAppHelper::BitmapAndSource> bitmap_map, std::map<int, web_app::BitmapAndSource> bitmap_map,
WebApplicationInfo* web_app_info); WebApplicationInfo* web_app_info);
// Begins the asynchronous bookmark app creation. // Begins the asynchronous bookmark app creation.
......
...@@ -53,18 +53,11 @@ const char kAppIcon2[] = "fav2.png"; ...@@ -53,18 +53,11 @@ const char kAppIcon2[] = "fav2.png";
const char kAppIcon3[] = "fav3.png"; const char kAppIcon3[] = "fav3.png";
const char kAppIconURL1[] = "http://foo.com/1.png"; const char kAppIconURL1[] = "http://foo.com/1.png";
const char kAppIconURL2[] = "http://foo.com/2.png"; const char kAppIconURL2[] = "http://foo.com/2.png";
const char kAppIconURL3[] = "http://foo.com/3.png";
const char kAppIconURL4[] = "http://foo.com/4.png";
const int kIconSizeTiny = extension_misc::EXTENSION_ICON_BITTY; const int kIconSizeTiny = extension_misc::EXTENSION_ICON_BITTY;
const int kIconSizeSmall = extension_misc::EXTENSION_ICON_SMALL; const int kIconSizeSmall = extension_misc::EXTENSION_ICON_SMALL;
const int kIconSizeMedium = extension_misc::EXTENSION_ICON_MEDIUM; const int kIconSizeMedium = extension_misc::EXTENSION_ICON_MEDIUM;
const int kIconSizeLarge = extension_misc::EXTENSION_ICON_LARGE; const int kIconSizeLarge = extension_misc::EXTENSION_ICON_LARGE;
const int kIconSizeGigantor = extension_misc::EXTENSION_ICON_GIGANTOR;
const int kIconSizeUnsupported = 123;
const int kIconSizeSmallBetweenMediumAndLarge = 63;
const int kIconSizeLargeBetweenMediumAndLarge = 96;
class BookmarkAppHelperTest : public testing::Test { class BookmarkAppHelperTest : public testing::Test {
public: public:
...@@ -112,20 +105,6 @@ SkBitmap CreateSquareBitmapWithColor(int size, SkColor color) { ...@@ -112,20 +105,6 @@ SkBitmap CreateSquareBitmapWithColor(int size, SkColor color) {
return bitmap; return bitmap;
} }
BookmarkAppHelper::BitmapAndSource CreateSquareBitmapAndSourceWithColor(
int size,
SkColor color) {
return BookmarkAppHelper::BitmapAndSource(
GURL(), CreateSquareBitmapWithColor(size, color));
}
void ValidateBitmapSizeAndColor(SkBitmap bitmap, int size, SkColor color) {
// Obtain pixel lock to access pixels.
EXPECT_EQ(color, bitmap.getColor(0, 0));
EXPECT_EQ(size, bitmap.width());
EXPECT_EQ(size, bitmap.height());
}
WebApplicationInfo::IconInfo CreateIconInfoWithBitmap(int size, SkColor color) { WebApplicationInfo::IconInfo CreateIconInfoWithBitmap(int size, SkColor color) {
WebApplicationInfo::IconInfo icon_info; WebApplicationInfo::IconInfo icon_info;
icon_info.width = size; icon_info.width = size;
...@@ -134,152 +113,6 @@ WebApplicationInfo::IconInfo CreateIconInfoWithBitmap(int size, SkColor color) { ...@@ -134,152 +113,6 @@ WebApplicationInfo::IconInfo CreateIconInfoWithBitmap(int size, SkColor color) {
return icon_info; return icon_info;
} }
std::set<int> TestSizesToGenerate() {
const int kIconSizesToGenerate[] = {
extension_misc::EXTENSION_ICON_SMALL,
extension_misc::EXTENSION_ICON_MEDIUM,
extension_misc::EXTENSION_ICON_LARGE,
};
return std::set<int>(kIconSizesToGenerate,
kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
}
void ValidateAllIconsWithURLsArePresent(const WebApplicationInfo& info_to_check,
const WebApplicationInfo& other_info) {
for (const auto& icon : info_to_check.icons) {
if (!icon.url.is_empty()) {
bool found = false;
for (const auto& other_icon : info_to_check.icons) {
if (other_icon.url == icon.url && other_icon.width == icon.width) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
}
}
std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator
FindLargestBitmapAndSourceVector(
const std::vector<BookmarkAppHelper::BitmapAndSource>& bitmap_vector) {
auto result = bitmap_vector.end();
int largest = -1;
for (std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() > largest) {
result = it;
}
}
return result;
}
std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator
FindMatchingBitmapAndSourceVector(
const std::vector<BookmarkAppHelper::BitmapAndSource>& bitmap_vector,
int size) {
for (std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() == size) {
return it;
}
}
return bitmap_vector.end();
}
std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator
FindEqualOrLargerBitmapAndSourceVector(
const std::vector<BookmarkAppHelper::BitmapAndSource>& bitmap_vector,
int size) {
for (std::vector<BookmarkAppHelper::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() >= size) {
return it;
}
}
return bitmap_vector.end();
}
void ValidateIconsGeneratedAndResizedCorrectly(
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded,
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map,
std::set<int> sizes_to_generate,
int expected_generated,
int expected_resized) {
GURL empty_url("");
int number_generated = 0;
int number_resized = 0;
auto icon_largest = FindLargestBitmapAndSourceVector(downloaded);
for (const auto& size : sizes_to_generate) {
auto icon_downloaded = FindMatchingBitmapAndSourceVector(downloaded, size);
auto icon_larger = FindEqualOrLargerBitmapAndSourceVector(downloaded, size);
if (icon_downloaded == downloaded.end()) {
auto icon_resized = size_map.find(size);
if (icon_largest == downloaded.end()) {
// There are no downloaded icons. Expect an icon to be generated.
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(empty_url, icon_resized->second.source_url);
++number_generated;
} else {
// If there is a larger downloaded icon, it should be resized. Otherwise
// the largest downloaded icon should be resized.
auto icon_to_resize = icon_largest;
if (icon_larger != downloaded.end())
icon_to_resize = icon_larger;
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(icon_to_resize->source_url, icon_resized->second.source_url);
++number_resized;
}
} else {
// There is an icon of exactly this size downloaded. Expect no icon to be
// generated, and the existing downloaded icon to be used.
auto icon_resized = size_map.find(size);
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_downloaded->bitmap.width());
EXPECT_EQ(size, icon_downloaded->bitmap.height());
EXPECT_EQ(icon_downloaded->source_url, icon_resized->second.source_url);
}
}
EXPECT_EQ(expected_generated, number_generated);
EXPECT_EQ(expected_resized, number_resized);
}
void TestIconGeneration(int icon_size,
int expected_generated,
int expected_resized) {
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
// Add an icon with a URL and bitmap. 'Download' it.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(icon_size, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
// Now run the resizing/generation and validation.
WebApplicationInfo web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &web_app_info);
ValidateIconsGeneratedAndResizedCorrectly(downloaded, size_map,
TestSizesToGenerate(),
expected_generated,
expected_resized);
}
class TestBookmarkAppHelper : public BookmarkAppHelper { class TestBookmarkAppHelper : public BookmarkAppHelper {
public: public:
TestBookmarkAppHelper(ExtensionService* service, TestBookmarkAppHelper(ExtensionService* service,
...@@ -691,53 +524,6 @@ TEST_F(BookmarkAppHelperExtensionServiceTest, CreateAndUpdateBookmarkApp) { ...@@ -691,53 +524,6 @@ TEST_F(BookmarkAppHelperExtensionServiceTest, CreateAndUpdateBookmarkApp) {
} }
} }
TEST_F(BookmarkAppHelperExtensionServiceTest, LinkedAppIconsAreNotChanged) {
WebApplicationInfo web_app_info;
// Add two icons with a URL and bitmap, two icons with just a bitmap, an
// icon with just URL and an icon in an unsupported size with just a URL.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
web_app_info.icons.push_back(icon_info);
icon_info = CreateIconInfoWithBitmap(kIconSizeMedium, SK_ColorRED);
icon_info.url = GURL(kAppIconURL2);
web_app_info.icons.push_back(icon_info);
icon_info.data = SkBitmap();
icon_info.url = GURL(kAppIconURL3);
icon_info.width = 0;
icon_info.height = 0;
web_app_info.icons.push_back(icon_info);
icon_info.url = GURL(kAppIconURL4);
web_app_info.icons.push_back(icon_info);
icon_info = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorRED);
web_app_info.icons.push_back(icon_info);
icon_info = CreateIconInfoWithBitmap(kIconSizeUnsupported, SK_ColorRED);
web_app_info.icons.push_back(icon_info);
// 'Download' one of the icons without a size or bitmap.
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
GURL(kAppIconURL3),
CreateSquareBitmapWithColor(kIconSizeLarge, SK_ColorBLACK)));
// Now run the resizing and generation into a new web app info.
WebApplicationInfo new_web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &new_web_app_info);
// Now check that the linked app icons (i.e. those with URLs) are matching in
// both lists.
ValidateAllIconsWithURLsArePresent(web_app_info, new_web_app_info);
ValidateAllIconsWithURLsArePresent(new_web_app_info, web_app_info);
}
TEST_F(BookmarkAppHelperTest, UpdateWebAppInfoFromManifest) { TEST_F(BookmarkAppHelperTest, UpdateWebAppInfoFromManifest) {
WebApplicationInfo web_app_info; WebApplicationInfo web_app_info;
web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle); web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle);
...@@ -812,53 +598,6 @@ TEST_F(BookmarkAppHelperTest, UpdateWebAppInfoFromManifestInstallableSite) { ...@@ -812,53 +598,6 @@ TEST_F(BookmarkAppHelperTest, UpdateWebAppInfoFromManifestInstallableSite) {
} }
} }
TEST_F(BookmarkAppHelperTest, ConstrainBitmapsToSizes) {
std::set<int> desired_sizes;
desired_sizes.insert(16);
desired_sizes.insert(32);
desired_sizes.insert(48);
desired_sizes.insert(96);
desired_sizes.insert(128);
desired_sizes.insert(256);
{
std::vector<BookmarkAppHelper::BitmapAndSource> bitmaps;
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(16, SK_ColorRED));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(32, SK_ColorGREEN));
bitmaps.push_back(
CreateSquareBitmapAndSourceWithColor(144, SK_ColorYELLOW));
std::map<int, BookmarkAppHelper::BitmapAndSource> results(
BookmarkAppHelper::ConstrainBitmapsToSizes(bitmaps, desired_sizes));
EXPECT_EQ(6u, results.size());
ValidateBitmapSizeAndColor(results[16].bitmap, 16, SK_ColorRED);
ValidateBitmapSizeAndColor(results[32].bitmap, 32, SK_ColorGREEN);
ValidateBitmapSizeAndColor(results[48].bitmap, 48, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[96].bitmap, 96, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[128].bitmap, 128, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[256].bitmap, 256, SK_ColorYELLOW);
}
{
std::vector<BookmarkAppHelper::BitmapAndSource> bitmaps;
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(512, SK_ColorRED));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(18, SK_ColorGREEN));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(33, SK_ColorBLUE));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(17, SK_ColorYELLOW));
std::map<int, BookmarkAppHelper::BitmapAndSource> results(
BookmarkAppHelper::ConstrainBitmapsToSizes(bitmaps, desired_sizes));
EXPECT_EQ(6u, results.size());
ValidateBitmapSizeAndColor(results[16].bitmap, 16, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[32].bitmap, 32, SK_ColorBLUE);
ValidateBitmapSizeAndColor(results[48].bitmap, 48, SK_ColorRED);
ValidateBitmapSizeAndColor(results[96].bitmap, 96, SK_ColorRED);
ValidateBitmapSizeAndColor(results[128].bitmap, 128, SK_ColorRED);
ValidateBitmapSizeAndColor(results[256].bitmap, 256, SK_ColorRED);
}
}
TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) { TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) {
EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://chromium.org"))); EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://chromium.org")));
EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://www.chromium.org"))); EXPECT_TRUE(IsValidBookmarkAppUrl(GURL("https://www.chromium.org")));
...@@ -879,140 +618,4 @@ TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) { ...@@ -879,140 +618,4 @@ TEST_F(BookmarkAppHelperTest, IsValidBookmarkAppUrl) {
EXPECT_FALSE(IsValidBookmarkAppUrl(GURL("chrome://extensions"))); EXPECT_FALSE(IsValidBookmarkAppUrl(GURL("chrome://extensions")));
} }
TEST_F(BookmarkAppHelperTest, IconsResizedFromOddSizes) {
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' each of them.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeSmallBetweenMediumAndLarge,
SK_ColorRED);
icon_info.url = GURL(kAppIconURL2);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeLargeBetweenMediumAndLarge,
SK_ColorRED);
icon_info.url = GURL(kAppIconURL3);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
// Now run the resizing and generation.
WebApplicationInfo web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &web_app_info);
// No icons should be generated. The LARGE and MEDIUM sizes should be resized.
ValidateIconsGeneratedAndResizedCorrectly(
downloaded, size_map, TestSizesToGenerate(), 0, 2);
}
TEST_F(BookmarkAppHelperTest, IconsResizedFromLarger) {
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' two of them and pretend
// the third failed to download.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
// Now run the resizing and generation.
WebApplicationInfo web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &web_app_info);
// Expect icon for MEDIUM and LARGE to be resized from the gigantor icon
// as it was not downloaded.
ValidateIconsGeneratedAndResizedCorrectly(
downloaded, size_map, TestSizesToGenerate(), 0, 2);
}
TEST_F(BookmarkAppHelperTest, AllIconsGeneratedWhenNotDownloaded) {
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' none of them.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
icon_info = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
// Now run the resizing and generation.
WebApplicationInfo web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &web_app_info);
// Expect all icons to be generated.
ValidateIconsGeneratedAndResizedCorrectly(
downloaded, size_map, TestSizesToGenerate(), 3, 0);
}
TEST_F(BookmarkAppHelperTest, IconResizedFromLargerAndSmaller) {
std::vector<BookmarkAppHelper::BitmapAndSource> downloaded;
// Pretend the huge icon wasn't downloaded but two smaller ones were.
WebApplicationInfo::IconInfo icon_info =
CreateIconInfoWithBitmap(kIconSizeTiny, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeMedium, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
downloaded.push_back(BookmarkAppHelper::BitmapAndSource(
icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
// Now run the resizing and generation.
WebApplicationInfo web_app_info;
std::map<int, BookmarkAppHelper::BitmapAndSource> size_map =
BookmarkAppHelper::ResizeIconsAndGenerateMissing(
downloaded, TestSizesToGenerate(), &web_app_info);
// Expect no icons to be generated, but the LARGE and SMALL icons to be
// resized from the MEDIUM icon.
ValidateIconsGeneratedAndResizedCorrectly(
downloaded, size_map, TestSizesToGenerate(), 0, 2);
// Verify specifically that the LARGE icons was resized from the medium icon.
const auto it = size_map.find(kIconSizeLarge);
EXPECT_NE(size_map.end(), it);
EXPECT_EQ(GURL(kAppIconURL2), it->second.source_url);
}
TEST_F(BookmarkAppHelperTest, IconsResizedWhenOnlyATinyOneIsProvided) {
// When only a tiny icon is downloaded (smaller than the three desired
// sizes), 3 icons should be resized.
TestIconGeneration(kIconSizeTiny, 0, 3);
}
TEST_F(BookmarkAppHelperTest, IconsResizedWhenOnlyAGigantorOneIsProvided) {
// When an enormous icon is provided, each desired icon size should be resized
// from it, and no icons should be generated.
TestIconGeneration(kIconSizeGigantor, 0, 3);
}
} // namespace extensions } // namespace extensions
...@@ -8,6 +8,8 @@ source_set("components") { ...@@ -8,6 +8,8 @@ source_set("components") {
"pending_app_manager.h", "pending_app_manager.h",
"web_app_helpers.cc", "web_app_helpers.cc",
"web_app_helpers.h", "web_app_helpers.h",
"web_app_icon_generator.cc",
"web_app_icon_generator.h",
"web_app_shortcut.cc", "web_app_shortcut.cc",
"web_app_shortcut.h", "web_app_shortcut.h",
"web_app_shortcut_chromeos.cc", "web_app_shortcut_chromeos.cc",
...@@ -49,6 +51,7 @@ source_set("unit_tests") { ...@@ -49,6 +51,7 @@ source_set("unit_tests") {
sources = [ sources = [
"web_app_helpers_unittest.cc", "web_app_helpers_unittest.cc",
"web_app_icon_downloader_unittest.cc", "web_app_icon_downloader_unittest.cc",
"web_app_icon_generator_unittest.cc",
"web_app_shortcut_mac_unittest.mm", "web_app_shortcut_mac_unittest.mm",
"web_app_shortcut_unittest.cc", "web_app_shortcut_unittest.cc",
] ]
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include <cctype>
#include <string>
#include <utility>
#include "base/macros.h"
#include "build/build_config.h"
#include "chrome/grit/platform_locale_settings.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "skia/ext/image_operations.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.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/geometry/rect.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image.h"
namespace web_app {
namespace {
// Generates a square container icon of |output_size| by drawing the given
// |letter| into a rounded background of |color|.
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) {}
~GeneratedIconImageSource() override {}
private:
// gfx::CanvasImageSource overrides:
void Draw(gfx::Canvas* canvas) override {
const uint8_t kLumaThreshold = 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|.
cc::PaintFlags background_flags;
background_flags.setAntiAlias(true);
background_flags.setColor(color_);
gfx::Rect icon_rect(icon_inset, icon_inset, icon_size, icon_size);
canvas->DrawRoundRect(icon_rect, border_radius, background_flags);
// 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
// luma of |color|.
const uint8_t luma = color_utils::GetLuma(color_);
canvas->DrawStringRectWithFlags(
base::string16(1, std::toupper(letter_)),
gfx::FontList(gfx::Font(font_name, font_size)),
(luma > kLumaThreshold) ? SK_ColorBLACK : SK_ColorWHITE, text_rect,
gfx::Canvas::TEXT_ALIGN_CENTER);
}
char letter_;
SkColor color_;
int output_size_;
DISALLOW_COPY_AND_ASSIGN(GeneratedIconImageSource);
};
// Adds a square container icon of |output_size| and 2 * |output_size| pixels
// to |bitmaps| by drawing the given |letter| into a rounded background of
// |color|. For each size, if an icon of the requested size already exists in
// |bitmaps|, nothing will happen.
void GenerateIcon(std::map<int, BitmapAndSource>* bitmaps,
int output_size,
SkColor color,
char letter) {
// Do nothing if there is already an icon of |output_size|.
if (bitmaps->count(output_size))
return;
(*bitmaps)[output_size].bitmap = GenerateBitmap(output_size, color, letter);
}
void GenerateIcons(std::set<int> generate_sizes,
const GURL& app_url,
SkColor generated_icon_color,
std::map<int, BitmapAndSource>* bitmap_map) {
// The letter that will be painted on the generated icon.
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.has_host()) {
icon_letter = app_url.host_piece()[0];
}
// If no color has been specified, use a dark gray so it will stand out on the
// black shelf.
if (generated_icon_color == SK_ColorTRANSPARENT)
generated_icon_color = SK_ColorDKGRAY;
for (int size : generate_sizes) {
GenerateIcon(bitmap_map, size, generated_icon_color, icon_letter);
}
}
} // namespace
BitmapAndSource::BitmapAndSource() {}
BitmapAndSource::BitmapAndSource(const GURL& source_url_p,
const SkBitmap& bitmap_p)
: source_url(source_url_p), bitmap(bitmap_p) {}
BitmapAndSource::~BitmapAndSource() {}
std::map<int, BitmapAndSource> ConstrainBitmapsToSizes(
const std::vector<BitmapAndSource>& bitmaps,
const std::set<int>& sizes) {
std::map<int, BitmapAndSource> output_bitmaps;
std::map<int, BitmapAndSource> ordered_bitmaps;
for (const BitmapAndSource& bitmap_and_source : bitmaps) {
const SkBitmap& bitmap = bitmap_and_source.bitmap;
DCHECK(bitmap.width() == bitmap.height());
ordered_bitmaps[bitmap.width()] = bitmap_and_source;
}
if (ordered_bitmaps.size() > 0) {
for (const auto& size : sizes) {
// Find the closest not-smaller bitmap, or failing that use the largest
// icon available.
auto bitmaps_it = ordered_bitmaps.lower_bound(size);
if (bitmaps_it != ordered_bitmaps.end())
output_bitmaps[size] = bitmaps_it->second;
else
output_bitmaps[size] = ordered_bitmaps.rbegin()->second;
// Resize the bitmap if it does not exactly match the desired size.
if (output_bitmaps[size].bitmap.width() != size) {
output_bitmaps[size].bitmap = skia::ImageOperations::Resize(
output_bitmaps[size].bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
size, size);
}
}
}
return output_bitmaps;
}
SkBitmap GenerateBitmap(int output_size, SkColor color, char letter) {
gfx::ImageSkia icon_image(
std::make_unique<GeneratedIconImageSource>(letter, color, output_size),
gfx::Size(output_size, output_size));
SkBitmap dst;
if (dst.tryAllocPixels(icon_image.bitmap()->info())) {
icon_image.bitmap()->readPixels(dst.info(), dst.getPixels(), dst.rowBytes(),
0, 0);
}
return dst;
}
std::map<int, BitmapAndSource> ResizeIconsAndGenerateMissing(
const std::vector<BitmapAndSource>& icons,
const std::set<int>& sizes_to_generate,
const GURL& app_url,
SkColor* generated_icon_color) {
DCHECK(generated_icon_color);
// Resize provided icons to make sure we have versions for each size in
// |sizes_to_generate|.
std::map<int, BitmapAndSource> resized_bitmaps(
ConstrainBitmapsToSizes(icons, sizes_to_generate));
// Also add all provided icon sizes.
for (const BitmapAndSource& icon : icons) {
if (resized_bitmaps.find(icon.bitmap.width()) == resized_bitmaps.end())
resized_bitmaps.insert(std::make_pair(icon.bitmap.width(), icon));
}
// Determine the color that will be used for the icon's background. For this
// the dominant color of the first icon found is used.
if (resized_bitmaps.size()) {
color_utils::GridSampler sampler;
*generated_icon_color = color_utils::CalculateKMeanColorOfBitmap(
resized_bitmaps.begin()->second.bitmap);
}
// Work out what icons we need to generate here. Icons are only generated if
// there is no icon in the required size.
std::set<int> generate_sizes;
for (int size : sizes_to_generate) {
if (resized_bitmaps.find(size) == resized_bitmaps.end())
generate_sizes.insert(size);
}
GenerateIcons(generate_sizes, app_url, *generated_icon_color,
&resized_bitmaps);
return resized_bitmaps;
}
} // namespace web_app
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_ICON_GENERATOR_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_ICON_GENERATOR_H_
#include <map>
#include <set>
#include <vector>
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "url/gurl.h"
namespace web_app {
struct BitmapAndSource {
BitmapAndSource();
BitmapAndSource(const GURL& source_url_p, const SkBitmap& bitmap_p);
~BitmapAndSource();
GURL source_url;
SkBitmap bitmap;
};
// This finds the closest not-smaller bitmap in |bitmaps| for each size in
// |sizes| and resizes it to that size. This returns a map of sizes to bitmaps
// which contains only bitmaps of a size in |sizes| and at most one bitmap of
// each size.
std::map<int, BitmapAndSource> ConstrainBitmapsToSizes(
const std::vector<BitmapAndSource>& bitmaps,
const std::set<int>& sizes);
// Generates a square container icon of |output_size| by drawing the given
// |letter| into a rounded background of |color|.
SkBitmap GenerateBitmap(int output_size, SkColor color, char letter);
// Resize icons to the accepted sizes, and generate any that are missing.
// Note that |app_url| is the launch URL for the app.
// Output: |generated_icon_color| is the color to use if an icon needs to be
// generated for the web app.
std::map<int, BitmapAndSource> ResizeIconsAndGenerateMissing(
const std::vector<BitmapAndSource>& icons,
const std::set<int>& sizes_to_generate,
const GURL& app_url,
SkColor* generated_icon_color);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_ICON_GENERATOR_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include <vector>
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace web_app {
namespace {
const char kAppIconURL1[] = "http://foo.com/1.png";
const char kAppIconURL2[] = "http://foo.com/2.png";
const char kAppIconURL3[] = "http://foo.com/3.png";
// These sizes match extension_misc::ExtensionIcons enum.
const int kIconSizeTiny = 16;
const int kIconSizeSmall = 32;
const int kIconSizeMedium = 48;
const int kIconSizeLarge = 128;
const int kIconSizeGigantor = 512;
const int kIconSizeSmallBetweenMediumAndLarge = 63;
const int kIconSizeLargeBetweenMediumAndLarge = 96;
SkBitmap CreateSquareBitmapWithColor(int size, SkColor color) {
SkBitmap bitmap;
bitmap.allocN32Pixels(size, size);
bitmap.eraseColor(color);
return bitmap;
}
web_app::BitmapAndSource CreateSquareBitmapAndSourceWithColor(int size,
SkColor color) {
return web_app::BitmapAndSource(GURL(),
CreateSquareBitmapWithColor(size, color));
}
struct IconInfo {
IconInfo() : width(0), height(0) {}
~IconInfo() = default;
GURL url;
int width;
int height;
SkBitmap data;
};
IconInfo CreateIconInfoWithBitmap(int size, SkColor color) {
IconInfo icon_info;
icon_info.width = size;
icon_info.height = size;
icon_info.data = CreateSquareBitmapWithColor(size, color);
return icon_info;
}
std::set<int> TestSizesToGenerate() {
const int kIconSizesToGenerate[] = {
kIconSizeSmall, kIconSizeMedium, kIconSizeLarge,
};
return std::set<int>(kIconSizesToGenerate,
kIconSizesToGenerate + base::size(kIconSizesToGenerate));
}
void ValidateAllIconsWithURLsArePresent(
const std::vector<IconInfo>& icons_to_check,
const std::map<int, BitmapAndSource>& size_map) {
EXPECT_EQ(icons_to_check.size(), size_map.size());
// Check that every icon with URL has a mapped icon.
for (const auto& icon : icons_to_check) {
if (!icon.url.is_empty()) {
bool found = false;
if (base::ContainsKey(size_map, icon.width)) {
const BitmapAndSource& mapped_icon = size_map.at(icon.width);
if (mapped_icon.source_url == icon.url &&
mapped_icon.bitmap.width() == icon.width) {
found = true;
}
}
EXPECT_TRUE(found);
}
}
}
std::vector<web_app::BitmapAndSource>::const_iterator
FindLargestBitmapAndSourceVector(
const std::vector<web_app::BitmapAndSource>& bitmap_vector) {
auto result = bitmap_vector.end();
int largest = -1;
for (std::vector<web_app::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() > largest) {
result = it;
}
}
return result;
}
std::vector<web_app::BitmapAndSource>::const_iterator
FindMatchingBitmapAndSourceVector(
const std::vector<web_app::BitmapAndSource>& bitmap_vector,
int size) {
for (std::vector<web_app::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() == size) {
return it;
}
}
return bitmap_vector.end();
}
std::vector<web_app::BitmapAndSource>::const_iterator
FindEqualOrLargerBitmapAndSourceVector(
const std::vector<web_app::BitmapAndSource>& bitmap_vector,
int size) {
for (std::vector<web_app::BitmapAndSource>::const_iterator it =
bitmap_vector.begin();
it != bitmap_vector.end(); ++it) {
if (it->bitmap.width() >= size) {
return it;
}
}
return bitmap_vector.end();
}
void ValidateIconsGeneratedAndResizedCorrectly(
std::vector<web_app::BitmapAndSource> downloaded,
std::map<int, web_app::BitmapAndSource> size_map,
std::set<int> sizes_to_generate,
int expected_generated,
int expected_resized) {
GURL empty_url("");
int number_generated = 0;
int number_resized = 0;
auto icon_largest = FindLargestBitmapAndSourceVector(downloaded);
for (const auto& size : sizes_to_generate) {
auto icon_downloaded = FindMatchingBitmapAndSourceVector(downloaded, size);
auto icon_larger = FindEqualOrLargerBitmapAndSourceVector(downloaded, size);
if (icon_downloaded == downloaded.end()) {
auto icon_resized = size_map.find(size);
if (icon_largest == downloaded.end()) {
// There are no downloaded icons. Expect an icon to be generated.
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(empty_url, icon_resized->second.source_url);
++number_generated;
} else {
// If there is a larger downloaded icon, it should be resized. Otherwise
// the largest downloaded icon should be resized.
auto icon_to_resize = icon_largest;
if (icon_larger != downloaded.end())
icon_to_resize = icon_larger;
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(icon_to_resize->source_url, icon_resized->second.source_url);
++number_resized;
}
} else {
// There is an icon of exactly this size downloaded. Expect no icon to be
// generated, and the existing downloaded icon to be used.
auto icon_resized = size_map.find(size);
EXPECT_NE(size_map.end(), icon_resized);
EXPECT_EQ(size, icon_resized->second.bitmap.width());
EXPECT_EQ(size, icon_resized->second.bitmap.height());
EXPECT_EQ(size, icon_downloaded->bitmap.width());
EXPECT_EQ(size, icon_downloaded->bitmap.height());
EXPECT_EQ(icon_downloaded->source_url, icon_resized->second.source_url);
}
}
EXPECT_EQ(expected_generated, number_generated);
EXPECT_EQ(expected_resized, number_resized);
}
void ValidateBitmapSizeAndColor(SkBitmap bitmap, int size, SkColor color) {
// Obtain pixel lock to access pixels.
EXPECT_EQ(color, bitmap.getColor(0, 0));
EXPECT_EQ(size, bitmap.width());
EXPECT_EQ(size, bitmap.height());
}
void TestIconGeneration(int icon_size,
int expected_generated,
int expected_resized) {
std::vector<BitmapAndSource> downloaded;
// Add an icon with a URL and bitmap. 'Download' it.
IconInfo icon_info = CreateIconInfoWithBitmap(icon_size, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(BitmapAndSource(icon_info.url, icon_info.data));
// Now run the resizing/generation and validation.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, TestSizesToGenerate(), GURL(),
&generated_icon_color);
ValidateIconsGeneratedAndResizedCorrectly(
downloaded, size_map, TestSizesToGenerate(), expected_generated,
expected_resized);
}
} // namespace
TEST(WebAppIconGeneratorTest, ConstrainBitmapsToSizes) {
std::set<int> desired_sizes;
desired_sizes.insert(16);
desired_sizes.insert(32);
desired_sizes.insert(48);
desired_sizes.insert(96);
desired_sizes.insert(128);
desired_sizes.insert(256);
{
std::vector<web_app::BitmapAndSource> bitmaps;
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(16, SK_ColorRED));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(32, SK_ColorGREEN));
bitmaps.push_back(
CreateSquareBitmapAndSourceWithColor(144, SK_ColorYELLOW));
std::map<int, web_app::BitmapAndSource> results(
ConstrainBitmapsToSizes(bitmaps, desired_sizes));
EXPECT_EQ(6u, results.size());
ValidateBitmapSizeAndColor(results[16].bitmap, 16, SK_ColorRED);
ValidateBitmapSizeAndColor(results[32].bitmap, 32, SK_ColorGREEN);
ValidateBitmapSizeAndColor(results[48].bitmap, 48, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[96].bitmap, 96, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[128].bitmap, 128, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[256].bitmap, 256, SK_ColorYELLOW);
}
{
std::vector<web_app::BitmapAndSource> bitmaps;
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(512, SK_ColorRED));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(18, SK_ColorGREEN));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(33, SK_ColorBLUE));
bitmaps.push_back(CreateSquareBitmapAndSourceWithColor(17, SK_ColorYELLOW));
std::map<int, web_app::BitmapAndSource> results(
ConstrainBitmapsToSizes(bitmaps, desired_sizes));
EXPECT_EQ(6u, results.size());
ValidateBitmapSizeAndColor(results[16].bitmap, 16, SK_ColorYELLOW);
ValidateBitmapSizeAndColor(results[32].bitmap, 32, SK_ColorBLUE);
ValidateBitmapSizeAndColor(results[48].bitmap, 48, SK_ColorRED);
ValidateBitmapSizeAndColor(results[96].bitmap, 96, SK_ColorRED);
ValidateBitmapSizeAndColor(results[128].bitmap, 128, SK_ColorRED);
ValidateBitmapSizeAndColor(results[256].bitmap, 256, SK_ColorRED);
}
}
TEST(WebAppIconGeneratorTest, LinkedAppIconsAreNotChanged) {
std::vector<IconInfo> icons_info;
IconInfo icon_info;
icon_info.url = GURL(kAppIconURL3);
icon_info.width = kIconSizeMedium;
icons_info.push_back(icon_info);
icon_info.width = kIconSizeSmall;
icons_info.push_back(icon_info);
icon_info.width = kIconSizeLarge;
icons_info.push_back(icon_info);
// 'Download' one of the icons without a size or bitmap.
std::vector<BitmapAndSource> downloaded;
downloaded.push_back(BitmapAndSource(
GURL(kAppIconURL3),
CreateSquareBitmapWithColor(kIconSizeLarge, SK_ColorBLACK)));
const auto& sizes = TestSizesToGenerate();
// Now run the resizing and generation into a new web icons info.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, sizes, GURL(),
&generated_icon_color);
EXPECT_EQ(sizes.size(), size_map.size());
// Now check that the linked app icons (i.e. those with URLs) are matching.
ValidateAllIconsWithURLsArePresent(icons_info, size_map);
}
TEST(WebAppIconGeneratorTest, IconsResizedFromOddSizes) {
std::vector<BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' each of them.
IconInfo icon_info = CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeSmallBetweenMediumAndLarge,
SK_ColorRED);
icon_info.url = GURL(kAppIconURL2);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeLargeBetweenMediumAndLarge,
SK_ColorRED);
icon_info.url = GURL(kAppIconURL3);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
// Now run the resizing and generation.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, TestSizesToGenerate(), GURL(),
&generated_icon_color);
// No icons should be generated. The LARGE and MEDIUM sizes should be resized.
ValidateIconsGeneratedAndResizedCorrectly(downloaded, size_map,
TestSizesToGenerate(), 0, 2);
}
TEST(WebAppIconGeneratorTest, IconsResizedFromLarger) {
std::vector<web_app::BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' two of them and pretend
// the third failed to download.
IconInfo icon_info = CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
// Now run the resizing and generation.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, TestSizesToGenerate(), GURL(),
&generated_icon_color);
// Expect icon for MEDIUM and LARGE to be resized from the gigantor icon
// as it was not downloaded.
ValidateIconsGeneratedAndResizedCorrectly(downloaded, size_map,
TestSizesToGenerate(), 0, 2);
}
TEST(WebAppIconGeneratorTest, AllIconsGeneratedWhenNotDownloaded) {
std::vector<web_app::BitmapAndSource> downloaded;
// Add three icons with a URL and bitmap. 'Download' none of them.
IconInfo icon_info = CreateIconInfoWithBitmap(kIconSizeSmall, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
icon_info = CreateIconInfoWithBitmap(kIconSizeLarge, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
// Now run the resizing and generation.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, TestSizesToGenerate(), GURL(),
&generated_icon_color);
// Expect all icons to be generated.
ValidateIconsGeneratedAndResizedCorrectly(downloaded, size_map,
TestSizesToGenerate(), 3, 0);
}
TEST(WebAppIconGeneratorTest, IconResizedFromLargerAndSmaller) {
std::vector<web_app::BitmapAndSource> downloaded;
// Pretend the huge icon wasn't downloaded but two smaller ones were.
IconInfo icon_info = CreateIconInfoWithBitmap(kIconSizeTiny, SK_ColorRED);
icon_info.url = GURL(kAppIconURL1);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeMedium, SK_ColorBLUE);
icon_info.url = GURL(kAppIconURL2);
downloaded.push_back(web_app::BitmapAndSource(icon_info.url, icon_info.data));
icon_info = CreateIconInfoWithBitmap(kIconSizeGigantor, SK_ColorBLACK);
icon_info.url = GURL(kAppIconURL3);
// Now run the resizing and generation.
SkColor generated_icon_color = SK_ColorTRANSPARENT;
std::map<int, web_app::BitmapAndSource> size_map =
ResizeIconsAndGenerateMissing(downloaded, TestSizesToGenerate(), GURL(),
&generated_icon_color);
// Expect no icons to be generated, but the LARGE and SMALL icons to be
// resized from the MEDIUM icon.
ValidateIconsGeneratedAndResizedCorrectly(downloaded, size_map,
TestSizesToGenerate(), 0, 2);
// Verify specifically that the LARGE icons was resized from the medium icon.
const auto it = size_map.find(kIconSizeLarge);
EXPECT_NE(size_map.end(), it);
EXPECT_EQ(GURL(kAppIconURL2), it->second.source_url);
}
TEST(WebAppIconGeneratorTest, IconsResizedWhenOnlyATinyOneIsProvided) {
// When only a tiny icon is downloaded (smaller than the three desired
// sizes), 3 icons should be resized.
TestIconGeneration(kIconSizeTiny, 0, 3);
}
TEST(WebAppIconGeneratorTest, IconsResizedWhenOnlyAGigantorOneIsProvided) {
// When an enormous icon is provided, each desired icon size should be resized
// from it, and no icons should be generated.
TestIconGeneration(kIconSizeGigantor, 0, 3);
}
} // namespace web_app
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