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 "third_party/skia/include/core/SkRect.h"
20#include "ui/gfx/icon_util.h"
21#include "ui/gfx/image/image.h"
22#include "ui/views/win/hwnd_util.h"
23
24namespace chrome {
25
26namespace {
27
28// Responsible for invoking TaskbarList::SetOverlayIcon(). The call to
29// TaskbarList::SetOverlayIcon() runs a nested message loop that proves
30// problematic when called on the UI thread. Additionally it seems the call may
31// take a while to complete. For this reason we call it on a worker thread.
32//
33// Docs for TaskbarList::SetOverlayIcon() say it does nothing if the HWND is not
34// valid.
35void SetOverlayIcon(HWND hwnd, scoped_ptr<SkBitmap> bitmap) {
36  base::win::ScopedCOMInitializer com_initializer;
37  base::win::ScopedComPtr<ITaskbarList3> taskbar;
38  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
39                                          CLSCTX_INPROC_SERVER);
40  if (FAILED(result) || FAILED(taskbar->HrInit()))
41    return;
42
43  base::win::ScopedGDIObject<HICON> icon;
44  if (bitmap.get()) {
45    const SkBitmap* source_bitmap = NULL;
46    SkBitmap squarer_bitmap;
47    if ((bitmap->width() == profiles::kAvatarIconWidth) &&
48        (bitmap->height() == profiles::kAvatarIconHeight)) {
49      // Shave a couple of columns so the bitmap is more square. So when
50      // resized to a square aspect ratio it looks pretty.
51      int x = 2;
52      bitmap->extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
53          profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
54      source_bitmap = &squarer_bitmap;
55    } else {
56      // The image's size has changed. Resize what we have.
57      source_bitmap = bitmap.get();
58    }
59    // Since the target size is so small, we use our best resizer. Never pass
60    // windows a different size because it will badly hammer it to 16x16.
61    SkBitmap sk_icon = skia::ImageOperations::Resize(
62        *source_bitmap,
63        skia::ImageOperations::RESIZE_LANCZOS3,
64        16, 16);
65    icon.Set(IconUtil::CreateHICONFromSkBitmap(sk_icon));
66    if (!icon.Get())
67      return;
68  }
69  taskbar->SetOverlayIcon(hwnd, icon, L"");
70}
71
72}  // namespace
73
74void DrawTaskbarDecoration(gfx::NativeWindow window, const gfx::Image* image) {
75  // HOST_DESKTOP_TYPE_ASH doesn't use the taskbar.
76  if (base::win::GetVersion() < base::win::VERSION_WIN7 ||
77      chrome::GetHostDesktopTypeForNativeWindow(window) !=
78      chrome::HOST_DESKTOP_TYPE_NATIVE)
79    return;
80
81  HWND hwnd = views::HWNDForNativeWindow(window);
82
83  // SetOverlayIcon() does nothing if the window is not visible so testing here
84  // avoids all the wasted effort of the image resizing.
85  if (!::IsWindowVisible(hwnd))
86    return;
87
88  // Copy the image since we're going to use it on a separate thread and
89  // gfx::Image isn't thread safe.
90  scoped_ptr<SkBitmap> bitmap(
91      image ? new SkBitmap(*image->ToSkBitmap()) : NULL);
92  // TaskbarList::SetOverlayIcon() may take a while, so we use slow here.
93  base::WorkerPool::PostTask(
94      FROM_HERE, base::Bind(&SetOverlayIcon, hwnd, Passed(&bitmap)), true);
95}
96
97}  // namespace chrome
98