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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/extension_icon_image.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/image_loader.h"
12 #include "content/public/browser/notification_service.h"
13 #include "extensions/common/extension.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/image/canvas_image_source.h"
16 #include "ui/gfx/image/image.h"
17 #include "ui/gfx/image/image_skia_operations.h"
18 #include "ui/gfx/image/image_skia_source.h"
19 #include "ui/gfx/size.h"
20 #include "ui/gfx/size_conversions.h"
22 // The ImageSkia provided by extensions::IconImage contains ImageSkiaReps that
23 // are computed and updated using the following algorithm (if no default icon
24 // was supplied, transparent icon is considered the default):
25 // - |LoadImageForScaleFactors()| searches the extension for an icon of an
26 // appropriate size. If the extension doesn't have a icon resource needed for
27 // the image representation, the default icon's representation for the
28 // requested scale factor is returned by ImageSkiaSource.
29 // - If the extension has the resource, IconImage tries to load it using
31 // - |ImageLoader| is asynchronous.
32 // - ImageSkiaSource will initially return transparent image resource of the
34 // - The image will be updated with an appropriate image representation when
35 // the |ImageLoader| finishes. The image representation is chosen the same
36 // way as in the synchronous case. The observer is notified of the image
37 // change, unless the added image representation is transparent (in which
38 // case the image had already contained the appropriate image
43 const int kMatchBiggerTreshold
= 32;
45 extensions::ExtensionResource
GetExtensionIconResource(
46 const extensions::Extension
* extension
,
47 const ExtensionIconSet
& icons
,
49 ExtensionIconSet::MatchType match_type
) {
50 std::string path
= icons
.Get(size
, match_type
);
52 return extensions::ExtensionResource();
54 return extension
->GetResource(path
);
57 class BlankImageSource
: public gfx::CanvasImageSource
{
59 explicit BlankImageSource(const gfx::Size
& size_in_dip
)
60 : CanvasImageSource(size_in_dip
, /*is_opaque =*/ false) {
62 virtual ~BlankImageSource() {}
65 // gfx::CanvasImageSource overrides:
66 virtual void Draw(gfx::Canvas
* canvas
) OVERRIDE
{
67 canvas
->DrawColor(SkColorSetARGB(0, 0, 0, 0));
70 DISALLOW_COPY_AND_ASSIGN(BlankImageSource
);
75 namespace extensions
{
77 ////////////////////////////////////////////////////////////////////////////////
80 class IconImage::Source
: public gfx::ImageSkiaSource
{
82 Source(IconImage
* host
, const gfx::Size
& size_in_dip
);
88 // gfx::ImageSkiaSource overrides:
89 virtual gfx::ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
;
91 // Used to load images, possibly asynchronously. NULLed out when the IconImage
95 // Image whose representations will be used until |host_| loads the real
96 // representations for the image.
97 gfx::ImageSkia blank_image_
;
99 DISALLOW_COPY_AND_ASSIGN(Source
);
102 IconImage::Source::Source(IconImage
* host
, const gfx::Size
& size_in_dip
)
104 blank_image_(new BlankImageSource(size_in_dip
), size_in_dip
) {
107 IconImage::Source::~Source() {
110 void IconImage::Source::ResetHost() {
114 gfx::ImageSkiaRep
IconImage::Source::GetImageForScale(float scale
) {
115 gfx::ImageSkiaRep representation
;
118 host_
->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale
));
121 if (!representation
.is_null())
122 return representation
;
124 return blank_image_
.GetRepresentation(scale
);
127 ////////////////////////////////////////////////////////////////////////////////
130 IconImage::IconImage(
131 content::BrowserContext
* context
,
132 const Extension
* extension
,
133 const ExtensionIconSet
& icon_set
,
134 int resource_size_in_dip
,
135 const gfx::ImageSkia
& default_icon
,
137 : browser_context_(context
),
138 extension_(extension
),
140 resource_size_in_dip_(resource_size_in_dip
),
143 default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
145 skia::ImageOperations::RESIZE_BEST
,
146 gfx::Size(resource_size_in_dip
, resource_size_in_dip
))),
147 weak_ptr_factory_(this) {
148 gfx::Size
resource_size(resource_size_in_dip
, resource_size_in_dip
);
149 source_
= new Source(this, resource_size
);
150 image_skia_
= gfx::ImageSkia(source_
, resource_size
);
152 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED
,
153 content::NotificationService::AllSources());
156 IconImage::~IconImage() {
157 source_
->ResetHost();
160 gfx::ImageSkiaRep
IconImage::LoadImageForScaleFactor(
161 ui::ScaleFactor scale_factor
) {
162 // Do nothing if extension is unloaded.
164 return gfx::ImageSkiaRep();
166 const float scale
= ui::GetImageScale(scale_factor
);
167 const int resource_size_in_pixel
=
168 static_cast<int>(resource_size_in_dip_
* scale
);
170 extensions::ExtensionResource resource
;
172 // Find extension resource for non bundled component extensions.
173 // We try loading bigger image only if resource size is >= 32.
174 if (resource_size_in_pixel
>= kMatchBiggerTreshold
) {
175 resource
= GetExtensionIconResource(extension_
, icon_set_
,
176 resource_size_in_pixel
, ExtensionIconSet::MATCH_BIGGER
);
179 // If resource is not found by now, try matching smaller one.
180 if (resource
.empty()) {
181 resource
= GetExtensionIconResource(extension_
, icon_set_
,
182 resource_size_in_pixel
, ExtensionIconSet::MATCH_SMALLER
);
185 // If there is no resource found, return default icon.
186 if (resource
.empty())
187 return default_icon_
.GetRepresentation(scale
);
189 std::vector
<ImageLoader::ImageRepresentation
> info_list
;
190 info_list
.push_back(ImageLoader::ImageRepresentation(
192 ImageLoader::ImageRepresentation::ALWAYS_RESIZE
,
193 gfx::ToFlooredSize(gfx::ScaleSize(
194 gfx::Size(resource_size_in_dip_
, resource_size_in_dip_
), scale
)),
197 extensions::ImageLoader
* loader
=
198 extensions::ImageLoader::Get(browser_context_
);
199 loader
->LoadImagesAsync(extension_
, info_list
,
200 base::Bind(&IconImage::OnImageLoaded
,
201 weak_ptr_factory_
.GetWeakPtr(),
204 return gfx::ImageSkiaRep();
207 void IconImage::OnImageLoaded(float scale
, const gfx::Image
& image_in
) {
208 const gfx::ImageSkia
* image
=
209 image_in
.IsEmpty() ? &default_icon_
: image_in
.ToImageSkia();
211 // Maybe default icon was not set.
215 gfx::ImageSkiaRep rep
= image
->GetRepresentation(scale
);
216 DCHECK(!rep
.is_null());
217 DCHECK_EQ(scale
, rep
.scale());
219 // Remove old representation if there is one.
220 image_skia_
.RemoveRepresentation(scale
);
221 image_skia_
.AddRepresentation(rep
);
224 observer_
->OnExtensionIconImageChanged(this);
227 void IconImage::Observe(int type
,
228 const content::NotificationSource
& source
,
229 const content::NotificationDetails
& details
) {
230 DCHECK_EQ(type
, chrome::NOTIFICATION_EXTENSION_UNLOADED
);
232 const Extension
* extension
=
233 content::Details
<extensions::UnloadedExtensionInfo
>(details
)->extension
;
235 if (extension_
== extension
)
239 } // namespace extensions