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