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 15 namespace { 16 17 HBITMAP 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 38 void 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 53 TaskbarWindowThumbnailerWin::TaskbarWindowThumbnailerWin( 54 HWND hwnd, TaskbarWindowThumbnailerDelegateWin* delegate) 55 : hwnd_(hwnd), 56 delegate_(delegate) { 57 ui::HWNDSubclass::AddFilterToTarget(hwnd_, this); 58 } 59 60 TaskbarWindowThumbnailerWin::~TaskbarWindowThumbnailerWin() { 61 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); 62 } 63 64 void TaskbarWindowThumbnailerWin::Start() { 65 EnableCustomThumbnail(hwnd_, true); 66 } 67 68 void TaskbarWindowThumbnailerWin::Stop() { 69 capture_bitmap_.reset(); 70 EnableCustomThumbnail(hwnd_, false); 71 } 72 73 void TaskbarWindowThumbnailerWin::CaptureSnapshot() { 74 if (!capture_bitmap_) 75 capture_bitmap_.reset(CaptureWindowImage()); 76 } 77 78 void TaskbarWindowThumbnailerWin::InvalidateSnapshot() { 79 capture_bitmap_.reset(); 80 81 // The snapshot feeded to the system could be cached. Invalidate it. 82 ::DwmInvalidateIconicBitmaps(hwnd_); 83 } 84 85 void 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 97 bool 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 114 bool 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 142 bool 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 153 SkBitmap* 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, ¤t_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