blob: aab7e5a5f4e672d496e059abff6411862c4d60f3 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/frame/taskbar_decorator_win.h"
#include <objbase.h>
#include <shobjidl.h>
#include "base/bind.h"
#include "base/location.h"
#include "base/task_scheduler/post_task.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_gdi_object.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/image/image.h"
#include "ui/views/win/hwnd_util.h"
namespace chrome {
namespace {
// Responsible for invoking TaskbarList::SetOverlayIcon(). The call to
// TaskbarList::SetOverlayIcon() runs a nested run loop that proves
// problematic when called on the UI thread. Additionally it seems the call may
// take a while to complete. For this reason we call it on a worker thread.
//
// Docs for TaskbarList::SetOverlayIcon() say it does nothing if the HWND is not
// valid.
void SetOverlayIcon(HWND hwnd, std::unique_ptr<SkBitmap> bitmap) {
base::win::ScopedComPtr<ITaskbarList3> taskbar;
HRESULT result = ::CoCreateInstance(
CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbar));
if (FAILED(result) || FAILED(taskbar->HrInit()))
return;
base::win::ScopedGDIObject<HICON> icon;
if (bitmap.get()) {
DCHECK_GE(bitmap.get()->width(), bitmap.get()->height());
// Maintain aspect ratio on resize.
const int kOverlayIconSize = 16;
int resized_height =
bitmap.get()->height() * kOverlayIconSize / bitmap.get()->width();
DCHECK_GE(kOverlayIconSize, resized_height);
// Since the target size is so small, we use our best resizer.
SkBitmap sk_icon = skia::ImageOperations::Resize(
*bitmap.get(),
skia::ImageOperations::RESIZE_LANCZOS3,
kOverlayIconSize, resized_height);
// Paint the resized icon onto a 16x16 canvas otherwise Windows will badly
// hammer it to 16x16.
SkBitmap offscreen_bitmap;
offscreen_bitmap.allocN32Pixels(kOverlayIconSize, kOverlayIconSize);
SkCanvas offscreen_canvas(offscreen_bitmap);
offscreen_canvas.clear(SK_ColorTRANSPARENT);
offscreen_canvas.drawBitmap(sk_icon, 0, kOverlayIconSize - resized_height);
icon = IconUtil::CreateHICONFromSkBitmap(offscreen_bitmap);
if (!icon.is_valid())
return;
}
taskbar->SetOverlayIcon(hwnd, icon.get(), L"");
}
} // namespace
void DrawTaskbarDecoration(gfx::NativeWindow window, const gfx::Image* image) {
HWND hwnd = views::HWNDForNativeWindow(window);
// SetOverlayIcon() does nothing if the window is not visible so testing here
// avoids all the wasted effort of the image resizing.
if (!::IsWindowVisible(hwnd))
return;
// Copy the image since we're going to use it on a separate thread and
// gfx::Image isn't thread safe.
std::unique_ptr<SkBitmap> bitmap;
if (image) {
bitmap.reset(new SkBitmap(
profiles::GetAvatarIconAsSquare(*image->ToSkBitmap(), 1)));
}
// TODO(robliao): Annotate this task with .WithCOM() once supported.
// https://crbug.com/662122
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::Bind(&SetOverlayIcon, hwnd, base::Passed(&bitmap)));
}
} // namespace chrome