blob: e6946bbb33c9bded7c52fe0da303b7bdfc006d63 [file] [log] [blame]
// 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), ui::SCALE_FACTOR_100P, 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());
}