Home | History | Annotate | Download | only in panels
      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, &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