root/chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetNativeBitmapFromSkBitmap
  2. EnableCustomThumbnail
  3. delegate_
  4. Start
  5. Stop
  6. CaptureSnapshot
  7. InvalidateSnapshot
  8. ReplaceWindow
  9. FilterMessage
  10. OnDwmSendIconicThumbnail
  11. OnDwmSendIconicLivePreviewBitmap
  12. CaptureWindowImage

// Copyright (c) 2012 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/panels/taskbar_window_thumbnailer_win.h"

#include <dwmapi.h>

#include "base/logging.h"
#include "base/win/scoped_hdc.h"
#include "skia/ext/image_operations.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/gdi_util.h"

namespace {

HBITMAP GetNativeBitmapFromSkBitmap(const SkBitmap& bitmap) {
  int width = bitmap.width();
  int height = bitmap.height();

  BITMAPV4HEADER native_bitmap_header;
  gfx::CreateBitmapV4Header(width, height, &native_bitmap_header);

  HDC dc = ::GetDC(NULL);
  void* bits;
  HBITMAP native_bitmap = ::CreateDIBSection(dc,
      reinterpret_cast<BITMAPINFO*>(&native_bitmap_header),
      DIB_RGB_COLORS,
      &bits,
      NULL,
      0);
  DCHECK(native_bitmap);
  ::ReleaseDC(NULL, dc);
  bitmap.copyPixelsTo(bits, width * height * 4, width * 4);
  return native_bitmap;
}

void EnableCustomThumbnail(HWND hwnd, bool enable) {
  BOOL enable_value = enable;
  ::DwmSetWindowAttribute(hwnd,
                          DWMWA_FORCE_ICONIC_REPRESENTATION,
                          &enable_value,
                          sizeof(enable_value));
  ::DwmSetWindowAttribute(hwnd,
                          DWMWA_HAS_ICONIC_BITMAP,
                          &enable_value,
                          sizeof(enable_value));
}

}  // namespace


TaskbarWindowThumbnailerWin::TaskbarWindowThumbnailerWin(
    HWND hwnd, TaskbarWindowThumbnailerDelegateWin* delegate)
    : hwnd_(hwnd),
      delegate_(delegate) {
  ui::HWNDSubclass::AddFilterToTarget(hwnd_, this);
}

TaskbarWindowThumbnailerWin::~TaskbarWindowThumbnailerWin() {
  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
}

void TaskbarWindowThumbnailerWin::Start() {
  EnableCustomThumbnail(hwnd_, true);
}

void TaskbarWindowThumbnailerWin::Stop() {
  capture_bitmap_.reset();
  EnableCustomThumbnail(hwnd_, false);
}

void TaskbarWindowThumbnailerWin::CaptureSnapshot() {
  if (!capture_bitmap_)
    capture_bitmap_.reset(CaptureWindowImage());
}

void TaskbarWindowThumbnailerWin::InvalidateSnapshot() {
  capture_bitmap_.reset();

  // The snapshot feeded to the system could be cached. Invalidate it.
  ::DwmInvalidateIconicBitmaps(hwnd_);
}

void TaskbarWindowThumbnailerWin::ReplaceWindow(HWND new_hwnd) {
  // Stop serving the custom thumbnail for the old window.
  EnableCustomThumbnail(hwnd_, false);
  ui::HWNDSubclass::RemoveFilterFromAllTargets(this);

  hwnd_ = new_hwnd;

  // Start serving the custom thumbnail to the new window.
  ui::HWNDSubclass::AddFilterToTarget(hwnd_, this);
  EnableCustomThumbnail(hwnd_, true);
}

bool TaskbarWindowThumbnailerWin::FilterMessage(HWND hwnd,
                                                UINT message,
                                                WPARAM w_param,
                                                LPARAM l_param,
                                                LRESULT* l_result) {
  DCHECK_EQ(hwnd_, hwnd);
  switch (message) {
    case WM_DWMSENDICONICTHUMBNAIL:
      return OnDwmSendIconicThumbnail(HIWORD(l_param),
                                      LOWORD(l_param),
                                      l_result);
    case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
      return OnDwmSendIconicLivePreviewBitmap(l_result);
  }
  return false;
}

