OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "ui/gfx/icon_util.h" | |
6 | |
7 #include "base/files/file_util.h" | |
8 #include "base/files/scoped_temp_dir.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/path_service.h" | |
11 #include "testing/gtest/include/gtest/gtest.h" | |
12 #include "third_party/skia/include/core/SkBitmap.h" | |
13 #include "ui/gfx/gfx_paths.h" | |
14 #include "ui/gfx/icon_util_unittests_resource.h" | |
15 #include "ui/gfx/image/image.h" | |
16 #include "ui/gfx/image/image_family.h" | |
17 #include "ui/gfx/size.h" | |
18 | |
19 namespace { | |
20 | |
21 static const char kSmallIconName[] = "icon_util/16_X_16_icon.ico"; | |
22 static const char kLargeIconName[] = "icon_util/128_X_128_icon.ico"; | |
23 static const char kTempIconFilename[] = "temp_test_icon.ico"; | |
24 | |
25 } // namespace | |
26 | |
27 class IconUtilTest : public testing::Test { | |
28 public: | |
29 virtual void SetUp() override { | |
30 gfx::RegisterPathProvider(); | |
31 ASSERT_TRUE(PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_)); | |
32 temp_directory_.CreateUniqueTempDir(); | |
33 } | |
34 | |
35 static const int kSmallIconWidth = 16; | |
36 static const int kSmallIconHeight = 16; | |
37 static const int kLargeIconWidth = 128; | |
38 static const int kLargeIconHeight = 128; | |
39 | |
40 // Given a file name for an .ico file and an image dimensions, this | |
41 // function loads the icon and returns an HICON handle. | |
42 HICON LoadIconFromFile(const base::FilePath& filename, | |
43 int width, int height) { | |
44 HICON icon = static_cast<HICON>(LoadImage(NULL, | |
45 filename.value().c_str(), | |
46 IMAGE_ICON, | |
47 width, | |
48 height, | |
49 LR_LOADTRANSPARENT | LR_LOADFROMFILE)); | |
50 return icon; | |
51 } | |
52 | |
53 SkBitmap CreateBlackSkBitmap(int width, int height) { | |
54 SkBitmap bitmap; | |
55 bitmap.allocN32Pixels(width, height); | |
56 // Setting the pixels to transparent-black. | |
57 memset(bitmap.getPixels(), 0, width * height * 4); | |
58 return bitmap; | |
59 } | |
60 | |
61 // Loads an .ico file from |icon_filename| and asserts that it contains all of | |
62 // the expected icon sizes up to and including |max_icon_size|, and no other | |
63 // icons. If |max_icon_size| >= 256, this tests for a 256x256 PNG icon entry. | |
64 void CheckAllIconSizes(const base::FilePath& icon_filename, | |
65 int max_icon_size); | |
66 | |
67 protected: | |
68 // The root directory for test files. This should be treated as read-only. | |
69 base::FilePath test_data_directory_; | |
70 | |
71 // Directory for creating files by this test. | |
72 base::ScopedTempDir temp_directory_; | |
73 }; | |
74 | |
75 void IconUtilTest::CheckAllIconSizes(const base::FilePath& icon_filename, | |
76 int max_icon_size) { | |
77 ASSERT_TRUE(base::PathExists(icon_filename)); | |
78 | |
79 // Determine how many icons to expect, based on |max_icon_size|. | |
80 int expected_num_icons = 0; | |
81 for (size_t i = 0; i < IconUtil::kNumIconDimensions; ++i) { | |
82 if (IconUtil::kIconDimensions[i] > max_icon_size) | |
83 break; | |
84 ++expected_num_icons; | |
85 } | |
86 | |
87 // First, use the Windows API to load the icon, a basic validity test. | |
88 HICON icon = LoadIconFromFile(icon_filename, kSmallIconWidth, | |
89 kSmallIconHeight); | |
90 EXPECT_NE(static_cast<HICON>(NULL), icon); | |
91 if (icon != NULL) | |
92 ::DestroyIcon(icon); | |
93 | |
94 // Read the file completely into memory. | |
95 std::string icon_data; | |
96 ASSERT_TRUE(base::ReadFileToString(icon_filename, &icon_data)); | |
97 ASSERT_GE(icon_data.length(), sizeof(IconUtil::ICONDIR)); | |
98 | |
99 // Ensure that it has exactly the expected number and sizes of icons, in the | |
100 // expected order. This matches each entry of the loaded file's icon directory | |
101 // with the corresponding element of kIconDimensions. | |
102 // Also extracts the 256x256 entry as png_entry. | |
103 const IconUtil::ICONDIR* icon_dir = | |
104 reinterpret_cast<const IconUtil::ICONDIR*>(icon_data.data()); | |
105 EXPECT_EQ(expected_num_icons, icon_dir->idCount); | |
106 ASSERT_GE(IconUtil::kNumIconDimensions, icon_dir->idCount); | |
107 ASSERT_GE(icon_data.length(), | |
108 sizeof(IconUtil::ICONDIR) + | |
109 icon_dir->idCount * sizeof(IconUtil::ICONDIRENTRY)); | |
110 const IconUtil::ICONDIRENTRY* png_entry = NULL; | |
111 for (size_t i = 0; i < icon_dir->idCount; ++i) { | |
112 const IconUtil::ICONDIRENTRY* entry = &icon_dir->idEntries[i]; | |
113 // Mod 256 because as a special case in ICONDIRENTRY, the value 0 represents | |
114 // a width or height of 256. | |
115 int expected_size = IconUtil::kIconDimensions[i] % 256; | |
116 EXPECT_EQ(expected_size, static_cast<int>(entry->bWidth)); | |
117 EXPECT_EQ(expected_size, static_cast<int>(entry->bHeight)); | |
118 if (entry->bWidth == 0 && entry->bHeight == 0) { | |
119 EXPECT_EQ(NULL, png_entry); | |
120 png_entry = entry; | |
121 } | |
122 } | |
123 | |
124 if (max_icon_size >= 256) { | |
125 ASSERT_TRUE(png_entry); | |
126 | |
127 // Convert the PNG entry data back to a SkBitmap to ensure it's valid. | |
128 ASSERT_GE(icon_data.length(), | |
129 png_entry->dwImageOffset + png_entry->dwBytesInRes); | |
130 const unsigned char* png_bytes = reinterpret_cast<const unsigned char*>( | |
131 icon_data.data() + png_entry->dwImageOffset); | |
132 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( | |
133 png_bytes, png_entry->dwBytesInRes); | |
134 SkBitmap bitmap = image.AsBitmap(); | |
135 EXPECT_EQ(256, bitmap.width()); | |
136 EXPECT_EQ(256, bitmap.height()); | |
137 } | |
138 } | |
139 | |
140 // The following test case makes sure IconUtil::SkBitmapFromHICON fails | |
141 // gracefully when called with invalid input parameters. | |
142 TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) { | |
143 base::FilePath icon_filename = | |
144 test_data_directory_.AppendASCII(kSmallIconName); | |
145 gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight); | |
146 HICON icon = LoadIconFromFile(icon_filename, | |
147 icon_size.width(), | |
148 icon_size.height()); | |
149 ASSERT_TRUE(icon != NULL); | |
150 | |
151 // Invalid size parameter. | |
152 gfx::Size invalid_icon_size(kSmallIconHeight, 0); | |
153 EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size), | |
154 static_cast<SkBitmap*>(NULL)); | |
155 | |
156 // Invalid icon. | |
157 EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size), | |
158 static_cast<SkBitmap*>(NULL)); | |
159 | |
160 // The following code should succeed. | |
161 scoped_ptr<SkBitmap> bitmap; | |
162 bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size)); | |
163 EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
164 ::DestroyIcon(icon); | |
165 } | |
166 | |
167 // The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails | |
168 // gracefully when called with invalid input parameters. | |
169 TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) { | |
170 HICON icon = NULL; | |
171 scoped_ptr<SkBitmap> bitmap; | |
172 | |
173 // Wrong bitmap format. | |
174 bitmap.reset(new SkBitmap); | |
175 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
176 bitmap->setInfo(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight)); | |
177 icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); | |
178 EXPECT_EQ(icon, static_cast<HICON>(NULL)); | |
179 | |
180 // Invalid bitmap size. | |
181 bitmap.reset(new SkBitmap); | |
182 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
183 bitmap->setInfo(SkImageInfo::MakeN32Premul(0, 0)); | |
184 icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); | |
185 EXPECT_EQ(icon, static_cast<HICON>(NULL)); | |
186 | |
187 // Valid bitmap configuration but no pixels allocated. | |
188 bitmap.reset(new SkBitmap); | |
189 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
190 bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth, | |
191 kSmallIconHeight)); | |
192 icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); | |
193 EXPECT_TRUE(icon == NULL); | |
194 } | |
195 | |
196 // The following test case makes sure IconUtil::CreateIconFileFromImageFamily | |
197 // fails gracefully when called with invalid input parameters. | |
198 TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { | |
199 scoped_ptr<SkBitmap> bitmap; | |
200 gfx::ImageFamily image_family; | |
201 base::FilePath valid_icon_filename = temp_directory_.path().AppendASCII( | |
202 kTempIconFilename); | |
203 base::FilePath invalid_icon_filename = temp_directory_.path().AppendASCII( | |
204 "<>?.ico"); | |
205 | |
206 // Wrong bitmap format. | |
207 bitmap.reset(new SkBitmap); | |
208 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
209 // Must allocate pixels or else ImageSkia will ignore the bitmap and just | |
210 // return an empty image. | |
211 bitmap->allocPixels(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight)); | |
212 memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height()); | |
213 image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); | |
214 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
215 valid_icon_filename)); | |
216 EXPECT_FALSE(base::PathExists(valid_icon_filename)); | |
217 | |
218 // Invalid bitmap size. | |
219 image_family.clear(); | |
220 bitmap.reset(new SkBitmap); | |
221 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
222 bitmap->allocPixels(SkImageInfo::MakeN32Premul(0, 0)); | |
223 image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); | |
224 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
225 valid_icon_filename)); | |
226 EXPECT_FALSE(base::PathExists(valid_icon_filename)); | |
227 | |
228 // Bitmap with no allocated pixels. | |
229 image_family.clear(); | |
230 bitmap.reset(new SkBitmap); | |
231 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
232 bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth, | |
233 kSmallIconHeight)); | |
234 image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); | |
235 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
236 valid_icon_filename)); | |
237 EXPECT_FALSE(base::PathExists(valid_icon_filename)); | |
238 | |
239 // Invalid file name. | |
240 image_family.clear(); | |
241 bitmap->allocPixels(); | |
242 // Setting the pixels to black. | |
243 memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4); | |
244 image_family.Add(gfx::Image::CreateFrom1xBitmap(*bitmap)); | |
245 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
246 invalid_icon_filename)); | |
247 EXPECT_FALSE(base::PathExists(invalid_icon_filename)); | |
248 } | |
249 | |
250 // This test case makes sure IconUtil::CreateIconFileFromImageFamily fails if | |
251 // the image family is empty or invalid. | |
252 TEST_F(IconUtilTest, TestCreateIconFileEmptyImageFamily) { | |
253 base::FilePath icon_filename = temp_directory_.path().AppendASCII( | |
254 kTempIconFilename); | |
255 | |
256 // Empty image family. | |
257 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(gfx::ImageFamily(), | |
258 icon_filename)); | |
259 EXPECT_FALSE(base::PathExists(icon_filename)); | |
260 | |
261 // Image family with only an empty image. | |
262 gfx::ImageFamily image_family; | |
263 image_family.Add(gfx::Image()); | |
264 EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
265 icon_filename)); | |
266 EXPECT_FALSE(base::PathExists(icon_filename)); | |
267 } | |
268 | |
269 // This test case makes sure that when we load an icon from disk and convert | |
270 // the HICON into a bitmap, the bitmap has the expected format and dimensions. | |
271 TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) { | |
272 scoped_ptr<SkBitmap> bitmap; | |
273 base::FilePath small_icon_filename = test_data_directory_.AppendASCII( | |
274 kSmallIconName); | |
275 gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight); | |
276 HICON small_icon = LoadIconFromFile(small_icon_filename, | |
277 small_icon_size.width(), | |
278 small_icon_size.height()); | |
279 ASSERT_NE(small_icon, static_cast<HICON>(NULL)); | |
280 bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size)); | |
281 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
282 EXPECT_EQ(bitmap->width(), small_icon_size.width()); | |
283 EXPECT_EQ(bitmap->height(), small_icon_size.height()); | |
284 EXPECT_EQ(bitmap->colorType(), kN32_SkColorType); | |
285 ::DestroyIcon(small_icon); | |
286 | |
287 base::FilePath large_icon_filename = test_data_directory_.AppendASCII( | |
288 kLargeIconName); | |
289 gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight); | |
290 HICON large_icon = LoadIconFromFile(large_icon_filename, | |
291 large_icon_size.width(), | |
292 large_icon_size.height()); | |
293 ASSERT_NE(large_icon, static_cast<HICON>(NULL)); | |
294 bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size)); | |
295 ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); | |
296 EXPECT_EQ(bitmap->width(), large_icon_size.width()); | |
297 EXPECT_EQ(bitmap->height(), large_icon_size.height()); | |
298 EXPECT_EQ(bitmap->colorType(), kN32_SkColorType); | |
299 ::DestroyIcon(large_icon); | |
300 } | |
301 | |
302 // This test case makes sure that when an HICON is created from an SkBitmap, | |
303 // the returned handle is valid and refers to an icon with the expected | |
304 // dimensions color depth etc. | |
305 TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) { | |
306 SkBitmap bitmap = CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight); | |
307 HICON icon = IconUtil::CreateHICONFromSkBitmap(bitmap); | |
308 EXPECT_NE(icon, static_cast<HICON>(NULL)); | |
309 ICONINFO icon_info; | |
310 ASSERT_TRUE(::GetIconInfo(icon, &icon_info)); | |
311 EXPECT_TRUE(icon_info.fIcon); | |
312 | |
313 // Now that have the icon information, we should obtain the specification of | |
314 // the icon's bitmap and make sure it matches the specification of the | |
315 // SkBitmap we started with. | |
316 // | |
317 // The bitmap handle contained in the icon information is a handle to a | |
318 // compatible bitmap so we need to call ::GetDIBits() in order to retrieve | |
319 // the bitmap's header information. | |
320 BITMAPINFO bitmap_info; | |
321 ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO)); | |
322 bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO); | |
323 HDC hdc = ::GetDC(NULL); | |
324 int result = ::GetDIBits(hdc, | |
325 icon_info.hbmColor, | |
326 0, | |
327 kSmallIconWidth, | |
328 NULL, | |
329 &bitmap_info, | |
330 DIB_RGB_COLORS); | |
331 ASSERT_GT(result, 0); | |
332 EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth); | |
333 EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight); | |
334 EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1); | |
335 EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32); | |
336 ::ReleaseDC(NULL, hdc); | |
337 ::DestroyIcon(icon); | |
338 } | |
339 | |
340 // This test case makes sure that CreateIconFileFromImageFamily creates a | |
341 // valid .ico file given an ImageFamily, and appropriately creates all icon | |
342 // sizes from the given input. | |
343 TEST_F(IconUtilTest, TestCreateIconFileFromImageFamily) { | |
344 gfx::ImageFamily image_family; | |
345 base::FilePath icon_filename = | |
346 temp_directory_.path().AppendASCII(kTempIconFilename); | |
347 | |
348 // Test with only a 16x16 icon. Should only scale up to 48x48. | |
349 image_family.Add(gfx::Image::CreateFrom1xBitmap( | |
350 CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight))); | |
351 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
352 icon_filename)); | |
353 CheckAllIconSizes(icon_filename, 48); | |
354 | |
355 // Test with a 48x48 icon. Should only scale down. | |
356 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(48, 48))); | |
357 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
358 icon_filename)); | |
359 CheckAllIconSizes(icon_filename, 48); | |
360 | |
361 // Test with a 64x64 icon. Should scale up to 256x256. | |
362 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(64, 64))); | |
363 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
364 icon_filename)); | |
365 CheckAllIconSizes(icon_filename, 256); | |
366 | |
367 // Test with a 256x256 icon. Should include the 256x256 in the output. | |
368 image_family.Add(gfx::Image::CreateFrom1xBitmap( | |
369 CreateBlackSkBitmap(256, 256))); | |
370 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
371 icon_filename)); | |
372 CheckAllIconSizes(icon_filename, 256); | |
373 | |
374 // Test with a 49x49 icon. Should scale up to 256x256, but exclude the | |
375 // original 49x49 representation from the output. | |
376 image_family.clear(); | |
377 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(49, 49))); | |
378 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
379 icon_filename)); | |
380 CheckAllIconSizes(icon_filename, 256); | |
381 | |
382 // Test with a non-square 16x32 icon. Should scale up to 48, but exclude the | |
383 // original 16x32 representation from the output. | |
384 image_family.clear(); | |
385 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(16, 32))); | |
386 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
387 icon_filename)); | |
388 CheckAllIconSizes(icon_filename, 48); | |
389 | |
390 // Test with a non-square 32x49 icon. Should scale up to 256, but exclude the | |
391 // original 32x49 representation from the output. | |
392 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(32, 49))); | |
393 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
394 icon_filename)); | |
395 CheckAllIconSizes(icon_filename, 256); | |
396 | |
397 // Test with an empty and non-empty image. | |
398 // The empty image should be ignored. | |
399 image_family.clear(); | |
400 image_family.Add(gfx::Image()); | |
401 image_family.Add(gfx::Image::CreateFrom1xBitmap(CreateBlackSkBitmap(16, 16))); | |
402 ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family, | |
403 icon_filename)); | |
404 CheckAllIconSizes(icon_filename, 48); | |
405 } | |
406 | |
407 TEST_F(IconUtilTest, TestCreateSkBitmapFromIconResource48x48) { | |
408 HMODULE module = GetModuleHandle(NULL); | |
409 scoped_ptr<SkBitmap> bitmap( | |
410 IconUtil::CreateSkBitmapFromIconResource(module, IDR_MAINFRAME, 48)); | |
411 ASSERT_TRUE(bitmap.get()); | |
412 EXPECT_EQ(48, bitmap->width()); | |
413 EXPECT_EQ(48, bitmap->height()); | |
414 } | |
415 | |
416 TEST_F(IconUtilTest, TestCreateSkBitmapFromIconResource256x256) { | |
417 HMODULE module = GetModuleHandle(NULL); | |
418 scoped_ptr<SkBitmap> bitmap( | |
419 IconUtil::CreateSkBitmapFromIconResource(module, IDR_MAINFRAME, 256)); | |
420 ASSERT_TRUE(bitmap.get()); | |
421 EXPECT_EQ(256, bitmap->width()); | |
422 EXPECT_EQ(256, bitmap->height()); | |
423 } | |
424 | |
425 // This tests that kNumIconDimensionsUpToMediumSize has the correct value. | |
426 TEST_F(IconUtilTest, TestNumIconDimensionsUpToMediumSize) { | |
427 ASSERT_LE(IconUtil::kNumIconDimensionsUpToMediumSize, | |
428 IconUtil::kNumIconDimensions); | |
429 EXPECT_EQ(IconUtil::kMediumIconSize, | |
430 IconUtil::kIconDimensions[ | |
431 IconUtil::kNumIconDimensionsUpToMediumSize - 1]); | |
432 } | |
OLD | NEW |