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