OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/extensions/extension_icon_image.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.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" | |
21 | |
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 | |
30 // ImageLoader. | |
31 // - |ImageLoader| is asynchronous. | |
32 // - ImageSkiaSource will initially return transparent image resource of the | |
33 // desired size. | |
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 | |
39 // representation). | |
40 | |
41 namespace { | |
42 | |
43 const int kMatchBiggerTreshold = 32; | |
44 | |
45 extensions::ExtensionResource GetExtensionIconResource( | |
46 const extensions::Extension* extension, | |
47 const ExtensionIconSet& icons, | |
48 int size, | |
49 ExtensionIconSet::MatchType match_type) { | |
50 std::string path = icons.Get(size, match_type); | |
51 if (path.empty()) | |
52 return extensions::ExtensionResource(); | |
53 | |
54 return extension->GetResource(path); | |
55 } | |
56 | |
57 class BlankImageSource : public gfx::CanvasImageSource { | |
58 public: | |
59 explicit BlankImageSource(const gfx::Size& size_in_dip) | |
60 : CanvasImageSource(size_in_dip, /*is_opaque =*/ false) { | |
61 } | |
62 virtual ~BlankImageSource() {} | |
63 | |
64 private: | |
65 // gfx::CanvasImageSource overrides: | |
66 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { | |
67 canvas->DrawColor(SkColorSetARGB(0, 0, 0, 0)); | |
68 } | |
69 | |
70 DISALLOW_COPY_AND_ASSIGN(BlankImageSource); | |
71 }; | |
72 | |
73 } // namespace | |
74 | |
75 namespace extensions { | |
76 | |
77 //////////////////////////////////////////////////////////////////////////////// | |
78 // IconImage::Source | |
79 | |
80 class IconImage::Source : public gfx::ImageSkiaSource { | |
81 public: | |
82 Source(IconImage* host, const gfx::Size& size_in_dip); | |
83 virtual ~Source(); | |
84 | |
85 void ResetHost(); | |
86 | |
87 private: | |
88 // gfx::ImageSkiaSource overrides: | |
89 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE; | |
90 | |
91 // Used to load images, possibly asynchronously. NULLed out when the IconImage | |
92 // is destroyed. | |
93 IconImage* host_; | |
94 | |
95 // Image whose representations will be used until |host_| loads the real | |
96 // representations for the image. | |
97 gfx::ImageSkia blank_image_; | |
98 | |
99 DISALLOW_COPY_AND_ASSIGN(Source); | |
100 }; | |
101 | |
102 IconImage::Source::Source(IconImage* host, const gfx::Size& size_in_dip) | |
103 : host_(host), | |
104 blank_image_(new BlankImageSource(size_in_dip), size_in_dip) { | |
105 } | |
106 | |
107 IconImage::Source::~Source() { | |
108 } | |
109 | |
110 void IconImage::Source::ResetHost() { | |
111 host_ = NULL; | |
112 } | |
113 | |
114 gfx::ImageSkiaRep IconImage::Source::GetImageForScale(float scale) { | |
115 gfx::ImageSkiaRep representation; | |
116 if (host_) { | |
117 representation = | |
118 host_->LoadImageForScaleFactor(ui::GetSupportedScaleFactor(scale)); | |
119 } | |
120 | |
121 if (!representation.is_null()) | |
122 return representation; | |
123 | |
124 return blank_image_.GetRepresentation(scale); | |
125 } | |
126 | |
127 //////////////////////////////////////////////////////////////////////////////// | |
128 // IconImage | |
129 | |
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, | |
136 Observer* observer) | |
137 : browser_context_(context), | |
138 extension_(extension), | |
139 icon_set_(icon_set), | |
140 resource_size_in_dip_(resource_size_in_dip), | |
141 observer_(observer), | |
142 source_(NULL), | |
143 default_icon_(gfx::ImageSkiaOperations::CreateResizedImage( | |
144 default_icon, | |
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); | |
151 | |
152 registrar_.Add(this, | |
153 chrome::NOTIFICATION_EXTENSION_REMOVED, | |
154 content::NotificationService::AllSources()); | |
155 } | |
156 | |
157 IconImage::~IconImage() { | |
158 source_->ResetHost(); | |
159 } | |
160 | |
161 gfx::ImageSkiaRep IconImage::LoadImageForScaleFactor( | |
162 ui::ScaleFactor scale_factor) { | |
163 // Do nothing if extension is unloaded. | |
164 if (!extension_) | |
165 return gfx::ImageSkiaRep(); | |
166 | |
167 const float scale = ui::GetScaleForScaleFactor(scale_factor); | |
168 const int resource_size_in_pixel = | |
169 static_cast<int>(resource_size_in_dip_ * scale); | |
170 | |
171 extensions::ExtensionResource resource; | |
172 | |
173 // Find extension resource for non bundled component extensions. | |
174 // We try loading bigger image only if resource size is >= 32. | |
175 if (resource_size_in_pixel >= kMatchBiggerTreshold) { | |
176 resource = GetExtensionIconResource(extension_, icon_set_, | |
177 resource_size_in_pixel, ExtensionIconSet::MATCH_BIGGER); | |
178 } | |
179 | |
180 // If resource is not found by now, try matching smaller one. | |
181 if (resource.empty()) { | |
182 resource = GetExtensionIconResource(extension_, icon_set_, | |
183 resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER); | |
184 } | |
185 | |
186 // If there is no resource found, return default icon. | |
187 if (resource.empty()) | |
188 return default_icon_.GetRepresentation(scale); | |
189 | |
190 std::vector<ImageLoader::ImageRepresentation> info_list; | |
191 info_list.push_back(ImageLoader::ImageRepresentation( | |
192 resource, | |
193 ImageLoader::ImageRepresentation::ALWAYS_RESIZE, | |
194 gfx::ToFlooredSize(gfx::ScaleSize( | |
195 gfx::Size(resource_size_in_dip_, resource_size_in_dip_), scale)), | |
196 scale_factor)); | |
197 | |
198 extensions::ImageLoader* loader = | |
199 extensions::ImageLoader::Get(browser_context_); | |
200 loader->LoadImagesAsync(extension_, info_list, | |
201 base::Bind(&IconImage::OnImageLoaded, | |
202 weak_ptr_factory_.GetWeakPtr(), | |
203 scale)); | |
204 | |
205 return gfx::ImageSkiaRep(); | |
206 } | |
207 | |
208 void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) { | |
209 const gfx::ImageSkia* image = | |
210 image_in.IsEmpty() ? &default_icon_ : image_in.ToImageSkia(); | |
211 | |
212 // Maybe default icon was not set. | |
213 if (image->isNull()) | |
214 return; | |
215 | |
216 gfx::ImageSkiaRep rep = image->GetRepresentation(scale); | |
217 DCHECK(!rep.is_null()); | |
218 DCHECK_EQ(scale, rep.scale()); | |
219 | |
220 // Remove old representation if there is one. | |
221 image_skia_.RemoveRepresentation(scale); | |
222 image_skia_.AddRepresentation(rep); | |
223 | |
224 if (observer_) | |
225 observer_->OnExtensionIconImageChanged(this); | |
226 } | |
227 | |
228 void IconImage::Observe(int type, | |
229 const content::NotificationSource& source, | |
230 const content::NotificationDetails& details) { | |
231 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_REMOVED); | |
232 | |
233 const Extension* extension = content::Details<const Extension>(details).ptr(); | |
234 | |
235 if (extension_ == extension) | |
236 extension_ = NULL; | |
237 } | |
238 | |
239 } // namespace extensions | |
OLD | NEW |