taskbar_window_thumbnailer_win.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 2012 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/panels/taskbar_window_thumbnailer_win.h"
6
7#include <dwmapi.h>
8
9#include "base/logging.h"
10#include "base/win/scoped_hdc.h"
11#include "skia/ext/image_operations.h"
12#include "ui/gfx/canvas.h"
13#include "ui/gfx/gdi_util.h"
14
15namespace {
16
17HBITMAP GetNativeBitmapFromSkBitmap(const SkBitmap& bitmap) {
18  int width = bitmap.width();
19  int height = bitmap.height();
20
21  BITMAPV4HEADER native_bitmap_header;
22  gfx::CreateBitmapV4Header(width, height, &native_bitmap_header);
23
24  HDC dc = ::GetDC(NULL);
25  void* bits;
26  HBITMAP native_bitmap = ::CreateDIBSection(dc,
27      reinterpret_cast<BITMAPINFO*>(&native_bitmap_header),
28      DIB_RGB_COLORS,
29      &bits,
30      NULL,
31      0);
32  DCHECK(native_bitmap);
33  ::ReleaseDC(NULL, dc);
34  bitmap.copyPixelsTo(bits, width * height * 4, width * 4);
35  return native_bitmap;
36}
37
38void EnableCustomThumbnail(HWND hwnd, bool enable) {
39  BOOL enable_value = enable;
40  ::DwmSetWindowAttribute(hwnd,
41                          DWMWA_FORCE_ICONIC_REPRESENTATION,
42                          &enable_value,
43                          sizeof(enable_value));
44  ::DwmSetWindowAttribute(hwnd,
45                          DWMWA_HAS_ICONIC_BITMAP,
46                          &enable_value,
47                          sizeof(enable_value));
48}
49
50}  // namespace
51
52
53TaskbarWindowThumbnailerWin::TaskbarWindowThumbnailerWin(
54    HWND hwnd, TaskbarWindowThumbnailerDelegateWin* delegate)
55    : hwnd_(hwnd),
56      delegate_(delegate) {
57  ui::HWNDSubclass::AddFilterToTarget(hwnd_, this);
58}
59
60TaskbarWindowThumbnailerWin::~TaskbarWindowThumbnailerWin() {
61  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
62}
63
64void TaskbarWindowThumbnailerWin::Start() {
65  EnableCustomThumbnail(hwnd_, true);
66}
67
68void TaskbarWindowThumbnailerWin::Stop() {
69  capture_bitmap_.reset();
70  EnableCustomThumbnail(hwnd_, false);
71}
72
73void TaskbarWindowThumbnailerWin::CaptureSnapshot() {
74  if (!capture_bitmap_)
75    capture_bitmap_.reset(CaptureWindowImage());
76}
77
78void TaskbarWindowThumbnailerWin::InvalidateSnapshot() {
79  capture_bitmap_.reset();
80
81  // The snapshot feeded to the system could be cached. Invalidate it.
82  ::DwmInvalidateIconicBitmaps(hwnd_);
83}
84
85void TaskbarWindowThumbnailerWin::ReplaceWindow(HWND new_hwnd) {
86  // Stop serving the custom thumbnail for the old window.
87  EnableCustomThumbnail(hwnd_, false);
88  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
89
90  hwnd_ = new_hwnd;
91
92  // Start serving the custom thumbnail to the new window.
93  ui::HWNDSubclass::AddFilterToTarget(hwnd_, this);
94  EnableCustomThumbnail(hwnd_, true);
95}
96
97bool TaskbarWindowThumbnailerWin::FilterMessage(HWND hwnd,
98                                                UINT message,
99                                                WPARAM w_param,
100                                                LPARAM l_param,
101                                                LRESULT* l_result) {
102  DCHECK_EQ(hwnd_, hwnd);
103  switch (message) {
104    case WM_DWMSENDICONICTHUMBNAIL:
105      return OnDwmSendIconicThumbnail(HIWORD(l_param),
106                                      LOWORD(l_param),
107                                      l_result);
108    case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
109      return OnDwmSendIconicLivePreviewBitmap(l_result);
110  }
111  return false;
112}
113
114bool TaskbarWindowThumbnailerWin::OnDwmSendIconicThumbnail(
115    int width, int height, LRESULT* l_result) {
116  CaptureSnapshot();
117
118  SkBitmap* thumbnail_bitmap = capture_bitmap_.get();
119
120  // Scale the image if needed.
121  SkBitmap scaled_bitmap;
122  if (capture_bitmap_->width() != width ||
123      capture_bitmap_->height() != height) {
124    double x_scale = static_cast<double>(width) / capture_bitmap_->width();
125    double y_scale = static_cast<double>(height) / capture_bitmap_->height();
126    double scale = std::min(x_scale, y_scale);
127    width = capture_bitmap_->width() * scale;
128    height = capture_bitmap_->height() * scale;
129    scaled_bitmap = skia::ImageOperations::Resize(
130        *capture_bitmap_, skia::ImageOperations::RESIZE_GOOD, width, height);
131    thumbnail_bitmap = &scaled_bitmap;
132  }
133
134  HBITMAP native_bitmap = GetNativeBitmapFromSkBitmap(*thumbnail_bitmap);
135  ::DwmSetIconicThumbnail(hwnd_, native_bitmap, 0);
136  ::DeleteObject(native_bitmap);
137
138  *l_result = 0;
139  return true;
140}
141
142bool TaskbarWindowThumbnailerWin::OnDwmSendIconicLivePreviewBitmap(
143    LRESULT* l_result) {
144  CaptureSnapshot();
145
146  HBITMAP native_bitmap = GetNativeBitmapFromSkBitmap(*capture_bitmap_);
147  ::DwmSetIconicLivePreviewBitmap(hwnd_, native_bitmap, NULL, 0);
148  ::DeleteObject(native_bitmap);
149  *l_result = 0;
150  return true;
151}
152
153SkBitmap* TaskbarWindowThumbnailerWin::CaptureWindowImage() const {
154  std::vector<HWND> snapshot_hwnds;
155  if (delegate_)
156    snapshot_hwnds = delegate_->GetSnapshotWindowHandles();
157  if (snapshot_hwnds.empty())
158    snapshot_hwnds.push_back(hwnd_);
159
160  int enclosing_x = 0;
161  int enclosing_y = 0;
162  int enclosing_right = 0;
163  int enclosing_bottom = 0;
164  for (std::vector<HWND>::const_iterator iter = snapshot_hwnds.begin();
165       iter != snapshot_hwnds.end(); ++iter) {
166    RECT bounds;
167    if (!::GetWindowRect(*iter, &bounds))
168      continue;
169    if (iter == snapshot_hwnds.begin()) {
170      enclosing_x = bounds.left;
171      enclosing_y = bounds.top;
172      enclosing_right = bounds.right;
173      enclosing_bottom = bounds.bottom;
174    } else {
175      if (bounds.left < enclosing_x)
176        enclosing_x = bounds.left;
177      if (bounds.top < enclosing_y)
178        enclosing_y = bounds.top;
179      if (bounds.right > enclosing_right)
180        enclosing_right = bounds.right;
181      if (bounds.bottom > enclosing_bottom)
182        enclosing_bottom = bounds.bottom;
183    }
184  }
185
186  int width = enclosing_right - enclosing_x;
187  int height = enclosing_bottom - enclosing_y;
188  if (!width || !height)
189    return NULL;
190
191  gfx::Canvas canvas(gfx::Size(width, height), ui::SCALE_FACTOR_100P, false);
192  {
193    skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
194    HDC target_dc = scoped_platform_paint.GetPlatformSurface();
195    for (std::vector<HWND>::const_iterator iter = snapshot_hwnds.begin();
196         iter != snapshot_hwnds.end(); ++iter) {
197      HWND current_hwnd = *iter;
198      RECT current_bounds;
199      if (!::GetWindowRect(current_hwnd, &current_bounds))
200        continue;
201      base::win::ScopedGetDC source_dc(current_hwnd);
202      ::BitBlt(target_dc,
203               current_bounds.left - enclosing_x,
204               current_bounds.top - enclosing_y,
205               current_bounds.right - current_bounds.left,
206               current_bounds.bottom - current_bounds.top,
207               source_dc,
208               0,
209               0,
210               SRCCOPY);
211      ::ReleaseDC(current_hwnd, source_dc);
212    }
213  }
214  return new SkBitmap(canvas.ExtractImageRep().sk_bitmap());
215}
216