diff options
author | asvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-08 19:02:59 +0000 |
---|---|---|
committer | asvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-08 19:02:59 +0000 |
commit | 6ed46c8295401386f3764f5155e9c513949f7c1c (patch) | |
tree | 5ea7af6ac99f10cc3b705c2623bac68cc4721b84 /ui | |
parent | 1f21a52382e86370a3b51a919763fc6bd76657ee (diff) | |
download | chromium_src-6ed46c8295401386f3764f5155e9c513949f7c1c.zip chromium_src-6ed46c8295401386f3764f5155e9c513949f7c1c.tar.gz chromium_src-6ed46c8295401386f3764f5155e9c513949f7c1c.tar.bz2 |
Add support for adding 256x256 pngs to Windows .ico files.
These are needed to support the "large icon" view under Vista+.
BUG=167277,163864,110379
TEST=New unit test.
Review URL: https://chromiumcodereview.appspot.com/11742007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175567 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gfx/icon_util.cc | 54 | ||||
-rw-r--r-- | ui/gfx/icon_util.h | 18 | ||||
-rw-r--r-- | ui/gfx/icon_util_unittest.cc | 89 |
3 files changed, 120 insertions, 41 deletions
diff --git a/ui/gfx/icon_util.cc b/ui/gfx/icon_util.cc index 62b6b98..1c87fa9 100644 --- a/ui/gfx/icon_util.cc +++ b/ui/gfx/icon_util.cc @@ -13,6 +13,7 @@ #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/size.h" namespace { @@ -282,14 +283,24 @@ SkBitmap IconUtil::CreateSkBitmapFromHICONHelper(HICON icon, } bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, + const SkBitmap& large_bitmap, const FilePath& icon_path) { // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has // been properly initialized. SkAutoLockPixels bitmap_lock(bitmap); if ((bitmap.config() != SkBitmap::kARGB_8888_Config) || (bitmap.height() <= 0) || (bitmap.width() <= 0) || - (bitmap.getPixels() == NULL)) + (bitmap.getPixels() == NULL)) { return false; + } + + // If |large_bitmap| was specified, validate its dimension and convert to PNG. + scoped_refptr<base::RefCountedMemory> png_bytes; + if (!large_bitmap.empty()) { + DCHECK_EQ(256, large_bitmap.width()); + DCHECK_EQ(256, large_bitmap.height()); + png_bytes = gfx::Image(large_bitmap).As1xPNGBytes(); + } // We start by creating the file. base::win::ScopedHandle icon_file(::CreateFile(icon_path.value().c_str(), @@ -309,21 +320,29 @@ bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, // Computing the total size of the buffer we need in order to store the // images in the desired icon format. size_t buffer_size = ComputeIconFileBufferSize(bitmaps); - unsigned char* buffer = new unsigned char[buffer_size]; - DCHECK(buffer != NULL); - memset(buffer, 0, buffer_size); + // Account for the bytes needed for the PNG entry. + if (png_bytes.get()) + buffer_size += sizeof(ICONDIRENTRY) + png_bytes->size(); // Setting the information in the structures residing within the buffer. // First, we set the information which doesn't require iterating through the // bitmap set and then we set the bitmap specific structures. In the latter // step we also copy the actual bits. - ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(buffer); + std::vector<uint8> buffer(buffer_size); + ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(&buffer[0]); icon_dir->idType = kResourceTypeIcon; icon_dir->idCount = bitmap_count; size_t icon_dir_count = bitmap_count - 1; // Note DCHECK(!bitmaps.empty())! + + // Increment counts if a PNG entry will be added. + if (png_bytes.get()) { + icon_dir->idCount++; + icon_dir_count++; + } + size_t offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count); for (size_t i = 0; i < bitmap_count; i++) { - ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(buffer + offset); + ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(&buffer[offset]); DCHECK_LT(offset, buffer_size); size_t icon_image_size = 0; SetSingleIconImageInformation(bitmaps[i], i, icon_dir, image, offset, @@ -331,17 +350,32 @@ bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, DCHECK_GT(icon_image_size, 0U); offset += icon_image_size; } + + // Add the PNG entry, if necessary. + if (png_bytes.get()) { + ICONDIRENTRY* entry = &icon_dir->idEntries[bitmap_count]; + entry->bWidth = 0; + entry->bHeight = 0; + entry->wPlanes = 1; + entry->wBitCount = 32; + entry->dwBytesInRes = png_bytes->size(); + entry->dwImageOffset = offset; + memcpy(&buffer[offset], png_bytes->front(), png_bytes->size()); + offset += png_bytes->size(); + } + DCHECK_EQ(offset, buffer_size); - // Finally, writing the data info the file. + // Finally, write the data to the file. DWORD bytes_written; bool delete_file = false; - if (!WriteFile(icon_file.Get(), buffer, buffer_size, &bytes_written, NULL) || - bytes_written != buffer_size) + if (!WriteFile(icon_file.Get(), &buffer[0], buffer_size, &bytes_written, + NULL) || + bytes_written != buffer_size) { delete_file = true; + } ::CloseHandle(icon_file.Take()); - delete [] buffer; if (delete_file) { bool success = file_util::Delete(icon_path, false); DCHECK(success); diff --git a/ui/gfx/icon_util.h b/ui/gfx/icon_util.h index edd4961..ae16e01 100644 --- a/ui/gfx/icon_util.h +++ b/ui/gfx/icon_util.h @@ -10,6 +10,7 @@ #include <vector> #include "base/basictypes.h" +#include "base/gtest_prod_util.h" #include "ui/base/ui_export.h" #include "ui/gfx/point.h" #include "ui/gfx/size.h" @@ -81,16 +82,19 @@ class UI_EXPORT IconUtil { // it when it is no longer needed. static SkBitmap* CreateSkBitmapFromHICON(HICON icon); - // Given an initialized SkBitmap object and a file name, this function - // creates a .ico file with the given name using the provided bitmap. The - // icon file is created with multiple icon images of varying predefined - // dimensions because Windows uses different image sizes when loading icons, + // Creates Windows .ico file at |icon_path|. The icon file is created with + // multiple BMP representations at varying predefined dimensions (by resizing + // |bitmap|) because Windows uses different image sizes when loading icons, // depending on where the icon is drawn (ALT+TAB window, desktop shortcut, - // Quick Launch, etc.). |icon_file_name| needs to specify the full path for - // the desired .ico file. + // Quick Launch, etc.). + // + // To create an icon file containing a 256x256 PNG entry, which is used by + // Vista+ for high res icons, specify a non-empty 256x256 SkBitmap for the + // |large_bitmap| parameter. // // The function returns true on success and false otherwise. static bool CreateIconFileFromSkBitmap(const SkBitmap& bitmap, + const SkBitmap& large_bitmap, const FilePath& icon_path); // Creates a cursor of the specified size from the DIB passed in. @@ -139,6 +143,8 @@ class UI_EXPORT IconUtil { }; #pragma pack(pop) + FRIEND_TEST_ALL_PREFIXES(IconUtilTest, TestCreateIconFileWithLargeBitmap); + // Used for indicating that the .ico contains an icon (rather than a cursor) // image. This value is set in the |idType| field of the ICONDIR structure. static const int kResourceTypeIcon = 1; diff --git a/ui/gfx/icon_util_unittest.cc b/ui/gfx/icon_util_unittest.cc index 50c1058..c8963af 100644 --- a/ui/gfx/icon_util_unittest.cc +++ b/ui/gfx/icon_util_unittest.cc @@ -3,12 +3,14 @@ // found in the LICENSE file. #include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/gfx_paths.h" #include "ui/gfx/icon_util.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/size.h" namespace { @@ -41,10 +43,22 @@ class IconUtilTest : public testing::Test { return icon; } + SkBitmap CreateBlackSkBitmap(int width, int height) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.allocPixels(); + // Setting the pixels to black. + memset(bitmap.getPixels(), 0, width * height * 4); + return bitmap; + } + protected: // The root directory for test files. FilePath test_data_directory_; + // Directory for creating files by this test. + base::ScopedTempDir temp_directory_; + private: DISALLOW_COPY_AND_ASSIGN(IconUtilTest); }; @@ -119,14 +133,14 @@ TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight); - EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, SkBitmap(), valid_icon_filename)); // Invalid bitmap size. bitmap.reset(new SkBitmap); ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0); - EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, SkBitmap(), valid_icon_filename)); // Bitmap with no allocated pixels. @@ -135,14 +149,14 @@ TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { bitmap->setConfig(SkBitmap::kARGB_8888_Config, kSmallIconWidth, kSmallIconHeight); - EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, SkBitmap(), valid_icon_filename)); // Invalid file name. bitmap->allocPixels(); // Setting the pixels to black. memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4); - EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, SkBitmap(), invalid_icon_filename)); } @@ -183,14 +197,8 @@ TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) { // the returned handle is valid and refers to an icon with the expected // dimentions color depth etc. TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) { - scoped_ptr<SkBitmap> bitmap; - bitmap.reset(new SkBitmap); - ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, - kSmallIconWidth, - kSmallIconHeight); - bitmap->allocPixels(); - HICON icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); + SkBitmap bitmap = CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight); + HICON icon = IconUtil::CreateHICONFromSkBitmap(bitmap); EXPECT_NE(icon, static_cast<HICON>(NULL)); ICONINFO icon_info; ASSERT_TRUE(::GetIconInfo(icon, &icon_info)); @@ -226,21 +234,10 @@ TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) { // The following test case makes sure IconUtil::CreateIconFileFromSkBitmap // creates a valid .ico file given an SkBitmap. TEST_F(IconUtilTest, TestCreateIconFile) { - scoped_ptr<SkBitmap> bitmap; FilePath icon_filename = test_data_directory_.AppendASCII(kTempIconFilename); - // Allocating the bitmap. - bitmap.reset(new SkBitmap); - ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); - bitmap->setConfig(SkBitmap::kARGB_8888_Config, - kSmallIconWidth, - kSmallIconHeight); - bitmap->allocPixels(); - - // Setting the pixels to black. - memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4); - - EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + SkBitmap bitmap = CreateBlackSkBitmap(kSmallIconWidth, kSmallIconHeight); + EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(bitmap, SkBitmap(), icon_filename)); // We are currently only testing that it is possible to load an icon from @@ -254,3 +251,45 @@ TEST_F(IconUtilTest, TestCreateIconFile) { ::DestroyIcon(icon); } } + +TEST_F(IconUtilTest, TestCreateIconFileWithLargeBitmap) { + const FilePath icon_path(temp_directory_.path().AppendASCII("test.ico")); + const SkBitmap bitmap_48 = CreateBlackSkBitmap(48, 48); + const SkBitmap bitmap_256 = CreateBlackSkBitmap(256, 256); + + // First, create the icon file. + ASSERT_TRUE(IconUtil::CreateIconFileFromSkBitmap(bitmap_48, bitmap_256, + icon_path)); + ASSERT_TRUE(file_util::PathExists(icon_path)); + + // Then, read the file and ensure it has a valid 256x256 PNG icon entry. + std::string icon_data; + ASSERT_TRUE(file_util::ReadFileToString(icon_path, &icon_data)); + ASSERT_GE(icon_data.length(), sizeof(IconUtil::ICONDIR)); + + const IconUtil::ICONDIR* icon_dir = + reinterpret_cast<const IconUtil::ICONDIR*>(icon_data.data()); + ASSERT_GE(icon_data.length(), + sizeof(IconUtil::ICONDIR) + + icon_dir->idCount * sizeof(IconUtil::ICONDIRENTRY)); + const IconUtil::ICONDIRENTRY* png_entry = NULL; + for (size_t i = 0; i < icon_dir->idCount; ++i) { + const IconUtil::ICONDIRENTRY* entry = &icon_dir->idEntries[i]; + if (entry->bWidth == 0 && entry->bHeight == 0) { + EXPECT_EQ(NULL, png_entry); + png_entry = entry; + } + } + ASSERT_TRUE(png_entry); + + // Convert the PNG entry data back to a SkBitmap to ensure it's valid. + ASSERT_GE(icon_data.length(), + png_entry->dwImageOffset + png_entry->dwBytesInRes); + const unsigned char* png_bytes = reinterpret_cast<const unsigned char*>( + icon_data.data() + png_entry->dwImageOffset); + gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(png_bytes, + png_entry->dwBytesInRes); + SkBitmap bitmap = image.AsBitmap(); + EXPECT_EQ(256, bitmap.width()); + EXPECT_EQ(256, bitmap.height()); +} |