1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
|
// Copyright (c) 2006-2008 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/icon_loader.h"
#include <windows.h>
#include <shellapi.h>
#include "base/file_util.h"
#include "base/gfx/size.h"
#include "base/message_loop.h"
#include "base/ref_counted.h"
#include "base/string_util.h"
#include "base/task.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/gfx/icon_util.h"
#include "SkBitmap.h"
namespace {
class IconLoaderProcessor :
public base::RefCountedThreadSafe<IconLoaderProcessor> {
public:
explicit IconLoaderProcessor(IconLoader* target)
: target_(target),
bitmap_(NULL),
small_icon_(NULL),
large_icon_(NULL),
loading_from_resource_(target->loading_from_resource_),
icon_type_(target->icon_type_),
icon_size_(target->icon_size_) {
DCHECK(target);
path_ = target->path_;
target_message_loop_ = MessageLoop::current();
}
virtual ~IconLoaderProcessor() {
delete bitmap_;
if (small_icon_)
::DestroyIcon(small_icon_);
if (large_icon_) {
::DestroyIcon(large_icon_);
}
}
// Loads the icon with the specified dimensions.
HICON LoadSizedIcon(int width, int height) {
return static_cast<HICON>(LoadImage(NULL,
path_.c_str(),
width, height,
IMAGE_ICON,
LR_LOADTRANSPARENT | LR_LOADFROMFILE));
}
// Invoked from the original thread.
void Cancel() {
target_ = NULL;
}
// Invoked in the file thread. Never access target_ from this method.
void ReadIcon() {
if (loading_from_resource_)
ReadIconFromFileResource();
else
ReadIconFile();
target_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &IconLoaderProcessor::NotifyFetcher));
}
// Invoked on the file thread to read a normal .ico file.
void ReadIconFile() {
// We start by loading the same icon image in the two desired dimensions,
// based on the dimensions we get back when we query the system.
int small_width = ::GetSystemMetrics(SM_CXSMICON);
int small_height = ::GetSystemMetrics(SM_CYSMICON);
int large_width = ::GetSystemMetrics(SM_CXICON);
int large_height = ::GetSystemMetrics(SM_CYICON);
small_icon_ = LoadSizedIcon(small_width, small_height);
large_icon_ = LoadSizedIcon(large_width, large_height);
// TODO(idana): Bug 991356. Currently, if the client asks for the icon in
// the form of an SkBitmap object, then we try converting the large icon
// into an SkBitmap, if the icon was successfully loaded. If the large icon
// is not available, we convert the small icon. The problem with converting
// the small or the large icon is that the resulting image is going to have
// the same dimensions as the icon (for example, 32X32 pixels). This can be
// problematic if, for example, the client tries to display the resulting
// image as a thumbnail. This will result in the client streching the
// bitmap from 32X32 to 128X128 which will decrease the image's quality.
//
// Since it is possible for web applications to define large .PNG images as
// their icons, the resulting .ico files created for these web applications
// contain icon images in sizes much larger the the system default sizes
// for icons. We can solve the aforementioned limitation by allowing the
// client to specify the size of the resulting image, when requesting an
// SkBitmap. The IconLoader code can then load a larger icon from the .ico
// file.
//
// Note that currently the components in Chrome that deal with SkBitmaps
// that represent application icons use the images to display icon size
// images and therefore the limitation above doesn't really manifest
// itself.
HICON icon_to_convert = NULL;
gfx::Size s;
if (icon_type_ == IconLoader::SK_BITMAP) {
if (large_icon_) {
icon_to_convert = large_icon_;
s.SetSize(large_width, large_height);
} else if (small_icon_) {
icon_to_convert = small_icon_;
s.SetSize(small_width, small_height);
}
}
if (icon_to_convert) {
bitmap_ = IconUtil::CreateSkBitmapFromHICON(icon_to_convert, s);
DCHECK(bitmap_);
if (small_icon_)
::DestroyIcon(small_icon_);
if (large_icon_)
::DestroyIcon(large_icon_);
small_icon_ = NULL;
large_icon_ = NULL;
}
}
void ReadIconFromFileResource() {
int size = 0;
switch (icon_size_) {
case IconLoader::SMALL:
size = SHGFI_SMALLICON;
break;
case IconLoader::NORMAL:
size = 0;
break;
case IconLoader::LARGE:
size = SHGFI_LARGEICON;
break;
default:
NOTREACHED();
}
SHFILEINFO file_info = { 0 };
if (!SHGetFileInfo(path_.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
sizeof(SHFILEINFO),
SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES))
return;
ICONINFO icon_info = { 0 };
BITMAP bitmap_info = { 0 };
BOOL r = ::GetIconInfo(file_info.hIcon, &icon_info);
DCHECK(r);
r = ::GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info);
DCHECK(r);
gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight);
bitmap_ = IconUtil::CreateSkBitmapFromHICON(file_info.hIcon, icon_size);
}
// Invoked in the target thread.
void NotifyFetcher() {
if (target_) {
switch (target_->icon_type_) {
case IconLoader::SK_BITMAP:
if (target_->OnLoadComplete(bitmap_)) {
// Receiver took ownership of the bitmap.
bitmap_ = NULL;
}
break;
case IconLoader::WINDOWS_HICON:
if (target_->OnLoadComplete(small_icon_, large_icon_)) {
// Receiver took ownership of the icons.
small_icon_ = NULL;
large_icon_ = NULL;
}
break;
default:
NOTREACHED();
}
}
}
private:
// The message loop object of the thread in which we notify the delegate.
MessageLoop* target_message_loop_;
// The corresponding file fetcher or NULL if this task has been canceled.
IconLoader* target_;
// The path of the file.
std::wstring path_;
// Fields from IconLoader that we need to copy as we cannot access them
// directly from the target_ in a thread-safe way.
bool loading_from_resource_;
IconLoader::IconSize icon_size_;
IconLoader::IconType icon_type_;
// The result bitmap.
SkBitmap* bitmap_;
// The result small icon.
HICON small_icon_;
// The result large icon.
HICON large_icon_;
DISALLOW_EVIL_CONSTRUCTORS(IconLoaderProcessor);
};
}
// static
IconLoader* IconLoader::CreateIconLoaderForFile(const std::wstring& path,
IconType icon_type,
Delegate* delegate) {
// Note: the icon size is unused in this case.
return new IconLoader(path, icon_type, false, IconLoader::NORMAL, delegate);
}
// static
IconLoader* IconLoader::CreateIconLoaderForFileResource(
const std::wstring& path, IconSize size, Delegate* delegate) {
return new IconLoader(path, IconLoader::SK_BITMAP, true, size, delegate);
}
IconLoader::IconLoader(const std::wstring& path,
IconType type,
bool from_resource,
IconSize size,
Delegate* delegate)
: path_(path),
icon_type_(type),
loading_from_resource_(from_resource),
icon_size_(size),
delegate_(delegate),
processor_(NULL) {
DCHECK(delegate_);
}
IconLoader::~IconLoader() {
Cancel();
}
void IconLoader::Start() {
processor_ = new IconLoaderProcessor(this);
processor_->AddRef();
g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(processor_, &IconLoaderProcessor::ReadIcon));
}
void IconLoader::Cancel() {
if (processor_) {
processor_->Cancel();
processor_->Release();
processor_ = NULL;
}
delegate_ = NULL;
}
bool IconLoader::OnLoadComplete(SkBitmap* bitmap) {
if (delegate_) {
return delegate_->OnSkBitmapLoaded(this, bitmap);
// We are likely deleted after this point.
}
return false;
}
bool IconLoader::OnLoadComplete(HICON small_icon, HICON large_icon) {
if (delegate_) {
return delegate_->OnHICONLoaded(this, small_icon, large_icon);
// We are likely deleted after this point.
}
return false;
}
|