bool TaskbarWindowThumbnailerWin::OnDwmSendIconicThumbnail(
    int width, int height, LRESULT* l_result) {
  CaptureSnapshot();

  SkBitmap* thumbnail_bitmap = capture_bitmap_.get();

  // Scale the image if needed.
  SkBitmap scaled_bitmap;
  if (capture_bitmap_->width() != width ||
      capture_bitmap_->height() != height) {
    double x_scale = static_cast<double>(width) / capture_bitmap_->width();
    double y_scale = static_cast<double>(height) / capture_bitmap_->height();
    double scale = std::min(x_scale, y_scale);
    width = capture_bitmap_->width() * scale;
    height = capture_bitmap_->height() * scale;
    scaled_bitmap = skia::ImageOperations::Resize(
        *capture_bitmap_, skia::ImageOperations::RESIZE_GOOD, width, height);
    thumbnail_bitmap = &scaled_bitmap;
  }

  HBITMAP native_bitmap = GetNativeBitmapFromSkBitmap(*thumbnail_bitmap);
  ::DwmSetIconicThumbnail(hwnd_, native_bitmap, 0);
  ::DeleteObject(native_bitmap);

  *l_result = 0;
  return true;
}

bool TaskbarWindowThumbnailerWin::OnDwmSendIconicLivePreviewBitmap(
    LRESULT* l_result) {
  CaptureSnapshot();

  HBITMAP native_bitmap = GetNativeBitmapFromSkBitmap(*capture_bitmap_);
  ::DwmSetIconicLivePreviewBitmap(hwnd_, native_bitmap, NULL, 0);
  ::DeleteObject(native_bitmap);
  *l_result = 0;
  return true;
}

SkBitmap* TaskbarWindowThumbnailerWin::CaptureWindowImage() const {
  std::vector<HWND> snapshot_hwnds;
  if (delegate_)
    snapshot_hwnds = delegate_->GetSnapshotWindowHandles();
  if (snapshot_hwnds.empty())
    snapshot_hwnds.push_back(hwnd_);

  int enclosing_x = 0;
  int enclosing_y = 0;
  int enclosing_right = 0;
  int enclosing_bottom = 0;
  for (std::vector<HWND>::const_iterator iter = snapshot_hwnds.begin();
       iter != snapshot_hwnds.end(); ++iter) {
    RECT bounds;
    if (!::GetWindowRect(*iter, &bounds))
      continue;
    if (iter == snapshot_hwnds.begin()) {
      enclosing_x = bounds.left;
      enclosing_y = bounds.top;
      enclosing_right = bounds.right;
      enclosing_bottom = bounds.bottom;
    } else {
      if (bounds.left < enclosing_x)
        enclosing_x = bounds.left;
      if (bounds.top < enclosing_y)
        enclosing_y = bounds.top;
      if (bounds.right > enclosing_right)
        enclosing_right = bounds.right;
      if (bounds.bottom > enclosing_bottom)
        enclosing_bottom = bounds.bottom;
    }
  }

  int width = enclosing_right - enclosing_x;
  int height = enclosing_bottom - enclosing_y;
  if (!width || !height)
    return NULL;

  gfx::Canvas canvas(gfx::Size(width, height), 1.0f, false);
  {
    skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
    HDC target_dc = scoped_platform_paint.GetPlatformSurface();
    for (std::vector<HWND>::const_iterator iter = snapshot_hwnds.begin();
         iter != snapshot_hwnds.end(); ++iter) {
      HWND current_hwnd = *iter;
      RECT current_bounds;
      if (!::GetWindowRect(current_hwnd, &current_bounds))
        continue;
      base::win::ScopedGetDC source_dc(current_hwnd);
      ::BitBlt(target_dc,
               current_bounds.left - enclosing_x,
               current_bounds.top - enclosing_y,
               current_bounds.right - current_bounds.left,
               current_bounds.bottom - current_bounds.top,
               source_dc,
               0,
               0,
               SRCCOPY);
      ::ReleaseDC(current_hwnd, source_dc);
    }
  }
  return new SkBitmap(canvas.ExtractImageRep().sk_bitmap());
}

/* [<][>][^][v][top][bottom][index][help] */