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