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

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: Fixes based on the feedback of finnur@ 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 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| is NULL when we force loading of the default icon.
Finnur 2013/10/01 11:14:16 nit: s/is/can be/
dvh 2013/10/02 05:15:09 Done.
411 if (extension == NULL)
412 LoadIconFailed(extension, icon_size, grayscale, callback);
413
414 ExtensionResource resource = IconsInfo::GetIconResource(
415 extension, icon_size, ExtensionIconSet::MATCH_BIGGER);
416 LoadImageAsync(extension,
417 resource,
418 gfx::Size(icon_size, icon_size),
419 base::Bind(&ImageLoader::LoadExtensionIconDone,
420 base::Unretained(this),
421 extension,
422 icon_size,
423 grayscale,
424 callback));
425 }
426
427 void ImageLoader::LoadExtensionIconDone(
428 const extensions::Extension* extension,
429 int icon_size,
430 bool grayscale,
431 const base::Callback<void(const gfx::Image&)>& callback,
432 const gfx::Image& image) {
433 if (image.IsEmpty())
434 LoadIconFailed(extension, icon_size, grayscale, callback);
435 else
436 FinalizeImage(image, grayscale, callback);
437 }
438
439 void ImageLoader::LoadIconFailed(
440 const extensions::Extension* extension,
441 int icon_size,
442 bool grayscale,
443 const base::Callback<void(const gfx::Image&)>& callback) {
444 if ((extension != NULL) &&
445 (icon_size == extension_misc::EXTENSION_ICON_BITTY))
446 LoadFaviconImage(extension, icon_size, grayscale, callback);
447 else
448 LoadDefaultImage(extension, icon_size, grayscale, callback);
449 }
450
451 void ImageLoader::LoadFaviconImage(
452 const extensions::Extension* extension,
453 int icon_size,
454 bool grayscale,
455 const base::Callback<void(const gfx::Image&)>& callback) {
456 FaviconService* favicon_service =
457 FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
458 // Fall back to the default icons if the service isn't available.
459 if (favicon_service == NULL) {
460 LoadDefaultImage(extension, icon_size, grayscale, callback);
461 return;
462 }
463
464 GURL favicon_url = AppLaunchInfo::GetFullLaunchURL(extension);
465 favicon_service->GetRawFaviconForURL(
466 FaviconService::FaviconForURLParams(
467 profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
468 ui::SCALE_FACTOR_100P,
469 base::Bind(&ImageLoader::OnFaviconDataAvailable,
470 base::Unretained(this),
471 extension,
472 icon_size,
473 grayscale,
474 callback),
475 &cancelable_task_tracker_);
476 }
477
478 void ImageLoader::OnFaviconDataAvailable(
479 const extensions::Extension* extension,
480 int icon_size,
481 bool grayscale,
482 const base::Callback<void(const gfx::Image&)>& callback,
483 const chrome::FaviconBitmapResult& bitmap_result) {
484 // Fallback to the default icon if there wasn't a favicon.
485 if (!bitmap_result.is_valid()) {
486 LoadDefaultImage(extension, icon_size, grayscale, callback);
487 return;
488 }
489
490 gfx::Image image = gfx::Image::CreateFrom1xBitmap(*ToBitmap(
491 bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size()));
492 FinalizeImage(image, grayscale, callback);
493 }
494
495 void ImageLoader::LoadDefaultImage(
496 const extensions::Extension* extension,
497 int icon_size,
498 bool grayscale,
499 const base::Callback<void(const gfx::Image&)>& callback) {
500 content::BrowserThread::PostTask(
501 content::BrowserThread::FILE,
502 FROM_HERE,
503 base::Bind(&ImageLoader::LoadDefaultImageOnFileThread,
504 base::Unretained(this),
505 extension,
506 icon_size,
507 grayscale,
508 callback));
509 }
510
511 void ImageLoader::LoadDefaultImageOnFileThread(
512 const extensions::Extension* extension,
513 int icon_size,
514 bool grayscale,
515 const base::Callback<void(const gfx::Image&)>& callback) {
516 const SkBitmap* default_image = NULL;
517 if ((extension == NULL) || (extension->is_app()))
518 default_image = GetDefaultAppImage();
519 else
520 default_image = GetDefaultExtensionImage();
521
522 SkBitmap result_image;
523 if (icon_size == -1) {
524 // If a specific size was not requested.
525 result_image = *default_image;
526 } else {
527 SkBitmap resized_image(
528 skia::ImageOperations::Resize(*default_image,
529 skia::ImageOperations::RESIZE_LANCZOS3,
530 icon_size,
531 icon_size));
532 // There are cases where Resize returns an empty bitmap, for example if you
533 // ask for an image too large. In this case it is better to return the
534 // default image than returning nothing at all.
535 if (resized_image.empty())
536 resized_image = *default_image;
537 result_image = resized_image;
538 }
539
540 content::BrowserThread::PostTask(
541 content::BrowserThread::UI,
542 FROM_HERE,
543 base::Bind(&ImageLoader::LoadDefaultImageDone,
544 base::Unretained(this),
545 gfx::Image::CreateFrom1xBitmap(result_image),
546 grayscale,
547 callback));
548 }
549
550 void ImageLoader::LoadDefaultImageDone(
551 const gfx::Image& image,
552 bool grayscale,
553 const base::Callback<void(const gfx::Image&)>& callback) {
554 FinalizeImage(image, grayscale, callback);
555 }
556
557 void ImageLoader::FinalizeImage(
558 const gfx::Image& image,
559 bool grayscale,
560 const base::Callback<void(const gfx::Image&)>& callback) {
561 content::BrowserThread::PostTask(
562 content::BrowserThread::FILE,
563 FROM_HERE,
564 base::Bind(&ImageLoader::FinalizeImageOnFileThread,
565 base::Unretained(this),
566 image,
567 grayscale,
568 callback));
569 }
570
571 void ImageLoader::FinalizeImageOnFileThread(
572 const gfx::Image& image,
573 bool grayscale,
574 const base::Callback<void(const gfx::Image&)>& callback) {
575 SkBitmap bitmap;
576 if (grayscale)
577 bitmap = DesaturateImage(image.ToSkBitmap());
578 else
579 bitmap = *image.ToSkBitmap();
580
581 gfx::Image modifiedImage = gfx::Image::CreateFrom1xBitmap(bitmap);
582 content::BrowserThread::PostTask(content::BrowserThread::UI,
583 FROM_HERE,
584 base::Bind(&ImageLoader::FinalizeImageDone,
585 base::Unretained(this),
586 modifiedImage,
587 callback));
588 }
589
590 void ImageLoader::FinalizeImageDone(
591 const gfx::Image& image,
592 const base::Callback<void(const gfx::Image&)>& callback) {
593 callback.Run(image);
594 }
595
596 void ImageLoader::LoadExtensionIconDataURLAsync(
597 const extensions::Extension* extension,
598 int icon_size,
599 bool grayscale,
600 const base::Callback<void(const GURL&)>& callback) {
601 LoadExtensionIconAsync(extension,
602 icon_size,
603 grayscale,
604 base::Bind(&ImageLoader::OnIconAvailable,
605 base::Unretained(this),
606 callback));
607 }
608
609 void ImageLoader::OnIconAvailable(
610 const base::Callback<void(const GURL&)>& callback,
611 const gfx::Image& image) {
612 content::BrowserThread::PostTask(
613 content::BrowserThread::FILE,
614 FROM_HERE,
615 base::Bind(&ImageLoader::ConvertIconToURLOnFileThread,
616 base::Unretained(this),
617 image,
618 callback));
619 }
620
621 void ImageLoader::ConvertIconToURLOnFileThread(
622 const gfx::Image& image,
623 const base::Callback<void(const GURL&)>& callback) {
624 GURL url = GetImageDataURL(image);
625 content::BrowserThread::PostTask(
626 content::BrowserThread::UI,
627 FROM_HERE,
628 base::Bind(&ImageLoader::OnIconConvertedToURL,
629 base::Unretained(this),
630 url,
631 callback));
632 }
633
634 void ImageLoader::OnIconConvertedToURL(
635 const GURL& url,
636 const base::Callback<void(const GURL&)>& callback) {
637 callback.Run(url);
638 }
639
640 const SkBitmap* ImageLoader::GetDefaultAppImage() {
641 if (!default_app_data_.get())
642 default_app_data_.reset(GetImageByResourceId(IDR_APP_DEFAULT_ICON));
643
644 return default_app_data_.get();
645 }
646
647 const SkBitmap* ImageLoader::GetDefaultExtensionImage() {
648 if (!default_extension_data_.get()) {
649 default_extension_data_.reset(
650 GetImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
651 }
652
653 return default_extension_data_.get();
654 }
655
656 scoped_refptr<base::RefCountedMemory> ImageLoader::BitmapToMemory(
657 const SkBitmap* image) {
658 base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
659 gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
660 return image_bytes;
661 }
662
350 } // namespace extensions 663 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698