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