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