| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |