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