Home | History | Annotate | Download | only in frame
      1 // Copyright (c) 2013 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 "chrome/browser/ui/views/frame/taskbar_decorator.h"
      6 
      7 #include <shobjidl.h>
      8 
      9 #include "base/bind.h"
     10 #include "base/location.h"
     11 #include "base/threading/worker_pool.h"
     12 #include "base/win/scoped_com_initializer.h"
     13 #include "base/win/scoped_comptr.h"
     14 #include "base/win/scoped_gdi_object.h"
     15 #include "base/win/windows_version.h"
     16 #include "chrome/browser/profiles/profile_info_util.h"
     17 #include "chrome/browser/ui/host_desktop.h"
     18 #include "skia/ext/image_operations.h"
     19 #include "skia/ext/platform_canvas.h"
     20 #include "third_party/skia/include/core/SkRect.h"
     21 #include "ui/gfx/icon_util.h"
     22 #include "ui/gfx/image/image.h"
     23 #include "ui/views/win/hwnd_util.h"
     24 
     25 namespace chrome {
     26 
     27 namespace {
     28 
     29 // Responsible for invoking TaskbarList::SetOverlayIcon(). The call to
     30 // TaskbarList::SetOverlayIcon() runs a nested message loop that proves
     31 // problematic when called on the UI thread. Additionally it seems the call may
     32 // take a while to complete. For this reason we call it on a worker thread.
     33 //
     34 // Docs for TaskbarList::SetOverlayIcon() say it does nothing if the HWND is not
     35 // valid.
     36 void SetOverlayIcon(HWND hwnd, scoped_ptr<SkBitmap> bitmap) {
     37   base::win::ScopedCOMInitializer com_initializer;
     38   base::win::ScopedComPtr<ITaskbarList3> taskbar;
     39   HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
     40                                           CLSCTX_INPROC_SERVER);
     41   if (FAILED(result) || FAILED(taskbar->HrInit()))
     42     return;
     43 
     44   base::win::ScopedGDIObject<HICON> icon;
     45   if (bitmap.get()) {
     46     const SkBitmap* source_bitmap = NULL;
     47     SkBitmap squarer_bitmap;
     48     if ((bitmap->width() == profiles::kAvatarIconWidth) &&
     49         (bitmap->height() == profiles::kAvatarIconHeight)) {
     50       // Shave a couple of columns so the bitmap is more square. So when
     51       // resized to a square aspect ratio it looks pretty.
     52       int x = 2;
     53       bitmap->extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
     54           profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
     55       source_bitmap = &squarer_bitmap;
     56     } else {
     57       // The image's size has changed. Resize what we have.
     58       source_bitmap = bitmap.get();
     59     }
     60 
     61     // Maintain aspect ratio on resize. It is assumed that the image is wider
     62     // than it is tall.
     63     const size_t kOverlayIconSize = 16;
     64     size_t resized_height =
     65         source_bitmap->height() * kOverlayIconSize / source_bitmap->width();
     66     DCHECK_GE(kOverlayIconSize, resized_height);
     67     // Since the target size is so small, we use our best resizer.
     68     SkBitmap sk_icon = skia::ImageOperations::Resize(
     69         *source_bitmap,
     70         skia::ImageOperations::RESIZE_LANCZOS3,
     71         kOverlayIconSize, resized_height);
     72 
     73     // Paint the resized icon onto a 16x16 canvas otherwise Windows will badly
     74     // hammer it to 16x16.
     75     scoped_ptr<SkCanvas> offscreen_canvas(
     76         skia::CreateBitmapCanvas(kOverlayIconSize, kOverlayIconSize, false));
     77     DCHECK(offscreen_canvas);
     78     offscreen_canvas->drawBitmap(sk_icon, 0, kOverlayIconSize - resized_height);
     79 
     80     icon.Set(IconUtil::CreateHICONFromSkBitmap(
     81         offscreen_canvas->getDevice()->accessBitmap(false)));
     82     if (!icon.Get())
     83       return;
     84   }
     85   taskbar->SetOverlayIcon(hwnd, icon, L"");
     86 }
     87 
     88 }  // namespace
     89 
     90 void DrawTaskbarDecoration(gfx::NativeWindow window, const gfx::Image* image) {
     91   // HOST_DESKTOP_TYPE_ASH doesn't use the taskbar.
     92   if (base::win::GetVersion() < base::win::VERSION_WIN7 ||
     93       chrome::GetHostDesktopTypeForNativeWindow(window) !=
     94       chrome::HOST_DESKTOP_TYPE_NATIVE)
     95     return;
     96 
     97   HWND hwnd = views::HWNDForNativeWindow(window);
     98 
     99   // SetOverlayIcon() does nothing if the window is not visible so testing here
    100   // avoids all the wasted effort of the image resizing.
    101   if (!::IsWindowVisible(hwnd))
    102     return;
    103 
    104   // Copy the image since we're going to use it on a separate thread and
    105   // gfx::Image isn't thread safe.
    106   scoped_ptr<SkBitmap> bitmap(
    107       image ? new SkBitmap(*image->ToSkBitmap()) : NULL);
    108   // TaskbarList::SetOverlayIcon() may take a while, so we use slow here.
    109   base::WorkerPool::PostTask(
    110       FROM_HERE, base::Bind(&SetOverlayIcon, hwnd, Passed(&bitmap)), true);
    111 }
    112 
    113 }  // namespace chrome
    114