icon_util.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/icon_util.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_handle.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/image_operations.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/size.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ScopedICONINFO : ICONINFO {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedICONINFO() {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hbmColor = NULL;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hbmMask = NULL;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~ScopedICONINFO() {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (hbmColor)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::DeleteObject(hbmColor);
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (hbmMask)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ::DeleteObject(hbmMask);
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Defining the dimensions for the icon images. We store only one value because
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we always resize to a square image; that is, the value 48 means that we are
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// going to resize the given bitmap to a 48 by 48 pixels bitmap.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The icon images appear in the icon file in same order in which their
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// corresponding dimensions appear in the |icon_dimensions_| array, so it is
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// important to keep this array sorted. Also note that the maximum icon image
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// size we can handle is 255 by 255.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int IconUtil::icon_dimensions_[] = {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  8,    // Recommended by the MSDN as a nice to have icon size.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  10,   // Used by the Shell (e.g. for shortcuts).
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  14,   // Recommended by the MSDN as a nice to have icon size.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  16,   // Toolbar, Application and Shell icon sizes.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  22,   // Recommended by the MSDN as a nice to have icon size.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  24,   // Used by the Shell (e.g. for shortcuts).
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  32,   // Toolbar, Dialog and Wizard icon size.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  40,   // Quick Launch.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  48,   // Alt+Tab icon size.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  64,   // Recommended by the MSDN as a nice to have icon size.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  96,   // Recommended by the MSDN as a nice to have icon size.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  128   // Used by the Shell (e.g. for shortcuts).
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only 32 bit ARGB bitmaps are supported. We also try to perform as many
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // validations as we can on the bitmap.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((bitmap.config() != SkBitmap::kARGB_8888_Config) ||
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (bitmap.width() <= 0) || (bitmap.height() <= 0) ||
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (bitmap.getPixels() == NULL))
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start by creating a DIB which we'll use later on in order to create
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the HICON. We use BITMAPV5HEADER since the bitmap we are about to convert
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // may contain an alpha channel and the V5 header allows us to specify the
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // alpha mask for the DIB.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BITMAPV5HEADER bitmap_header;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height());
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void* bits;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HDC hdc = ::GetDC(NULL);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HBITMAP dib;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header),
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           DIB_RGB_COLORS, &bits, NULL, 0);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dib);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::ReleaseDC(NULL, hdc);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Icons are generally created using an AND and XOR masks where the AND
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // specifies boolean transparency (the pixel is either opaque or
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // transparent) and the XOR mask contains the actual image pixels. If the XOR
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mask bitmap has an alpha channel, the AND monochrome bitmap won't
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // actually be used for computing the pixel transparency. Even though all our
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap has an alpha channel, Windows might not agree when all alpha values
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are zero. So the monochrome bitmap is created with all pixels transparent
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for this case. Otherwise, it is created with all pixels opaque.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool bitmap_has_alpha_channel = PixelsHaveAlpha(
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const uint32*>(bitmap.getPixels()),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bitmap.width() * bitmap.height());
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_array<uint8> mask_bits;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!bitmap_has_alpha_channel) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Bytes per line with paddings to make it word alignment.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t bytes_per_line = (bitmap.width() + 0xF) / 16 * 2;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t mask_bits_size = bytes_per_line * bitmap.height();
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mask_bits.reset(new uint8[mask_bits_size]);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(mask_bits.get());
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make all pixels transparent.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memset(mask_bits.get(), 0xFF, mask_bits_size);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HBITMAP mono_bitmap = ::CreateBitmap(bitmap.width(), bitmap.height(), 1, 1,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<LPVOID>(mask_bits.get()));
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(mono_bitmap);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ICONINFO icon_info;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_info.fIcon = TRUE;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_info.xHotspot = 0;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_info.yHotspot = 0;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_info.hbmMask = mono_bitmap;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_info.hbmColor = dib;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HICON icon = ::CreateIconIndirect(&icon_info);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DeleteObject(dib);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DeleteObject(mono_bitmap);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return icon;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start with validating parameters.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon || s.IsEmpty())
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedICONINFO icon_info;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::GetIconInfo(icon, &icon_info))
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon_info.fIcon)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new SkBitmap(CreateSkBitmapFromHICONHelper(icon, s));
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start with validating parameters.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedICONINFO icon_info;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BITMAP bitmap_info = { 0 };
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::GetIconInfo(icon, &icon_info))
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::GetObject(icon_info.hbmMask, sizeof(bitmap_info), &bitmap_info))
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Size icon_size(bitmap_info.bmWidth, bitmap_info.bmHeight);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new SkBitmap(CreateSkBitmapFromHICONHelper(icon, icon_size));
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap IconUtil::CreateSkBitmapFromHICONHelper(HICON icon,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 const gfx::Size& s) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(icon);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!s.IsEmpty());
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Allocating memory for the SkBitmap object. We are going to create an ARGB
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap so we should set the configuration appropriately.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap bitmap;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bitmap.setConfig(SkBitmap::kARGB_8888_Config, s.width(), s.height());
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bitmap.allocPixels();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bitmap.eraseARGB(0, 0, 0, 0);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now we should create a DIB so that we can use ::DrawIconEx in order to
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // obtain the icon's image.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BITMAPV5HEADER h;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitializeBitmapHeader(&h, s.width(), s.height());
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HDC hdc = ::GetDC(NULL);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32* bits;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HBITMAP dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&h),
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DIB_RGB_COLORS, reinterpret_cast<void**>(&bits), NULL, 0);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dib);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HDC dib_dc = CreateCompatibleDC(hdc);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::ReleaseDC(NULL, hdc);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dib_dc);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HGDIOBJ old_obj = ::SelectObject(dib_dc, dib);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Windows icons are defined using two different masks. The XOR mask, which
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // represents the icon image and an AND mask which is a monochrome bitmap
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which indicates the transparency of each pixel.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To make things more complex, the icon image itself can be an ARGB bitmap
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and therefore contain an alpha channel which specifies the transparency
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for each pixel. Unfortunately, there is no easy way to determine whether
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // or not a bitmap has an alpha channel and therefore constructing the bitmap
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the icon is nothing but straightforward.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The idea is to read the AND mask but use it only if we know for sure that
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the icon image does not have an alpha channel. The only way to tell if the
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap has an alpha channel is by looking through the pixels and checking
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // whether there are non-zero alpha bytes.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start by drawing the AND mask into our DIB.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t num_pixels = s.GetArea();
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(bits, 0, num_pixels * 4);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_MASK);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Capture boolean opacity. We may not use it if we find out the bitmap has
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an alpha channel.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_array<bool> opaque(new bool[num_pixels]);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < num_pixels; ++i)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    opaque[i] = !bits[i];
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Then draw the image itself which is really the XOR mask.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(bits, 0, num_pixels * 4);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_NORMAL);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(bitmap.getPixels(), static_cast<void*>(bits), num_pixels * 4);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finding out whether the bitmap has an alpha channel.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool bitmap_has_alpha_channel = PixelsHaveAlpha(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const uint32*>(bitmap.getPixels()), num_pixels);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the bitmap does not have an alpha channel, we need to build it using
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the previously captured AND mask. Otherwise, we are done.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!bitmap_has_alpha_channel) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32* p = static_cast<uint32*>(bitmap.getPixels());
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < num_pixels; ++p, ++i) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK_EQ((*p & 0xff000000), 0u);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (opaque[i])
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *p |= 0xff000000;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *p &= 0x00ffffff;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::SelectObject(dib_dc, old_obj);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DeleteObject(dib);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::DeleteDC(dib_dc);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bitmap;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap,
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const FilePath& icon_path) {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // been properly initialized.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((bitmap.config() != SkBitmap::kARGB_8888_Config) ||
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (bitmap.height() <= 0) || (bitmap.width() <= 0) ||
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (bitmap.getPixels() == NULL))
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start by creating the file.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedHandle icon_file(::CreateFile(icon_path.value().c_str(),
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon_file.IsValid())
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Creating a set of bitmaps corresponding to the icon images we'll end up
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // storing in the icon file. Each bitmap is created by resizing the given
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap to the desired size.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SkBitmap> bitmaps;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateResizedBitmapSet(bitmap, &bitmaps);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!bitmaps.empty());
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t bitmap_count = bitmaps.size();
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Computing the total size of the buffer we need in order to store the
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // images in the desired icon format.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t buffer_size = ComputeIconFileBufferSize(bitmaps);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned char* buffer = new unsigned char[buffer_size];
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(buffer != NULL);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(buffer, 0, buffer_size);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Setting the information in the structures residing within the buffer.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First, we set the information which doesn't require iterating through the
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap set and then we set the bitmap specific structures. In the latter
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // step we also copy the actual bits.
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(buffer);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idType = kResourceTypeIcon;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idCount = bitmap_count;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t icon_dir_count = bitmap_count - 1;  // Note DCHECK(!bitmaps.empty())!
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < bitmap_count; i++) {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(buffer + offset);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LT(offset, buffer_size);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t icon_image_size = 0;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetSingleIconImageInformation(bitmaps[i], i, icon_dir, image, offset,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  &icon_image_size);
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_GT(icon_image_size, 0U);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset += icon_image_size;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(offset, buffer_size);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finally, writing the data info the file.
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD bytes_written;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool delete_file = false;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!WriteFile(icon_file.Get(), buffer, buffer_size, &bytes_written, NULL) ||
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bytes_written != buffer_size)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete_file = true;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::CloseHandle(icon_file.Take());
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete [] buffer;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delete_file) {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool success = file_util::Delete(icon_path, false);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(success);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !delete_file;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IconUtil::PixelsHaveAlpha(const uint32* pixels, size_t num_pixels) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (const uint32* end = pixels + num_pixels; pixels != end; ++pixels) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((*pixels & 0xff000000) != 0)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IconUtil::InitializeBitmapHeader(BITMAPV5HEADER* header, int width,
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      int height) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(header);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(header, 0, sizeof(BITMAPV5HEADER));
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Size = sizeof(BITMAPV5HEADER);
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Note that icons are created using top-down DIBs so we must negate the
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // value used for the icon's height.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Width = width;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Height = -height;
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Planes = 1;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Compression = BI_RGB;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initializing the bitmap format to 32 bit ARGB.
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5BitCount = 32;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5RedMask = 0x00FF0000;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5GreenMask = 0x0000FF00;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5BlueMask = 0x000000FF;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5AlphaMask = 0xFF000000;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use the system color space.  The default value is LCS_CALIBRATED_RGB, which
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // causes us to crash if we don't specify the approprite gammas, etc.  See
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // <http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx> and
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // <http://b/1283121>.
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use a valid value for bV5Intent as 0 is not a valid one.
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // <http://msdn.microsoft.com/en-us/library/dd183381(VS.85).aspx>
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  header->bV5Intent = LCS_GM_IMAGES;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap,
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             size_t index,
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             ICONDIR* icon_dir,
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             ICONIMAGE* icon_image,
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             size_t image_offset,
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             size_t* image_byte_count) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(icon_dir != NULL);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(icon_image != NULL);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(image_offset, 0U);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(image_byte_count != NULL);
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start by computing certain image values we'll use later on.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t xor_mask_size, bytes_in_resource;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ComputeBitmapSizeComponents(bitmap,
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              &xor_mask_size,
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              &bytes_in_resource);
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].bWidth = static_cast<BYTE>(bitmap.width());
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].bHeight = static_cast<BYTE>(bitmap.height());
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].wPlanes = 1;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].wBitCount = 32;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].dwBytesInRes = bytes_in_resource;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_dir->idEntries[index].dwImageOffset = image_offset;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_image->icHeader.biSize = sizeof(BITMAPINFOHEADER);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The width field in the BITMAPINFOHEADER structure accounts for the height
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of both the AND mask and the XOR mask so we need to multiply the bitmap's
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // height by 2. The same does NOT apply to the width field.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_image->icHeader.biHeight = bitmap.height() * 2;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_image->icHeader.biWidth = bitmap.width();
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_image->icHeader.biPlanes = 1;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_image->icHeader.biBitCount = 32;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We use a helper function for copying to actual bits from the SkBitmap
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // object into the appropriate space in the buffer. We use a helper function
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (rather than just copying the bits) because there is no way to specify the
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // orientation (bottom-up vs. top-down) of a bitmap residing in a .ico file.
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Thus, if we just copy the bits, we'll end up with a bottom up bitmap in
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the .ico file which will result in the icon being displayed upside down.
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The helper function copies the image into the buffer one scanline at a
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // time.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Note that we don't need to initialize the AND mask since the memory
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // allocated for the icon data buffer was initialized to zero. The icon we
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // create will therefore use an AND mask containing only zeros, which is OK
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because the underlying image has an alpha channel. An AND mask containing
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // only zeros essentially means we'll initially treat all the pixels as
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opaque.
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned char* image_addr = reinterpret_cast<unsigned char*>(icon_image);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned char* xor_mask_addr = image_addr + sizeof(BITMAPINFOHEADER);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CopySkBitmapBitsIntoIconBuffer(bitmap, xor_mask_addr, xor_mask_size);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *image_byte_count = bytes_in_resource;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IconUtil::CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap,
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              unsigned char* buffer,
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              size_t buffer_size) {
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels bitmap_lock(bitmap);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned char* bitmap_ptr = static_cast<unsigned char*>(bitmap.getPixels());
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t bitmap_size = bitmap.height() * bitmap.width() * 4;
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(buffer_size, bitmap_size);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < bitmap_size; i += bitmap.width() * 4) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memcpy(buffer + bitmap_size - bitmap.width() * 4 - i,
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           bitmap_ptr + i,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           bitmap.width() * 4);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IconUtil::CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize,
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      std::vector<SkBitmap>* bitmaps) {
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(bitmaps != NULL);
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(bitmaps->empty());
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool inserted_original_bitmap = false;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < arraysize(icon_dimensions_); i++) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the dimensions of the bitmap we are resizing are the same as the
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // current dimensions, then we should insert the bitmap and not a resized
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // bitmap. If the bitmap's dimensions are smaller, we insert our bitmap
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // first so that the bitmaps we return in the vector are sorted based on
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // their dimensions.
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!inserted_original_bitmap) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((bitmap_to_resize.width() == icon_dimensions_[i]) &&
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (bitmap_to_resize.height() == icon_dimensions_[i])) {
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bitmaps->push_back(bitmap_to_resize);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        inserted_original_bitmap = true;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((bitmap_to_resize.width() < icon_dimensions_[i]) &&
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (bitmap_to_resize.height() < icon_dimensions_[i])) {
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bitmaps->push_back(bitmap_to_resize);
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        inserted_original_bitmap = true;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bitmaps->push_back(skia::ImageOperations::Resize(
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bitmap_to_resize, skia::ImageOperations::RESIZE_LANCZOS3,
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        icon_dimensions_[i], icon_dimensions_[i]));
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!inserted_original_bitmap)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bitmaps->push_back(bitmap_to_resize);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) {
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!set.empty());
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We start by counting the bytes for the structures that don't depend on the
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // number of icon images. Note that sizeof(ICONDIR) already accounts for a
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // single ICONDIRENTRY structure, which is why we subtract one from the
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // number of bitmaps.
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t total_buffer_size = sizeof(ICONDIR);
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t bitmap_count = set.size();
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  total_buffer_size += sizeof(ICONDIRENTRY) * (bitmap_count - 1);
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GE(bitmap_count, arraysize(icon_dimensions_));
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the bitmap specific structure sizes.
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < bitmap_count; i++) {
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t xor_mask_size, bytes_in_resource;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ComputeBitmapSizeComponents(set[i],
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &xor_mask_size,
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &bytes_in_resource);
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total_buffer_size += bytes_in_resource;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return total_buffer_size;
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IconUtil::ComputeBitmapSizeComponents(const SkBitmap& bitmap,
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           size_t* xor_mask_size,
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           size_t* bytes_in_resource) {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The XOR mask size is easy to calculate since we only deal with 32bpp
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // images.
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *xor_mask_size = bitmap.width() * bitmap.height() * 4;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Computing the AND mask is a little trickier since it is a monochrome
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bitmap (regardless of the number of bits per pixels used in the XOR mask).
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There are two things we must make sure we do when computing the AND mask
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // size:
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 1. Make sure the right number of bytes is allocated for each AND mask
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    scan line in case the number of pixels in the image is not divisible by
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    8. For example, in a 15X15 image, 15 / 8 is one byte short of
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    containing the number of bits we need in order to describe a single
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    image scan line so we need to add a byte. Thus, we need 2 bytes instead
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    of 1 for each scan line.
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 2. Make sure each scan line in the AND mask is 4 byte aligned (so that the
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    total icon image has a 4 byte alignment). In the 15X15 image example
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    above, we can not use 2 bytes so we increase it to the next multiple of
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    4 which is 4.
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Once we compute the size for a singe AND mask scan line, we multiply that
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // number by the image height in order to get the total number of bytes for
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the AND mask. Thus, for a 15X15 image, we need 15 * 4 which is 60 bytes
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the monochrome bitmap representing the AND mask.
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t and_line_length = (bitmap.width() + 7) >> 3;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  and_line_length = (and_line_length + 3) & ~3;
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t and_mask_size = and_line_length * bitmap.height();
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t masks_size = *xor_mask_size + and_mask_size;
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *bytes_in_resource = masks_size + sizeof(BITMAPINFOHEADER);
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
500