172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/tabs/native_view_photobooth_win.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h" 972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h" 1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/point.h" 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/gfx/rect.h" 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "views/widget/widget.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic BOOL CALLBACK MonitorEnumProc(HMONITOR monitor, HDC monitor_dc, 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RECT* monitor_rect, LPARAM data) { 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point* point = reinterpret_cast<gfx::Point*>(data); 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (monitor_rect->right > point->x() && monitor_rect->bottom > point->y()) { 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch point->set_x(monitor_rect->right); 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch point->set_y(monitor_rect->bottom); 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Point GetCaptureWindowPosition() { 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Since the capture window must be visible to be painted, it must be opened 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // off screen to avoid flashing. But if it is opened completely off-screen 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (e.g. at 0xFFFFx0xFFFF) then on Windows Vista it will not paint even if it 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // _is_ visible. So we need to find the right/bottommost monitor, and 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // position it so that 1x1 pixel is on-screen on that monitor which is enough 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to convince Vista to paint it. Don't ask why this is so - this appears to 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be a regression over XP. 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point point(0, 0); 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EnumDisplayMonitors(NULL, NULL, &MonitorEnumProc, 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch reinterpret_cast<LPARAM>(&point)); 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return gfx::Point(point.x() - 1, point.y() - 1); 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NativeViewPhotoboothWin, public: 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNativeViewPhotobooth* NativeViewPhotobooth::Create( 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::NativeView initial_view) { 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return new NativeViewPhotoboothWin(initial_view); 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNativeViewPhotoboothWin::NativeViewPhotoboothWin(HWND initial_hwnd) 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : capture_window_(NULL), 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_hwnd_(initial_hwnd) { 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(IsWindow(current_hwnd_)); 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CreateCaptureWindow(initial_hwnd); 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNativeViewPhotoboothWin::~NativeViewPhotoboothWin() { 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Detach the attached HWND. The creator of the photo-booth is responsible 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for destroying it. 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Replace(NULL); 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch capture_window_->Close(); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NativeViewPhotoboothWin::Replace(HWND new_hwnd) { 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (IsWindow(current_hwnd_) && 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetParent(current_hwnd_) == capture_window_->GetNativeView()) { 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We need to hide the window too, so it doesn't show up in the TaskBar or 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be parented to the desktop. 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ShowWindow(current_hwnd_, SW_HIDE); 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetParent(current_hwnd_, NULL); 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_hwnd_ = new_hwnd; 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (IsWindow(new_hwnd)) { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Insert the TabContents into the capture window. 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetParent(current_hwnd_, capture_window_->GetNativeView()); 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Show the window (it may not be visible). This is the only safe way of 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // doing this. ShowWindow does not work. 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetWindowPos(current_hwnd_, NULL, 0, 0, 0, 0, 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOCOPYBITS | 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOZORDER | 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SWP_SHOWWINDOW | SWP_NOSIZE); 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NativeViewPhotoboothWin::PaintScreenshotIntoCanvas( 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Canvas* canvas, 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const gfx::Rect& target_bounds) { 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Our contained window may have been re-parented. Make sure it belongs to 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // us until someone calls Replace(NULL). 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (IsWindow(current_hwnd_) && 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetParent(current_hwnd_) != capture_window_->GetNativeView()) { 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Replace(current_hwnd_); 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We compel the contained HWND to paint now, synchronously. We do this to 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // populate the device context with valid and current data. 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RedrawWindow(current_hwnd_, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Transfer the contents of the layered capture window to the screen-shot 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // canvas' DIB. 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HDC target_dc = canvas->BeginPlatformPaint(); 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HDC source_dc = GetDC(current_hwnd_); 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RECT window_rect = {0}; 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetWindowRect(current_hwnd_, &window_rect); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BitBlt(target_dc, target_bounds.x(), target_bounds.y(), 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch target_bounds.width(), target_bounds.height(), source_dc, 0, 0, 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SRCCOPY); 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Windows screws up the alpha channel on all text it draws, and so we need 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to call makeOpaque _after_ the blit to correct for this. 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch canvas->AsCanvasSkia()->getTopPlatformDevice().makeOpaque( 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch target_bounds.x(), target_bounds.y(), target_bounds.width(), 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch target_bounds.height()); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReleaseDC(current_hwnd_, source_dc); 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch canvas->EndPlatformPaint(); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/////////////////////////////////////////////////////////////////////////////// 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NativeViewPhotoboothWin, private: 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NativeViewPhotoboothWin::CreateCaptureWindow(HWND initial_hwnd) { 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Snapshotting a HWND is tricky - if the HWND is clipped (e.g. positioned 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // partially off-screen) then just blitting from the HWND' DC to the capture 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // bitmap would be incorrect, since the capture bitmap would show only the 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // visible area of the HWND. 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The approach turns out to be to create a second layered window in 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // hyperspace the to act as a "photo booth." The window is created with the 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // size of the unclipped HWND, and we attach the HWND as a child, refresh the 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HWND' by calling |Paint| on it, and then blitting from the HWND's DC to 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the capture bitmap. This results in the entire unclipped HWND display 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // bitmap being captured. 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The capture window must be layered so that Windows generates a backing 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // store for it, so that blitting from a child window's DC produces data. If 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the window is not layered, because it is off-screen Windows does not 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // retain its contents and blitting results in blank data. The capture window 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // is a "basic" (1 level of alpha) layered window because that is the mode 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // that supports having child windows (variable alpha layered windows do not 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // support child HWNDs). 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This function sets up the off-screen capture window, and attaches the 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // associated HWND to it. Note that the details are important here, see below 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // for further comments. 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen RECT contents_rect; 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetClientRect(initial_hwnd, &contents_rect); 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Point window_position = GetCaptureWindowPosition(); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gfx::Rect capture_bounds(window_position.x(), window_position.y(), 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen contents_rect.right - contents_rect.left, 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen contents_rect.bottom - contents_rect.top); 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen params.transparent = true; 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen capture_window_ = views::Widget::CreateWidget(params); 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the capture window isn't visible, blitting from the TabContents' 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HWND's DC to the capture bitmap produces blankness. 159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen capture_window_->Init(NULL, capture_bounds); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch capture_window_->Show(); 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetLayeredWindowAttributes( 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch capture_window_->GetNativeView(), RGB(0xFF, 0xFF, 0xFF), 0xFF, LWA_ALPHA); 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Replace(initial_hwnd); 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 166