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/win/scoped_comptr.h"
12#include "base/win/scoped_gdi_object.h"
13#include "base/win/windows_version.h"
14#include "chrome/browser/profiles/profile_avatar_icon_util.h"
15#include "chrome/browser/ui/host_desktop.h"
16#include "content/public/browser/browser_thread.h"
17#include "skia/ext/image_operations.h"
18#include "skia/ext/platform_canvas.h"
19#include "ui/gfx/icon_util.h"
20#include "ui/gfx/image/image.h"
21#include "ui/views/win/hwnd_util.h"
22
23namespace chrome {
24
25namespace {
26
27// Responsible for invoking TaskbarList::SetOverlayIcon(). The call to
28// TaskbarList::SetOverlayIcon() runs a nested message loop that proves
29// problematic when called on the UI thread. Additionally it seems the call may
30// take a while to complete. For this reason we call it on a worker thread.
31//
32// Docs for TaskbarList::SetOverlayIcon() say it does nothing if the HWND is not
33// valid.
34void SetOverlayIcon(HWND hwnd, scoped_ptr<SkBitmap> bitmap) {
35  base::win::ScopedComPtr<ITaskbarList3> taskbar;
36  HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL,
37                                          CLSCTX_INPROC_SERVER);
38  if (FAILED(result) || FAILED(taskbar->HrInit()))
39    return;
40
41  base::win::ScopedGDIObject<HICON> icon;
42  if (bitmap.get()) {
43    DCHECK_GE(bitmap.get()->width(), bitmap.get()->height());
44    // Maintain aspect ratio on resize.
45    const int kOverlayIconSize = 16;
46    int resized_height =
47        bitmap.get()->height() * kOverlayIconSize / bitmap.get()->width();
48    DCHECK_GE(kOverlayIconSize, resized_height);
49    // Since the target size is so small, we use our best resizer.
50    SkBitmap sk_icon = skia::ImageOperations::Resize(
51        *bitmap.get(),
52        skia::ImageOperations::RESIZE_LANCZOS3,
53        kOverlayIconSize, resized_height);
54
55    // Paint the resized icon onto a 16x16 canvas otherwise Windows will badly
56    // hammer it to 16x16.
57    SkBitmap offscreen_bitmap;
58    offscreen_bitmap.allocN32Pixels(kOverlayIconSize, kOverlayIconSize);
59    SkCanvas offscreen_canvas(offscreen_bitmap);
60    offscreen_canvas.clear(SK_ColorTRANSPARENT);
61    offscreen_canvas.drawBitmap(sk_icon, 0, kOverlayIconSize - resized_height);
62    icon.Set(IconUtil::CreateHICONFromSkBitmap(offscreen_bitmap));
63    if (!icon.Get())
64      return;
65  }
66  taskbar->SetOverlayIcon(hwnd, icon, L"");
67}
68
69}  // namespace
70
71void DrawTaskbarDecoration(gfx::NativeWindow window, const gfx::Image* image) {
72  // HOST_DESKTOP_TYPE_ASH doesn't use the taskbar.
73  if (base::win::GetVersion() < base::win::VERSION_WIN7 ||
74      chrome::GetHostDesktopTypeForNativeWindow(window) !=
75      chrome::HOST_DESKTOP_TYPE_NATIVE)
76    return;
77
78  HWND hwnd = views::HWNDForNativeWindow(window);
79
80  // SetOverlayIcon() does nothing if the window is not visible so testing here
81  // avoids all the wasted effort of the image resizing.
82  if (!::IsWindowVisible(hwnd))
83    return;
84
85  // Copy the image since we're going to use it on a separate thread and
86  // gfx::Image isn't thread safe.
87  scoped_ptr<SkBitmap> bitmap;
88  if (image) {
89    bitmap.reset(new SkBitmap(
90        profiles::GetAvatarIconAsSquare(*image->ToSkBitmap(), 1)));
91  }
92  content::BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
93      FROM_HERE, base::Bind(&SetOverlayIcon, hwnd, Passed(&bitmap)),
94      base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
95}
96
97}  // namespace chrome
98