Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Side by Side Diff: chrome/browser/extensions/image_loader.cc

Issue 25050005: Refactored loading of applications / extensions icons. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Additional feedbacks. Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/image_loader.h" 5 #include "chrome/browser/extensions/image_loader.h"
6 6
7 #include <map> 7 #include <map>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/base64.h"
10 #include "base/callback.h" 11 #include "base/callback.h"
11 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
12 #include "base/file_util.h" 13 #include "base/file_util.h"
13 #include "base/lazy_instance.h" 14 #include "base/lazy_instance.h"
14 #include "base/path_service.h" 15 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/sequenced_worker_pool.h" 17 #include "base/threading/sequenced_worker_pool.h"
17 #include "chrome/browser/extensions/image_loader_factory.h" 18 #include "chrome/browser/extensions/image_loader_factory.h"
19 #include "chrome/browser/favicon/favicon_service.h"
20 #include "chrome/browser/favicon/favicon_service_factory.h"
18 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_paths.h"
19 #include "chrome/common/extensions/extension.h" 22 #include "chrome/common/extensions/extension.h"
23 #include "chrome/common/extensions/extension_icon_set.h"
24 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
25 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
20 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/common/url_constants.h"
21 #include "grit/chrome_unscaled_resources.h" 28 #include "grit/chrome_unscaled_resources.h"
22 #include "grit/component_extension_resources_map.h" 29 #include "grit/component_extension_resources_map.h"
23 #include "grit/theme_resources.h" 30 #include "grit/theme_resources.h"
24 #include "skia/ext/image_operations.h" 31 #include "skia/ext/image_operations.h"
25 #include "ui/base/resource/resource_bundle.h" 32 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/codec/png_codec.h" 33 #include "ui/gfx/codec/png_codec.h"
34 #include "ui/gfx/color_utils.h"
35 #include "ui/gfx/favicon_size.h"
27 #include "ui/gfx/image/image_skia.h" 36 #include "ui/gfx/image/image_skia.h"
37 #include "ui/gfx/size.h"
38 #include "ui/gfx/skbitmap_operations.h"
28 39
29 #if defined(USE_AURA) 40 #if defined(USE_AURA)
30 #include "ui/keyboard/keyboard_util.h" 41 #include "ui/keyboard/keyboard_util.h"
31 #endif 42 #endif
32 43
33 using content::BrowserThread; 44 using content::BrowserThread;
34 using extensions::Extension; 45 using extensions::Extension;
35 using extensions::ImageLoader; 46 using extensions::ImageLoader;
36 using extensions::Manifest; 47 using extensions::Manifest;
37 48
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 base::FilePath resource_path = base::FilePath().AppendASCII( 119 base::FilePath resource_path = base::FilePath().AppendASCII(
109 entries[i].name); 120 entries[i].name);
110 resource_path = resource_path.NormalizePathSeparators(); 121 resource_path = resource_path.NormalizePathSeparators();
111 122
112 DCHECK(path_to_resource_id->find(resource_path) == 123 DCHECK(path_to_resource_id->find(resource_path) ==
113 path_to_resource_id->end()); 124 path_to_resource_id->end());
114 (*path_to_resource_id)[resource_path] = entries[i].value; 125 (*path_to_resource_id)[resource_path] = entries[i].value;
115 } 126 }
116 } 127 }
117 128
129 // Returns a PNG image data URL of a given image.
130 GURL GetImageDataURL(const gfx::Image& image) {
131 if (image.IsEmpty())
132 return GURL();
133
134 scoped_refptr<base::RefCountedMemory> mem =
135 ImageLoader::BitmapToMemory(image.ToSkBitmap());
136 std::string contents_base64;
137 if (!base::Base64Encode(
138 std::string(reinterpret_cast<const char*>(mem->front()), mem->size()),
139 &contents_base64))
140 return GURL();
141
142 const char kDataURLPrefix[] = ":image/png;base64,";
143 return GURL(std::string(chrome::kDataScheme) + kDataURLPrefix +
144 contents_base64);
145 }
146
147 // Converts the image to grayscale.
148 SkBitmap DesaturateImage(const SkBitmap* image) {
149 color_utils::HSL shift = {-1, 0, 0.6};
150 return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
151 }
152
153 // Creates an image from PNG data.
154 SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
155 SkBitmap* decoded = new SkBitmap();
156 bool success = gfx::PNGCodec::Decode(data, size, decoded);
157 DCHECK(success);
158 return decoded;
159 }
160
161 // Load an image from a resource given its identifier |resource_id|.
162 SkBitmap* GetImageByResourceId(int resource_id) {
163 std::string contents = ResourceBundle::GetSharedInstance()
164 .GetRawDataResourceForScale(resource_id,
165 ui::SCALE_FACTOR_100P).as_string();
166
167 // Convert and return it.
168 const unsigned char* data =
169 reinterpret_cast<const unsigned char*>(contents.data());
170 return ToBitmap(data, contents.length());
171 }
172
118 } // namespace 173 } // namespace
119 174
120 namespace extensions { 175 namespace extensions {
121 176
122 //////////////////////////////////////////////////////////////////////////////// 177 ////////////////////////////////////////////////////////////////////////////////
123 // ImageLoader::ImageRepresentation 178 // ImageLoader::ImageRepresentation
124 179
125 ImageLoader::ImageRepresentation::ImageRepresentation( 180 ImageLoader::ImageRepresentation::ImageRepresentation(
126 const ExtensionResource& resource, 181 const ExtensionResource& resource,
127 ResizeCondition resize_condition, 182 ResizeCondition resize_condition,
(...skipping 30 matching lines...) Expand all
158 original_size(original_size), 213 original_size(original_size),
159 image_representation(image_representation) { 214 image_representation(image_representation) {
160 } 215 }
161 216
162 ImageLoader::LoadResult::~LoadResult() { 217 ImageLoader::LoadResult::~LoadResult() {
163 } 218 }
164 219
165 //////////////////////////////////////////////////////////////////////////////// 220 ////////////////////////////////////////////////////////////////////////////////
166 // ImageLoader 221 // ImageLoader
167 222
168 ImageLoader::ImageLoader() 223 ImageLoader::ImageLoader(Profile* profile)
169 : weak_ptr_factory_(this) { 224 : weak_ptr_factory_(this), profile_(profile) {
170 } 225 }
171 226
172 ImageLoader::~ImageLoader() { 227 ImageLoader::~ImageLoader() {
173 } 228 }
174 229
175 // static 230 // static
176 ImageLoader* ImageLoader::Get(Profile* profile) { 231 ImageLoader* ImageLoader::Get(Profile* profile) {
177 return ImageLoaderFactory::GetForProfile(profile); 232 return ImageLoaderFactory::GetForProfile(profile);
178 } 233 }
179 234
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 395
341 gfx::Image image; 396 gfx::Image image;
342 if (!image_skia.isNull()) { 397 if (!image_skia.isNull()) {
343 image_skia.MakeThreadSafe(); 398 image_skia.MakeThreadSafe();
344 image = gfx::Image(image_skia); 399 image = gfx::Image(image_skia);
345 } 400 }
346 401
347 callback.Run(image); 402 callback.Run(image);
348 } 403 }
349 404
405 void ImageLoader::LoadExtensionIconAsync(
406 const extensions::Extension* extension,
407 int icon_size,
408 bool grayscale,
409 const base::Callback<void(const gfx::Image&)>& callback) {
410 // |extension| can be NULL when the default icon is requested. In this case,
411 // fail the icon loading, which will result in the default icon being loaded.
412 if (extension == NULL) {
413 LoadIconFailed(extension, icon_size, grayscale, callback);
414 return;
415 }
416
417 ExtensionResource resource = IconsInfo::GetIconResource(
418 extension, icon_size, ExtensionIconSet::MATCH_BIGGER);
419 LoadImageAsync(extension,
420 resource,
421 gfx::Size(icon_size, icon_size),
422 base::Bind(&ImageLoader::LoadExtensionIconDone,
423 base::Unretained(this),
424 extension,
425 icon_size,
426 grayscale,
427 callback));
428 }
429
430 void ImageLoader::LoadExtensionIconDone(
431 const extensions::Extension* extension,
432 int icon_size,
433 bool grayscale,
434 const base::Callback<void(const gfx::Image&)>& callback,
435 const gfx::Image& image) {
436 if (image.IsEmpty())
437 LoadIconFailed(extension, icon_size, grayscale, callback);
438 else
439 FinalizeImage(image, grayscale, callback);
440 }
441
442 void ImageLoader::LoadIconFailed(
443 const extensions::Extension* extension,
444 int icon_size,
445 bool grayscale,
446 const base::Callback<void(const gfx::Image&)>& callback) {
447 if ((extension != NULL) &&
448 (icon_size == extension_misc::EXTENSION_ICON_BITTY))
449 LoadFaviconImage(extension, icon_size, grayscale, callback);
450 else
451 LoadDefaultImage(extension, icon_size, grayscale, callback);
452 }
453
454 void ImageLoader::LoadFaviconImage(
455 const extensions::Extension* extension,
456 int icon_size,
457 bool grayscale,
458 const base::Callback<void(const gfx::Image&)>& callback) {
459 FaviconService* favicon_service = NULL;
460 if (profile_ != NULL)
461 favicon_service = FaviconServiceFactory::GetForProfile(
462 profile_, Profile::EXPLICIT_ACCESS);
463 // Fall back to the default icons if the service isn't available.
464 if (favicon_service == NULL) {
465 LoadDefaultImage(extension, icon_size, grayscale, callback);
466 return;
467 }
468
469 GURL favicon_url = AppLaunchInfo::GetFullLaunchURL(extension);
470 favicon_service->GetRawFaviconForURL(
471 FaviconService::FaviconForURLParams(
472 profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
473 ui::SCALE_FACTOR_100P,
474 base::Bind(&ImageLoader::OnFaviconDataAvailable,
475 base::Unretained(this),
476 extension,
477 icon_size,
478 grayscale,
479 callback),
480 &cancelable_task_tracker_);
481 }
482
483 void ImageLoader::OnFaviconDataAvailable(
484 const extensions::Extension* extension,
485 int icon_size,
486 bool grayscale,
487 const base::Callback<void(const gfx::Image&)>& callback,
488 const chrome::FaviconBitmapResult& bitmap_result) {
489 // Fallback to the default icon if there wasn't a favicon.
490 if (!bitmap_result.is_valid()) {
491 LoadDefaultImage(extension, icon_size, grayscale, callback);
492 return;
493 }
494
495 gfx::Image image = gfx::Image::CreateFrom1xBitmap(*ToBitmap(
496 bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size()));
497 FinalizeImage(image, grayscale, callback);
498 }
499
500 void ImageLoader::LoadDefaultImage(
501 const extensions::Extension* extension,
502 int icon_size,
503 bool grayscale,
504 const base::Callback<void(const gfx::Image&)>& callback) {
505 content::BrowserThread::PostTask(
506 content::BrowserThread::FILE,
507 FROM_HERE,
508 base::Bind(&ImageLoader::LoadDefaultImageOnFileThread,
509 base::Unretained(this),
510 extension,
511 icon_size,
512 grayscale,
513 callback));
514 }
515
516 void ImageLoader::LoadDefaultImageOnFileThread(
517 const extensions::Extension* extension,
518 int icon_size,
519 bool grayscale,
520 const base::Callback<void(const gfx::Image&)>& callback) {
521 const SkBitmap* default_image = NULL;
522 if ((extension == NULL) || (extension->is_app()))
523 default_image = GetDefaultAppImage();
524 else
525 default_image = GetDefaultExtensionImage();
526
527 SkBitmap result_image;
528 if (icon_size == -1) {
529 // If a specific size was not requested.
530 result_image = *default_image;
531 } else {
532 SkBitmap resized_image(
533 skia::ImageOperations::Resize(*default_image,
534 skia::ImageOperations::RESIZE_LANCZOS3,
535 icon_size,
536 icon_size));
537 // There are cases where Resize returns an empty bitmap, for example if you
538 // ask for an image too large. In this case it is better to return the
539 // default image than returning nothing at all.
540 if (resized_image.empty())
541 resized_image = *default_image;
542 result_image = resized_image;
543 }
544
545 content::BrowserThread::PostTask(
546 content::BrowserThread::UI,
547 FROM_HERE,
548 base::Bind(&ImageLoader::LoadDefaultImageDone,
549 base::Unretained(this),
550 gfx::Image::CreateFrom1xBitmap(result_image),
551 grayscale,
552 callback));
553 }
554
555 void ImageLoader::LoadDefaultImageDone(
556 const gfx::Image& image,
557 bool grayscale,
558 const base::Callback<void(const gfx::Image&)>& callback) {
559 FinalizeImage(image, grayscale, callback);
560 }
561
562 void ImageLoader::FinalizeImage(
563 const gfx::Image& image,
564 bool grayscale,
565 const base::Callback<void(const gfx::Image&)>& callback) {
566 content::BrowserThread::PostTask(
567 content::BrowserThread::FILE,
568 FROM_HERE,
569 base::Bind(&ImageLoader::FinalizeImageOnFileThread,
570 base::Unretained(this),
571 image,
572 grayscale,
573 callback));
574 }
575
576 void ImageLoader::FinalizeImageOnFileThread(
577 const gfx::Image& image,
578 bool grayscale,
579 const base::Callback<void(const gfx::Image&)>& callback) {
580 SkBitmap bitmap;
581 if (grayscale)
582 bitmap = DesaturateImage(image.ToSkBitmap());
583 else
584 bitmap = *image.ToSkBitmap();
585
586 gfx::Image modifiedImage = gfx::Image::CreateFrom1xBitmap(bitmap);
587 content::BrowserThread::PostTask(content::BrowserThread::UI,
588 FROM_HERE,
589 base::Bind(&ImageLoader::FinalizeImageDone,
590 base::Unretained(this),
591 modifiedImage,
592 callback));
593 }
594
595 void ImageLoader::FinalizeImageDone(
596 const gfx::Image& image,
597 const base::Callback<void(const gfx::Image&)>& callback) {
598 callback.Run(image);
599 }
600
601 void ImageLoader::LoadExtensionIconDataURLAsync(
602 const extensions::Extension* extension,
603 int icon_size,
604 bool grayscale,
605 const base::Callback<void(const GURL&)>& callback) {
606 LoadExtensionIconAsync(extension,
607 icon_size,
608 grayscale,
609 base::Bind(&ImageLoader::OnIconAvailable,
610 base::Unretained(this),
611 callback));
612 }
613
614 void ImageLoader::OnIconAvailable(
615 const base::Callback<void(const GURL&)>& callback,
616 const gfx::Image& image) {
617 content::BrowserThread::PostTask(
618 content::BrowserThread::FILE,
619 FROM_HERE,
620 base::Bind(&ImageLoader::ConvertIconToURLOnFileThread,
621 base::Unretained(this),
622 image,
623 callback));
624 }
625
626 void ImageLoader::ConvertIconToURLOnFileThread(
627 const gfx::Image& image,
628 const base::Callback<void(const GURL&)>& callback) {
629 GURL url = GetImageDataURL(image);
630 content::BrowserThread::PostTask(
631 content::BrowserThread::UI,
632 FROM_HERE,
633 base::Bind(&ImageLoader::OnIconConvertedToURL,
634 base::Unretained(this),
635 url,
636 callback));
637 }
638
639 void ImageLoader::OnIconConvertedToURL(
640 const GURL& url,
641 const base::Callback<void(const GURL&)>& callback) {
642 callback.Run(url);
643 }
644
645 const SkBitmap* ImageLoader::GetDefaultAppImage() {
646 if (!default_app_icon_.get())
647 default_app_icon_.reset(GetImageByResourceId(IDR_APP_DEFAULT_ICON));
648
649 return default_app_icon_.get();
650 }
651
652 const SkBitmap* ImageLoader::GetDefaultExtensionImage() {
653 if (!default_extension_icon_.get()) {
654 default_extension_icon_.reset(
655 GetImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
656 }
657
658 return default_extension_icon_.get();
659 }
660
661 scoped_refptr<base::RefCountedMemory> ImageLoader::BitmapToMemory(
662 const SkBitmap* image) {
663 base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
664 gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
665 return image_bytes;
666 }
667
350 } // namespace extensions 668 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698