| // 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_.reset(); |
| } |
| |
| 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_ = sk_sp<SkCanvas>(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); |
| |
| skia::ScopedPlatformPaint spp(contents_.get()); |
| HDC dib_dc = spp.GetPlatformSurface(); |
| ::UpdateLayeredWindow(hwnd_, NULL, &position, &size, dib_dc, &zero, |
| RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); |
| } 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_.reset(); |
| } |
| |
| } // namespace content |