icon_util.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/file_util.h" 8#include "base/logging.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/win/resource_util.h" 11#include "base/win/scoped_gdi_object.h" 12#include "base/win/scoped_handle.h" 13#include "base/win/scoped_hdc.h" 14#include "skia/ext/image_operations.h" 15#include "third_party/skia/include/core/SkBitmap.h" 16#include "ui/gfx/gdi_util.h" 17#include "ui/gfx/image/image.h" 18#include "ui/gfx/size.h" 19 20namespace { 21 22struct ScopedICONINFO : ICONINFO { 23 ScopedICONINFO() { 24 hbmColor = NULL; 25 hbmMask = NULL; 26 } 27 28 ~ScopedICONINFO() { 29 if (hbmColor) 30 ::DeleteObject(hbmColor); 31 if (hbmMask) 32 ::DeleteObject(hbmMask); 33 } 34}; 35 36} // namespace 37 38// Defining the dimensions for the icon images. We store only one value because 39// we always resize to a square image; that is, the value 48 means that we are 40// going to resize the given bitmap to a 48 by 48 pixels bitmap. 41// 42// The icon images appear in the icon file in same order in which their 43// corresponding dimensions appear in the |icon_dimensions_| array, so it is 44// important to keep this array sorted. Also note that the maximum icon image 45// size we can handle is 255 by 255. 46const int IconUtil::icon_dimensions_[] = { 47 8, // Recommended by the MSDN as a nice to have icon size. 48 10, // Used by the Shell (e.g. for shortcuts). 49 14, // Recommended by the MSDN as a nice to have icon size. 50 16, // Toolbar, Application and Shell icon sizes. 51 22, // Recommended by the MSDN as a nice to have icon size. 52 24, // Used by the Shell (e.g. for shortcuts). 53 32, // Toolbar, Dialog and Wizard icon size. 54 40, // Quick Launch. 55 48, // Alt+Tab icon size. 56 64, // Recommended by the MSDN as a nice to have icon size. 57 96, // Recommended by the MSDN as a nice to have icon size. 58 128 // Used by the Shell (e.g. for shortcuts). 59}; 60 61HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) { 62 // Only 32 bit ARGB bitmaps are supported. We also try to perform as many 63 // validations as we can on the bitmap. 64 SkAutoLockPixels bitmap_lock(bitmap); 65 if ((bitmap.config() != SkBitmap::kARGB_8888_Config) || 66 (bitmap.width() <= 0) || (bitmap.height() <= 0) || 67 (bitmap.getPixels() == NULL)) 68 return NULL; 69 70 // We start by creating a DIB which we'll use later on in order to create 71 // the HICON. We use BITMAPV5HEADER since the bitmap we are about to convert 72 // may contain an alpha channel and the V5 header allows us to specify the 73 // alpha mask for the DIB. 74 BITMAPV5HEADER bitmap_header; 75 InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height()); 76 void* bits; 77 HDC hdc = ::GetDC(NULL); 78 HBITMAP dib; 79 dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header), 80 DIB_RGB_COLORS, &bits, NULL, 0); 81 DCHECK(dib); 82 ::ReleaseDC(NULL, hdc); 83 memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4); 84 85 // Icons are generally created using an AND and XOR masks where the AND 86 // specifies boolean transparency (the pixel is either opaque or 87 // transparent) and the XOR mask contains the actual image pixels. If the XOR 88 // mask bitmap has an alpha channel, the AND monochrome bitmap won't 89 // actually be used for computing the pixel transparency. Even though all our 90 // bitmap has an alpha channel, Windows might not agree when all alpha values 91 // are zero. So the monochrome bitmap is created with all pixels transparent 92 // for this case. Otherwise, it is created with all pixels opaque. 93 bool bitmap_has_alpha_channel = PixelsHaveAlpha( 94 static_cast<const uint32*>(bitmap.getPixels()), 95 bitmap.width() * bitmap.height()); 96 97 scoped_ptr<uint8[]> mask_bits; 98 if (!bitmap_has_alpha_channel) { 99 // Bytes per line with paddings to make it word alignment. 100 size_t bytes_per_line = (bitmap.width() + 0xF) / 16 * 2; 101 size_t mask_bits_size = bytes_per_line * bitmap.height(); 102 103 mask_bits.reset(new uint8[mask_bits_size]); 104 DCHECK(mask_bits.get()); 105 106 // Make all pixels transparent. 107 memset(mask_bits.get(), 0xFF, mask_bits_size); 108 } 109 110 HBITMAP mono_bitmap = ::CreateBitmap(bitmap.width(), bitmap.height(), 1, 1, 111 reinterpret_cast<LPVOID>(mask_bits.get())); 112 DCHECK(mono_bitmap); 113 114 ICONINFO icon_info; 115 icon_info.fIcon = TRUE; 116 icon_info.xHotspot = 0; 117 icon_info.yHotspot = 0; 118 icon_info.hbmMask = mono_bitmap; 119 icon_info.hbmColor = dib; 120 HICON icon = ::CreateIconIndirect(&icon_info); 121 ::DeleteObject(dib); 122 ::DeleteObject(mono_bitmap); 123 return icon; 124} 125 126SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s) { 127 // We start with validating parameters. 128 if (!icon || s.IsEmpty()) 129 return NULL; 130 ScopedICONINFO icon_info; 131 if (!::GetIconInfo(icon, &icon_info)) 132 return NULL; 133 if (!icon_info.fIcon) 134 return NULL; 135 return new SkBitmap(CreateSkBitmapFromHICONHelper(icon, s)); 136} 137 138scoped_ptr<SkBitmap> IconUtil::CreateSkBitmapFromIconResource(HMODULE module, 139 int resource_id, 140 int size) { 141 DCHECK_LE(size, kLargeIconSize); 142 143 // For everything except the Vista+ 256x256 icons, use |LoadImage()|. 144 if (size != kLargeIconSize) { 145 HICON icon_handle = 146 static_cast<HICON>(LoadImage(module, MAKEINTRESOURCE(resource_id), 147 IMAGE_ICON, size, size, 148 LR_DEFAULTCOLOR | LR_DEFAULTSIZE)); 149 scoped_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(icon_handle)); 150 DestroyIcon(icon_handle); 151 return bitmap.Pass(); 152 } 153 154 // For Vista+ 256x256 PNG icons, read the resource directly and find 155 // the corresponding icon entry to get its PNG bytes. 156 void* icon_dir_data = NULL; 157 size_t icon_dir_size = 0; 158 if (!base::win::GetResourceFromModule(module, resource_id, RT_GROUP_ICON, 159 &icon_dir_data, &icon_dir_size)) { 160 return scoped_ptr<SkBitmap>(); 161 } 162 DCHECK(icon_dir_data); 163 DCHECK_GE(icon_dir_size, sizeof(GRPICONDIR)); 164 165 const GRPICONDIR* icon_dir = 166 reinterpret_cast<const GRPICONDIR*>(icon_dir_data); 167 const GRPICONDIRENTRY* large_icon_entry = NULL; 168 for (size_t i = 0; i < icon_dir->idCount; ++i) { 169 const GRPICONDIRENTRY* entry = &icon_dir->idEntries[i]; 170 // 256x256 icons are stored with width and height set to 0. 171 // See: http://en.wikipedia.org/wiki/ICO_(file_format) 172 if (entry->bWidth == 0 && entry->bHeight == 0) { 173 large_icon_entry = entry; 174 break; 175 } 176 } 177 if (!large_icon_entry) 178 return scoped_ptr<SkBitmap>(); 179 180 void* png_data = NULL; 181 size_t png_size = 0; 182 if (!base::win::GetResourceFromModule(module, large_icon_entry->nID, RT_ICON, 183 &png_data, &png_size)) { 184 return scoped_ptr<SkBitmap>(); 185 } 186 DCHECK(png_data); 187 DCHECK_EQ(png_size, large_icon_entry->dwBytesInRes); 188 189 const unsigned char* png_bytes = 190 reinterpret_cast<const unsigned char*>(png_data); 191 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(png_bytes, png_size); 192 return scoped_ptr<SkBitmap>(new SkBitmap(image.AsBitmap())); 193} 194 195SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon) { 196 // We start with validating parameters. 197 if (!icon) 198 return NULL; 199 200 ScopedICONINFO icon_info; 201 BITMAP bitmap_info = { 0 }; 202 203 if (!::GetIconInfo(icon, &icon_info)) 204 return NULL; 205 206 if (!::GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info)) 207 return NULL; 208 209 gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight); 210 return new SkBitmap(CreateSkBitmapFromHICONHelper(icon, icon_size)); 211} 212 213HICON IconUtil::CreateCursorFromDIB(const gfx::Size& icon_size, 214 const gfx::Point& hotspot, 215 const void* dib_bits, 216 size_t dib_size) { 217 BITMAPINFO icon_bitmap_info = {0}; 218 gfx::CreateBitmapHeader( 219 icon_size.width(), 220 icon_size.height(), 221 reinterpret_cast<BITMAPINFOHEADER*>(&icon_bitmap_info)); 222 223 base::win::ScopedGetDC dc(NULL); 224 base::win::ScopedCreateDC working_dc(CreateCompatibleDC(dc)); 225 base::win::ScopedGDIObject<HBITMAP> bitmap_handle( 226 CreateDIBSection(dc, 227 &icon_bitmap_info, 228 DIB_RGB_COLORS, 229 0, 230 0, 231 0)); 232 if (dib_size > 0) { 233 SetDIBits(0, 234 bitmap_handle, 235 0, 236 icon_size.height(), 237 dib_bits, 238 &icon_bitmap_info, 239 DIB_RGB_COLORS); 240 } 241 242 HBITMAP old_bitmap = reinterpret_cast<HBITMAP>( 243 SelectObject(working_dc, bitmap_handle)); 244 SetBkMode(working_dc, TRANSPARENT); 245 SelectObject(working_dc, old_bitmap); 246 247 base::win::ScopedGDIObject<HBITMAP> mask( 248 CreateBitmap(icon_size.width(), 249 icon_size.height(), 250 1, 251 1, 252 NULL)); 253 ICONINFO ii = {0}; 254 ii.fIcon = FALSE; 255 ii.xHotspot = hotspot.x(); 256 ii.yHotspot = hotspot.y(); 257 ii.hbmMask = mask; 258 ii.hbmColor = bitmap_handle; 259 260 return CreateIconIndirect(&ii); 261} 262 263SkBitmap IconUtil::CreateSkBitmapFromHICONHelper(HICON icon, 264 const gfx::Size& s) { 265 DCHECK(icon); 266 DCHECK(!s.IsEmpty()); 267 268 // Allocating memory for the SkBitmap object. We are going to create an ARGB 269 // bitmap so we should set the configuration appropriately. 270 SkBitmap bitmap; 271 bitmap.setConfig(SkBitmap::kARGB_8888_Config, s.width(), s.height()); 272 bitmap.allocPixels(); 273 bitmap.eraseARGB(0, 0, 0, 0); 274 SkAutoLockPixels bitmap_lock(bitmap); 275 276 // Now we should create a DIB so that we can use ::DrawIconEx in order to 277 // obtain the icon's image. 278 BITMAPV5HEADER h; 279 InitializeBitmapHeader(&h, s.width(), s.height()); 280 HDC hdc = ::GetDC(NULL); 281 uint32* bits; 282 HBITMAP dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&h), 283 DIB_RGB_COLORS, reinterpret_cast<void**>(&bits), NULL, 0); 284 DCHECK(dib); 285 HDC dib_dc = CreateCompatibleDC(hdc); 286 ::ReleaseDC(NULL, hdc); 287 DCHECK(dib_dc); 288 HGDIOBJ old_obj = ::SelectObject(dib_dc, dib); 289 290 // Windows icons are defined using two different masks. The XOR mask, which 291 // represents the icon image and an AND mask which is a monochrome bitmap 292 // which indicates the transparency of each pixel. 293 // 294 // To make things more complex, the icon image itself can be an ARGB bitmap 295 // and therefore contain an alpha channel which specifies the transparency 296 // for each pixel. Unfortunately, there is no easy way to determine whether 297 // or not a bitmap has an alpha channel and therefore constructing the bitmap 298 // for the icon is nothing but straightforward. 299 // 300 // The idea is to read the AND mask but use it only if we know for sure that 301 // the icon image does not have an alpha channel. The only way to tell if the 302 // bitmap has an alpha channel is by looking through the pixels and checking 303 // whether there are non-zero alpha bytes. 304 // 305 // We start by drawing the AND mask into our DIB. 306 size_t num_pixels = s.GetArea(); 307 memset(bits, 0, num_pixels * 4); 308 ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_MASK); 309 310 // Capture boolean opacity. We may not use it if we find out the bitmap has 311 // an alpha channel. 312 scoped_ptr<bool[]> opaque(new bool[num_pixels]); 313 for (size_t i = 0; i < num_pixels; ++i) 314 opaque[i] = !bits[i]; 315 316 // Then draw the image itself which is really the XOR mask. 317 memset(bits, 0, num_pixels * 4); 318 ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_NORMAL); 319 memcpy(bitmap.getPixels(), static_cast<void*>(bits), num_pixels * 4); 320 321 // Finding out whether the bitmap has an alpha channel. 322 bool bitmap_has_alpha_channel = PixelsHaveAlpha( 323 static_cast<const uint32*>(bitmap.getPixels()), num_pixels); 324 325 // If the bitmap does not have an alpha channel, we need to build it using 326 // the previously captured AND mask. Otherwise, we are done. 327 if (!bitmap_has_alpha_channel) { 328 uint32* p = static_cast<uint32*>(bitmap.getPixels()); 329 for (size_t i = 0; i < num_pixels; ++p, ++i) { 330 DCHECK_EQ((*p & 0xff000000), 0u); 331 if (opaque[i]) 332 *p |= 0xff000000; 333 else 334 *p &= 0x00ffffff; 335 } 336 } 337 338 ::SelectObject(dib_dc, old_obj); 339 ::DeleteObject(dib); 340 ::DeleteDC(dib_dc); 341 342 return bitmap; 343} 344 345bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, 346 const SkBitmap& large_bitmap, 347 const base::FilePath& icon_path) { 348 // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has 349 // been properly initialized. 350 SkAutoLockPixels bitmap_lock(bitmap); 351 if ((bitmap.config() != SkBitmap::kARGB_8888_Config) || 352 (bitmap.height() <= 0) || (bitmap.width() <= 0) || 353 (bitmap.getPixels() == NULL)) { 354 return false; 355 } 356 357 // If |large_bitmap| was specified, validate its dimension and convert to PNG. 358 scoped_refptr<base::RefCountedMemory> png_bytes; 359 if (!large_bitmap.empty()) { 360 CHECK_EQ(kLargeIconSize, large_bitmap.width()); 361 CHECK_EQ(kLargeIconSize, large_bitmap.height()); 362 png_bytes = gfx::Image::CreateFrom1xBitmap(large_bitmap).As1xPNGBytes(); 363 } 364 365 // We start by creating the file. 366 base::win::ScopedHandle icon_file(::CreateFile(icon_path.value().c_str(), 367 GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); 368 369 if (!icon_file.IsValid()) 370 return false; 371 372 // Creating a set of bitmaps corresponding to the icon images we'll end up 373 // storing in the icon file. Each bitmap is created by resizing the given 374 // bitmap to the desired size. 375 std::vector<SkBitmap> bitmaps; 376 CreateResizedBitmapSet(bitmap, &bitmaps); 377 DCHECK(!bitmaps.empty()); 378 size_t bitmap_count = bitmaps.size(); 379 380 // Computing the total size of the buffer we need in order to store the 381 // images in the desired icon format. 382 size_t buffer_size = ComputeIconFileBufferSize(bitmaps); 383 // Account for the bytes needed for the PNG entry. 384 if (png_bytes.get()) 385 buffer_size += sizeof(ICONDIRENTRY) + png_bytes->size(); 386 387 // Setting the information in the structures residing within the buffer. 388 // First, we set the information which doesn't require iterating through the 389 // bitmap set and then we set the bitmap specific structures. In the latter 390 // step we also copy the actual bits. 391 std::vector<uint8> buffer(buffer_size); 392 ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(&buffer[0]); 393 icon_dir->idType = kResourceTypeIcon; 394 icon_dir->idCount = static_cast<WORD>(bitmap_count); 395 size_t icon_dir_count = bitmap_count - 1; // Note DCHECK(!bitmaps.empty())! 396 397 // Increment counts if a PNG entry will be added. 398 if (png_bytes.get()) { 399 icon_dir->idCount++; 400 icon_dir_count++; 401 } 402 403 size_t offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count); 404 for (size_t i = 0; i < bitmap_count; i++) { 405 ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(&buffer[offset]); 406 DCHECK_LT(offset, buffer_size); 407 size_t icon_image_size = 0; 408 SetSingleIconImageInformation(bitmaps[i], i, icon_dir, image, offset, 409 &icon_image_size); 410 DCHECK_GT(icon_image_size, 0U); 411 offset += icon_image_size; 412 } 413 414 // Add the PNG entry, if necessary. 415 if (png_bytes.get()) { 416 ICONDIRENTRY* entry = &icon_dir->idEntries[bitmap_count]; 417 entry->bWidth = 0; 418 entry->bHeight = 0; 419 entry->wPlanes = 1; 420 entry->wBitCount = 32; 421 entry->dwBytesInRes = static_cast<DWORD>(png_bytes->size()); 422 entry->dwImageOffset = static_cast<DWORD>(offset); 423 memcpy(&buffer[offset], png_bytes->front(), png_bytes->size()); 424 offset += png_bytes->size(); 425 } 426 427 DCHECK_EQ(offset, buffer_size); 428 429 // Finally, write the data to the file. 430 DWORD bytes_written; 431 bool delete_file = false; 432 if (!WriteFile(icon_file.Get(), &buffer[0], buffer_size, &bytes_written, 433 NULL) || 434 bytes_written != buffer_size) { 435 delete_file = true; 436 } 437 438 ::CloseHandle(icon_file.Take()); 439 if (delete_file) { 440 bool success = file_util::Delete(icon_path, false); 441 DCHECK(success); 442 } 443 444 return !delete_file; 445} 446 447bool IconUtil::PixelsHaveAlpha(const uint32* pixels, size_t num_pixels) { 448 for (const uint32* end = pixels + num_pixels; pixels != end; ++pixels) { 449 if ((*pixels & 0xff000000) != 0) 450 return true; 451 } 452 453 return false; 454} 455 456void IconUtil::InitializeBitmapHeader(BITMAPV5HEADER* header, int width, 457 int height) { 458 DCHECK(header); 459 memset(header, 0, sizeof(BITMAPV5HEADER)); 460 header->bV5Size = sizeof(BITMAPV5HEADER); 461 462 // Note that icons are created using top-down DIBs so we must negate the 463 // value used for the icon's height. 464 header->bV5Width = width; 465 header->bV5Height = -height; 466 header->bV5Planes = 1; 467 header->bV5Compression = BI_RGB; 468 469 // Initializing the bitmap format to 32 bit ARGB. 470 header->bV5BitCount = 32; 471 header->bV5RedMask = 0x00FF0000; 472 header->bV5GreenMask = 0x0000FF00; 473 header->bV5BlueMask = 0x000000FF; 474 header->bV5AlphaMask = 0xFF000000; 475 476 // Use the system color space. The default value is LCS_CALIBRATED_RGB, which 477 // causes us to crash if we don't specify the approprite gammas, etc. See 478 // <http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx> and 479 // <http://b/1283121>. 480 header->bV5CSType = LCS_WINDOWS_COLOR_SPACE; 481 482 // Use a valid value for bV5Intent as 0 is not a valid one. 483 // <http://msdn.microsoft.com/en-us/library/dd183381(VS.85).aspx> 484 header->bV5Intent = LCS_GM_IMAGES; 485} 486 487void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap, 488 size_t index, 489 ICONDIR* icon_dir, 490 ICONIMAGE* icon_image, 491 size_t image_offset, 492 size_t* image_byte_count) { 493 DCHECK(icon_dir != NULL); 494 DCHECK(icon_image != NULL); 495 DCHECK_GT(image_offset, 0U); 496 DCHECK(image_byte_count != NULL); 497 498 // We start by computing certain image values we'll use later on. 499 size_t xor_mask_size, bytes_in_resource; 500 ComputeBitmapSizeComponents(bitmap, 501 &xor_mask_size, 502 &bytes_in_resource); 503 504 icon_dir->idEntries[index].bWidth = static_cast<BYTE>(bitmap.width()); 505 icon_dir->idEntries[index].bHeight = static_cast<BYTE>(bitmap.height()); 506 icon_dir->idEntries[index].wPlanes = 1; 507 icon_dir->idEntries[index].wBitCount = 32; 508 icon_dir->idEntries[index].dwBytesInRes = bytes_in_resource; 509 icon_dir->idEntries[index].dwImageOffset = image_offset; 510 icon_image->icHeader.biSize = sizeof(BITMAPINFOHEADER); 511 512 // The width field in the BITMAPINFOHEADER structure accounts for the height 513 // of both the AND mask and the XOR mask so we need to multiply the bitmap's 514 // height by 2. The same does NOT apply to the width field. 515 icon_image->icHeader.biHeight = bitmap.height() * 2; 516 icon_image->icHeader.biWidth = bitmap.width(); 517 icon_image->icHeader.biPlanes = 1; 518 icon_image->icHeader.biBitCount = 32; 519 520 // We use a helper function for copying to actual bits from the SkBitmap 521 // object into the appropriate space in the buffer. We use a helper function 522 // (rather than just copying the bits) because there is no way to specify the 523 // orientation (bottom-up vs. top-down) of a bitmap residing in a .ico file. 524 // Thus, if we just copy the bits, we'll end up with a bottom up bitmap in 525 // the .ico file which will result in the icon being displayed upside down. 526 // The helper function copies the image into the buffer one scanline at a 527 // time. 528 // 529 // Note that we don't need to initialize the AND mask since the memory 530 // allocated for the icon data buffer was initialized to zero. The icon we 531 // create will therefore use an AND mask containing only zeros, which is OK 532 // because the underlying image has an alpha channel. An AND mask containing 533 // only zeros essentially means we'll initially treat all the pixels as 534 // opaque. 535 unsigned char* image_addr = reinterpret_cast<unsigned char*>(icon_image); 536 unsigned char* xor_mask_addr = image_addr + sizeof(BITMAPINFOHEADER); 537 CopySkBitmapBitsIntoIconBuffer(bitmap, xor_mask_addr, xor_mask_size); 538 *image_byte_count = bytes_in_resource; 539} 540 541void IconUtil::CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap, 542 unsigned char* buffer, 543 size_t buffer_size) { 544 SkAutoLockPixels bitmap_lock(bitmap); 545 unsigned char* bitmap_ptr = static_cast<unsigned char*>(bitmap.getPixels()); 546 size_t bitmap_size = bitmap.height() * bitmap.width() * 4; 547 DCHECK_EQ(buffer_size, bitmap_size); 548 for (size_t i = 0; i < bitmap_size; i += bitmap.width() * 4) { 549 memcpy(buffer + bitmap_size - bitmap.width() * 4 - i, 550 bitmap_ptr + i, 551 bitmap.width() * 4); 552 } 553} 554 555void IconUtil::CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize, 556 std::vector<SkBitmap>* bitmaps) { 557 DCHECK(bitmaps != NULL); 558 DCHECK(bitmaps->empty()); 559 560 bool inserted_original_bitmap = false; 561 for (size_t i = 0; i < arraysize(icon_dimensions_); i++) { 562 // If the dimensions of the bitmap we are resizing are the same as the 563 // current dimensions, then we should insert the bitmap and not a resized 564 // bitmap. If the bitmap's dimensions are smaller, we insert our bitmap 565 // first so that the bitmaps we return in the vector are sorted based on 566 // their dimensions. 567 if (!inserted_original_bitmap) { 568 if ((bitmap_to_resize.width() == icon_dimensions_[i]) && 569 (bitmap_to_resize.height() == icon_dimensions_[i])) { 570 bitmaps->push_back(bitmap_to_resize); 571 inserted_original_bitmap = true; 572 continue; 573 } 574 575 if ((bitmap_to_resize.width() < icon_dimensions_[i]) && 576 (bitmap_to_resize.height() < icon_dimensions_[i])) { 577 bitmaps->push_back(bitmap_to_resize); 578 inserted_original_bitmap = true; 579 } 580 } 581 bitmaps->push_back(skia::ImageOperations::Resize( 582 bitmap_to_resize, skia::ImageOperations::RESIZE_LANCZOS3, 583 icon_dimensions_[i], icon_dimensions_[i])); 584 } 585 586 if (!inserted_original_bitmap) 587 bitmaps->push_back(bitmap_to_resize); 588} 589 590size_t IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) { 591 DCHECK(!set.empty()); 592 593 // We start by counting the bytes for the structures that don't depend on the 594 // number of icon images. Note that sizeof(ICONDIR) already accounts for a 595 // single ICONDIRENTRY structure, which is why we subtract one from the 596 // number of bitmaps. 597 size_t total_buffer_size = sizeof(ICONDIR); 598 size_t bitmap_count = set.size(); 599 total_buffer_size += sizeof(ICONDIRENTRY) * (bitmap_count - 1); 600 DCHECK_GE(bitmap_count, arraysize(icon_dimensions_)); 601 602 // Add the bitmap specific structure sizes. 603 for (size_t i = 0; i < bitmap_count; i++) { 604 size_t xor_mask_size, bytes_in_resource; 605 ComputeBitmapSizeComponents(set[i], 606 &xor_mask_size, 607 &bytes_in_resource); 608 total_buffer_size += bytes_in_resource; 609 } 610 return total_buffer_size; 611} 612 613void IconUtil::ComputeBitmapSizeComponents(const SkBitmap& bitmap, 614 size_t* xor_mask_size, 615 size_t* bytes_in_resource) { 616 // The XOR mask size is easy to calculate since we only deal with 32bpp 617 // images. 618 *xor_mask_size = bitmap.width() * bitmap.height() * 4; 619 620 // Computing the AND mask is a little trickier since it is a monochrome 621 // bitmap (regardless of the number of bits per pixels used in the XOR mask). 622 // There are two things we must make sure we do when computing the AND mask 623 // size: 624 // 625 // 1. Make sure the right number of bytes is allocated for each AND mask 626 // scan line in case the number of pixels in the image is not divisible by 627 // 8. For example, in a 15X15 image, 15 / 8 is one byte short of 628 // containing the number of bits we need in order to describe a single 629 // image scan line so we need to add a byte. Thus, we need 2 bytes instead 630 // of 1 for each scan line. 631 // 632 // 2. Make sure each scan line in the AND mask is 4 byte aligned (so that the 633 // total icon image has a 4 byte alignment). In the 15X15 image example 634 // above, we can not use 2 bytes so we increase it to the next multiple of 635 // 4 which is 4. 636 // 637 // Once we compute the size for a singe AND mask scan line, we multiply that 638 // number by the image height in order to get the total number of bytes for 639 // the AND mask. Thus, for a 15X15 image, we need 15 * 4 which is 60 bytes 640 // for the monochrome bitmap representing the AND mask. 641 size_t and_line_length = (bitmap.width() + 7) >> 3; 642 and_line_length = (and_line_length + 3) & ~3; 643 size_t and_mask_size = and_line_length * bitmap.height(); 644 size_t masks_size = *xor_mask_size + and_mask_size; 645 *bytes_in_resource = masks_size + sizeof(BITMAPINFOHEADER); 646} 647