// Copyright 2014 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/compositor/software_output_device_win.h" #include "base/debug/alias.h" #include "base/memory/shared_memory.h" #include "cc/resources/shared_bitmap.h" #include "content/public/browser/browser_thread.h" #include "skia/ext/platform_canvas.h" #include "ui/compositor/compositor.h" #include "ui/gfx/gdi_util.h" #include "ui/gfx/skia_util.h" namespace content { // If a window is larger than this in bytes, don't even try to create a // backing bitmap for it. static const size_t kMaxBitmapSizeBytes = 4 * (16384 * 8192); OutputDeviceBacking::OutputDeviceBacking() : created_byte_size_(0) { } OutputDeviceBacking::~OutputDeviceBacking() { DCHECK(devices_.empty()); } void OutputDeviceBacking::Resized() { size_t new_size = GetMaxByteSize(); if (new_size == created_byte_size_) return; for (SoftwareOutputDeviceWin* device : devices_) { device->ReleaseContents(); } backing_.reset(); created_byte_size_ = 0; } void OutputDeviceBacking::RegisterOutputDevice( SoftwareOutputDeviceWin* device) { devices_.push_back(device); } void OutputDeviceBacking::UnregisterOutputDevice( SoftwareOutputDeviceWin* device) { auto it = std::find(devices_.begin(), devices_.end(), device); DCHECK(it != devices_.end()); devices_.erase(it); Resized(); } base::SharedMemory* OutputDeviceBacking::GetSharedMemory( const gfx::Size& size) { if (backing_) return backing_.get(); size_t expected_byte_size = GetMaxByteSize(); size_t required_size; if (!cc::SharedBitmap::SizeInBytes(size, &required_size)) return nullptr; if (required_size > expected_byte_size) return nullptr; created_byte_size_ = expected_byte_size; backing_.reset(new base::SharedMemory); base::debug::Alias(&expected_byte_size); CHECK(backing_->CreateAnonymous(created_byte_size_)); return backing_.get(); } size_t OutputDeviceBacking::GetMaxByteSize() { // Minimum byte size is 1 because creating a 0-byte-long SharedMemory fails. size_t max_size = 1; for (const SoftwareOutputDeviceWin* device : devices_) { size_t current_size; if (!cc::SharedBitmap::SizeInBytes(device->viewport_pixel_size(), ¤t_size)) continue; if (current_size > kMaxBitmapSizeBytes) continue; max_size = std::max(max_size, current_size); } return max_size; } SoftwareOutputDeviceWin::SoftwareOutputDeviceWin(OutputDeviceBacking* backing, ui::Compositor* compositor) : hwnd_(compositor->widget()), is_hwnd_composited_(false), backing_(backing), in_paint_(false) { DCHECK_CURRENTLY_ON(BrowserThread::UI); LONG style = GetWindowLong(hwnd_, GWL_EXSTYLE); is_hwnd_composited_ = !!(style & WS_EX_COMPOSITED); // Layered windows must be completely updated every time, so they can't // share contents with other windows. if (is_hwnd_composited_) backing_ = nullptr; if (backing_) backing_->RegisterOutputDevice(this); } SoftwareOutputDeviceWin::~SoftwareOutputDeviceWin() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!in_paint_); if (backing_) backing_->UnregisterOutputDevice(this); } void SoftwareOutputDeviceWin::Resize(const gfx::Size& viewport_pixel_size, float scale_factor) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!in_paint_); scale_factor_ = scale_factor; if (viewport_pixel_size_ == viewport_pixel_size) return; viewport_pixel_size_ = viewport_pixel_size; if (backing_) backing_->Resized(); contents_.clear(); } SkCanvas* SoftwareOutputDeviceWin::BeginPaint(const gfx::Rect& damage_rect) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!in_paint_); if (!contents_) { HANDLE shared_section = NULL; bool can_create_contents = true; if (backing_) { base::SharedMemory* memory = backing_->GetSharedMemory(viewport_pixel_size_); if (memory) { shared_section = memory->handle().GetHandle(); } else { can_create_contents = false; } } if (can_create_contents) { contents_ = skia::AdoptRef(skia::CreatePlatformCanvas( viewport_pixel_size_.width(), viewport_pixel_size_.height(), true, shared_section, skia::CRASH_ON_FAILURE)); } } damage_rect_ = damage_rect; in_paint_ = true; return contents_.get(); } void SoftwareOutputDeviceWin::EndPaint() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(in_paint_); in_paint_ = false; SoftwareOutputDevice::EndPaint(); if (!contents_) return; gfx::Rect rect = damage_rect_; rect.Intersect(gfx::Rect(viewport_pixel_size_)); if (rect.IsEmpty()) return; if (is_hwnd_composited_) { RECT wr; GetWindowRect(hwnd_, &wr); SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; POINT position = {wr.left, wr.top}; POINT zero = {0, 0}; BLENDFUNCTION blend = {AC_SRC_OVER, 0x00, 0xFF, AC_SRC_ALPHA}; DWORD style = GetWindowLong(hwnd_, GWL_EXSTYLE); style &= ~WS_EX_COMPOSITED; style |= WS_EX_LAYERED; SetWindowLong(hwnd_, GWL_EXSTYLE, style); HDC dib_dc = skia::BeginPlatformPaint(contents_.get()); ::UpdateLayeredWindow(hwnd_, NULL, &position, &size, dib_dc, &zero, RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); skia::EndPlatformPaint(contents_.get()); } else { HDC hdc = ::GetDC(hwnd_); RECT src_rect = rect.ToRECT(); skia::DrawToNativeContext(contents_.get(), hdc, rect.x(), rect.y(), &src_rect); ::ReleaseDC(hwnd_, hdc); } } void SoftwareOutputDeviceWin::ReleaseContents() { DCHECK(!contents_ || contents_->unique()); DCHECK(!in_paint_); contents_.clear(); } } // namespace content