diff options
Diffstat (limited to 'ui/gfx/icon_util.cc')
-rw-r--r-- | ui/gfx/icon_util.cc | 219 |
1 files changed, 141 insertions, 78 deletions
diff --git a/ui/gfx/icon_util.cc b/ui/gfx/icon_util.cc index 4ce9779..37ab0d5 100644 --- a/ui/gfx/icon_util.cc +++ b/ui/gfx/icon_util.cc @@ -15,6 +15,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/gdi_util.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_family.h" #include "ui/gfx/size.h" namespace { @@ -33,17 +34,117 @@ struct ScopedICONINFO : ICONINFO { } }; +// Creates a new ImageFamily, |resized_image_family|, based on the images in +// |image_family|, but containing images of specific dimensions desirable for +// Windows icons. For each desired image dimension, it chooses the most +// appropriate image for that size, and resizes it to the desired size. +// Returns true on success, false on failure. Failure can occur if +// |image_family| is empty, all images in the family have size 0x0, or an image +// has no allocated pixel data. +// |resized_image_family| must be empty. +bool BuildResizedImageFamily(const gfx::ImageFamily& image_family, + gfx::ImageFamily* resized_image_family) { + DCHECK(resized_image_family); + DCHECK(resized_image_family->empty()); + + for (size_t i = 0; i < IconUtil::kNumIconDimensions; ++i) { + int dimension = IconUtil::kIconDimensions[i]; + gfx::Size size(dimension, dimension); + const gfx::Image* best = image_family.GetBest(size); + if (!best || best->IsEmpty()) { + // Either |image_family| is empty, or all images have size 0x0. + return false; + } + + // Optimize for the "Large icons" view in Windows Vista+. This view displays + // icons at full size if only if there is a 256x256 (kLargeIconSize) image + // in the .ico file. Otherwise, it shrinks icons to 48x48 (kMediumIconSize). + if (dimension > IconUtil::kMediumIconSize && + best->Width() <= IconUtil::kMediumIconSize && + best->Height() <= IconUtil::kMediumIconSize) { + // There is no source icon larger than 48x48, so do not create any + // images larger than 48x48. kIconDimensions is sorted in ascending + // order, so it is safe to break here. + break; + } + + if (best->Size() == size) { + resized_image_family->Add(*best); + } else { + // There is no |dimension|x|dimension| source image. + // Resize this one to the desired size, and insert it. + SkBitmap best_bitmap = best->AsBitmap(); + // If a gfx::Image was created from a SkBitmap with no allocated pixels, + // AsBitmap will return a null bitmap instead. This bitmap will have no + // config and a size of 0x0. Check this and fail early, to avoid having + // 0x0-sized bitmaps in our resized image family. + if (best_bitmap.config() == SkBitmap::kNo_Config) + return false; + SkBitmap resized_bitmap = skia::ImageOperations::Resize( + best_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, + dimension, dimension); + resized_image_family->Add(gfx::Image::CreateFrom1xBitmap(resized_bitmap)); + } + } + return true; +} + +// Creates a set of bitmaps from an image family. +// All images smaller than 256x256 are converted to SkBitmaps, and inserted into +// |bitmaps| in order of aspect ratio (thinnest to widest), and then ascending +// size order. If an image of exactly 256x256 is specified, it is converted into +// PNG format and stored in |png_bytes|. Images with width or height larger than +// 256 are ignored. +// |bitmaps| must be an empty vector, and not NULL. +// Returns true on success, false on failure. This fails if any image in +// |image_family| is not a 32-bit ARGB image, or is otherwise invalid. +bool ConvertImageFamilyToBitmaps( + const gfx::ImageFamily& image_family, + std::vector<SkBitmap>* bitmaps, + scoped_refptr<base::RefCountedMemory>* png_bytes) { + DCHECK(bitmaps != NULL); + DCHECK(bitmaps->empty()); + + for (gfx::ImageFamily::const_iterator it = image_family.begin(); + it != image_family.end(); ++it) { + const gfx::Image& image = *it; + + // All images should have one of the kIconDimensions sizes. + DCHECK_GT(image.Width(), 0); + DCHECK_LE(image.Width(), IconUtil::kLargeIconSize); + DCHECK_GT(image.Height(), 0); + DCHECK_LE(image.Height(), IconUtil::kLargeIconSize); + + SkBitmap bitmap = image.AsBitmap(); + + // 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.getPixels() == NULL)) { + return false; + } + + // Special case: Icons exactly 256x256 are stored in PNG format. + if (image.Width() == IconUtil::kLargeIconSize && + image.Height() == IconUtil::kLargeIconSize) { + *png_bytes = image.As1xPNGBytes(); + } else { + bitmaps->push_back(bitmap); + } + } + + return true; +} + } // namespace -// Defining the dimensions for the icon images. We store only one value because -// we always resize to a square image; that is, the value 48 means that we are -// going to resize the given bitmap to a 48 by 48 pixels bitmap. -// // The icon images appear in the icon file in same order in which their -// corresponding dimensions appear in the |icon_dimensions_| array, so it is -// important to keep this array sorted. Also note that the maximum icon image -// size we can handle is 255 by 255. -const int IconUtil::icon_dimensions_[] = { +// corresponding dimensions appear in this array, so it is important to keep +// this array sorted. Also note that the maximum icon image size we can handle +// is 256 by 256. See: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa511280.aspx#size +const int IconUtil::kIconDimensions[] = { 8, // Recommended by the MSDN as a nice to have icon size. 10, // Used by the Shell (e.g. for shortcuts). 14, // Recommended by the MSDN as a nice to have icon size. @@ -55,9 +156,13 @@ const int IconUtil::icon_dimensions_[] = { 48, // Alt+Tab icon size. 64, // Recommended by the MSDN as a nice to have icon size. 96, // Recommended by the MSDN as a nice to have icon size. - 128 // Used by the Shell (e.g. for shortcuts). + 128, // Used by the Shell (e.g. for shortcuts). + 256 // Used by Vista onwards for large icons. }; +const size_t IconUtil::kNumIconDimensions = arraysize(kIconDimensions); +const size_t IconUtil::kNumIconDimensionsUpToMediumSize = 9; + HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) { // Only 32 bit ARGB bitmaps are supported. We also try to perform as many // validations as we can on the bitmap. @@ -342,41 +447,36 @@ SkBitmap IconUtil::CreateSkBitmapFromHICONHelper(HICON icon, return bitmap; } -bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, - const SkBitmap& large_bitmap, - const base::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)) { +// static +bool IconUtil::CreateIconFileFromImageFamily( + const gfx::ImageFamily& image_family, + const base::FilePath& icon_path) { + // Creating a set of bitmaps corresponding to the icon images we'll end up + // storing in the icon file. Each bitmap is created by resizing the most + // appropriate image from |image_family| to the desired size. + gfx::ImageFamily resized_image_family; + if (!BuildResizedImageFamily(image_family, &resized_image_family)) return false; - } - // If |large_bitmap| was specified, validate its dimension and convert to PNG. + std::vector<SkBitmap> bitmaps; scoped_refptr<base::RefCountedMemory> png_bytes; - if (!large_bitmap.empty()) { - CHECK_EQ(kLargeIconSize, large_bitmap.width()); - CHECK_EQ(kLargeIconSize, large_bitmap.height()); - png_bytes = gfx::Image::CreateFrom1xBitmap(large_bitmap).As1xPNGBytes(); - } + if (!ConvertImageFamilyToBitmaps(resized_image_family, &bitmaps, &png_bytes)) + return false; - // We start by creating the file. + // Guaranteed true because BuildResizedImageFamily will provide at least one + // image < 256x256. + DCHECK(!bitmaps.empty()); + size_t bitmap_count = bitmaps.size(); // Not including PNG image. + // Including PNG image, if any. + size_t image_count = bitmap_count + (png_bytes.get() ? 1 : 0); + + // Now that basic checks are done, we can create the file. base::win::ScopedHandle icon_file(::CreateFile(icon_path.value().c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); if (!icon_file.IsValid()) return false; - // Creating a set of bitmaps corresponding to the icon images we'll end up - // storing in the icon file. Each bitmap is created by resizing the given - // bitmap to the desired size. - std::vector<SkBitmap> bitmaps; - CreateResizedBitmapSet(bitmap, &bitmaps); - DCHECK(!bitmaps.empty()); - size_t bitmap_count = bitmaps.size(); - // 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); @@ -391,14 +491,9 @@ bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, std::vector<uint8> buffer(buffer_size); ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(&buffer[0]); icon_dir->idType = kResourceTypeIcon; - icon_dir->idCount = static_cast<WORD>(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++; - } + icon_dir->idCount = static_cast<WORD>(image_count); + // - 1 because there is already one ICONDIRENTRY in ICONDIR. + size_t icon_dir_count = image_count - 1; size_t offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count); for (size_t i = 0; i < bitmap_count; i++) { @@ -494,6 +589,8 @@ void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap, DCHECK(icon_image != NULL); DCHECK_GT(image_offset, 0U); DCHECK(image_byte_count != NULL); + DCHECK_LT(bitmap.width(), kLargeIconSize); + DCHECK_LT(bitmap.height(), kLargeIconSize); // We start by computing certain image values we'll use later on. size_t xor_mask_size, bytes_in_resource; @@ -552,41 +649,6 @@ void IconUtil::CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap, } } -void IconUtil::CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize, - std::vector<SkBitmap>* bitmaps) { - DCHECK(bitmaps != NULL); - DCHECK(bitmaps->empty()); - - bool inserted_original_bitmap = false; - for (size_t i = 0; i < arraysize(icon_dimensions_); i++) { - // If the dimensions of the bitmap we are resizing are the same as the - // current dimensions, then we should insert the bitmap and not a resized - // bitmap. If the bitmap's dimensions are smaller, we insert our bitmap - // first so that the bitmaps we return in the vector are sorted based on - // their dimensions. - if (!inserted_original_bitmap) { - if ((bitmap_to_resize.width() == icon_dimensions_[i]) && - (bitmap_to_resize.height() == icon_dimensions_[i])) { - bitmaps->push_back(bitmap_to_resize); - inserted_original_bitmap = true; - continue; - } - - if ((bitmap_to_resize.width() < icon_dimensions_[i]) && - (bitmap_to_resize.height() < icon_dimensions_[i])) { - bitmaps->push_back(bitmap_to_resize); - inserted_original_bitmap = true; - } - } - bitmaps->push_back(skia::ImageOperations::Resize( - bitmap_to_resize, skia::ImageOperations::RESIZE_LANCZOS3, - icon_dimensions_[i], icon_dimensions_[i])); - } - - if (!inserted_original_bitmap) - bitmaps->push_back(bitmap_to_resize); -} - size_t IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) { DCHECK(!set.empty()); @@ -597,7 +659,8 @@ size_t IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) { size_t total_buffer_size = sizeof(ICONDIR); size_t bitmap_count = set.size(); total_buffer_size += sizeof(ICONDIRENTRY) * (bitmap_count - 1); - DCHECK_GE(bitmap_count, arraysize(icon_dimensions_)); + // May not have all icon sizes, but must have at least up to medium icon size. + DCHECK_GE(bitmap_count, kNumIconDimensionsUpToMediumSize); // Add the bitmap specific structure sizes. for (size_t i = 0; i < bitmap_count; i++) { |