blob: 8e761dd0c32929961b19c75bef3467f62e4fe25f [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 "content/browser/renderer_host/backing_store_win.h"
#include "base/command_line.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/common/content_switches.h"
#include "skia/ext/platform_canvas.h"
#include "ui/gfx/gdi_util.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/win/dpi.h"
#include "ui/surface/transport_dib.h"
namespace content {
namespace {
// Creates a dib conforming to the height/width/section parameters passed in.
HANDLE CreateDIB(HDC dc, int width, int height, int color_depth) {
BITMAPV5HEADER hdr = {0};
ZeroMemory(&hdr, sizeof(BITMAPV5HEADER));
// These values are shared with gfx::PlatformDevice
hdr.bV5Size = sizeof(BITMAPINFOHEADER);
hdr.bV5Width = width;
hdr.bV5Height = -height; // minus means top-down bitmap
hdr.bV5Planes = 1;
hdr.bV5BitCount = color_depth;
hdr.bV5Compression = BI_RGB; // no compression
hdr.bV5SizeImage = 0;
hdr.bV5XPelsPerMeter = 1;
hdr.bV5YPelsPerMeter = 1;
hdr.bV5ClrUsed = 0;
hdr.bV5ClrImportant = 0;
if (BackingStoreWin::ColorManagementEnabled()) {
hdr.bV5CSType = LCS_sRGB;
hdr.bV5Intent = LCS_GM_IMAGES;
}
void* data = NULL;
HANDLE dib = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&hdr),
0, &data, NULL, 0);
DCHECK(data);
return dib;
}
} // namespace
BackingStoreWin::BackingStoreWin(RenderWidgetHost* widget,
const gfx::Size& size)
: BackingStore(widget, size),
backing_store_dib_(NULL),
original_bitmap_(NULL) {
HDC screen_dc = ::GetDC(NULL);
color_depth_ = ::GetDeviceCaps(screen_dc, BITSPIXEL);
// Color depths less than 16 bpp require a palette to be specified. Instead,
// we specify the desired color depth as 16 which lets the OS to come up
// with an approximation.
if (color_depth_ < 16)
color_depth_ = 16;
hdc_ = CreateCompatibleDC(screen_dc);
ReleaseDC(NULL, screen_dc);
}
BackingStoreWin::~BackingStoreWin() {
DCHECK(hdc_);
if (original_bitmap_) {
SelectObject(hdc_, original_bitmap_);
}
if (backing_store_dib_) {
DeleteObject(backing_store_dib_);
backing_store_dib_ = NULL;
}
DeleteDC(hdc_);
}
// static
bool BackingStoreWin::ColorManagementEnabled() {
static bool enabled = false;
static bool checked = false;
if (!checked) {
checked = true;
const CommandLine& command = *CommandLine::ForCurrentProcess();
enabled = command.HasSwitch(switches::kEnableMonitorProfile);
}
return enabled;
}
size_t BackingStoreWin::MemorySize() {
gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(size(),
gfx::win::GetDeviceScaleFactor()));
return size_in_pixels.GetArea() * (color_depth_ / 8);
}
void BackingStoreWin::PaintToBackingStore(
RenderProcessHost* process,
TransportDIB::Id bitmap,
const gfx::Rect& bitmap_rect,
const std::vector<gfx::Rect>& copy_rects,
float scale_factor,
const base::Closure& completion_callback,
bool* scheduled_completion_callback) {
TRACE_EVENT0("content", "BackingStoreWin::PaintToBackingStore");
*scheduled_completion_callback = false;
gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(
size(), scale_factor));
if (!backing_store_dib_) {
backing_store_dib_ = CreateDIB(hdc_,
size_in_pixels.width(),
size_in_pixels.height(),
color_depth_);
if (!backing_store_dib_) {
NOTREACHED();
return;
}
original_bitmap_ = SelectObject(hdc_, backing_store_dib_);
}
TransportDIB* dib = process->GetTransportDIB(bitmap);
if (!dib)
return;
gfx::Rect pixel_bitmap_rect = gfx::ToEnclosingRect(
gfx::ScaleRect(bitmap_rect, scale_factor));
BITMAPINFO bitmap_info;
memset(&bitmap_info, 0, sizeof(bitmap_info));
gfx::CreateBitmapHeader(pixel_bitmap_rect.width(),
pixel_bitmap_rect.height(),
&bitmap_info.bmiHeader);
// Account for a bitmap_rect that exceeds the bounds of our view.
gfx::Rect view_rect(size());
for (size_t i = 0; i < copy_rects.size(); i++) {
gfx::Rect paint_rect = gfx::IntersectRects(view_rect, copy_rects[i]);
gfx::Rect pixel_copy_rect = gfx::ToEnclosingRect(
gfx::ScaleRect(paint_rect, scale_factor));
gfx::Rect target_rect = pixel_copy_rect;
gfx::StretchDIBits(hdc_,
target_rect.x(),
target_rect.y(),
target_rect.width(),
target_rect.height(),
pixel_copy_rect.x() - pixel_bitmap_rect.x(),
pixel_copy_rect.y() - pixel_bitmap_rect.y(),
pixel_copy_rect.width(),
pixel_copy_rect.height(),
dib->memory(),
&bitmap_info);
}
}
bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect,
skia::PlatformBitmap* output) {
TRACE_EVENT0("content", "BackingStoreWin::CopyFromBackingStore");
// TODO(kevers): Make sure this works with HiDPI backing stores.
if (!output->Allocate(rect.width(), rect.height(), true))
return false;
HDC temp_dc = output->GetSurface();
BitBlt(temp_dc, 0, 0, rect.width(), rect.height(),
hdc(), rect.x(), rect.y(), SRCCOPY);
return true;
}
void BackingStoreWin::ScrollBackingStore(const gfx::Vector2d& delta,
const gfx::Rect& clip_rect,
const gfx::Size& view_size) {
TRACE_EVENT0("content", "BackingStoreWin::ScrollBackingStore");
// TODO(darin): this doesn't work if delta x() and y() are both non-zero!
DCHECK(delta.x() == 0 || delta.y() == 0);
float scale = gfx::win::GetDeviceScaleFactor();
gfx::Rect screen_rect = gfx::ToEnclosingRect(
gfx::ScaleRect(clip_rect, scale));
int dx = static_cast<int>(delta.x() * scale);
int dy = static_cast<int>(delta.y() * scale);
RECT damaged_rect, r = screen_rect.ToRECT();
ScrollDC(hdc_, dx, dy, NULL, &r, NULL, &damaged_rect);
}
} // namespace content