Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package


// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/views/tabs/native_view_photobooth_win.h"

#include "chrome/browser/tab_contents/tab_contents.h"
#include "gfx/canvas.h"
#include "gfx/point.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/widget/widget_win.h"

namespace {

static BOOL CALLBACK MonitorEnumProc(HMONITOR monitor, HDC monitor_dc,
                                     RECT* monitor_rect, LPARAM data) {
  gfx::Point* point = reinterpret_cast<gfx::Point*>(data);
  if (monitor_rect->right > point->x() && monitor_rect->bottom > point->y()) {
  return TRUE;

gfx::Point GetCaptureWindowPosition() {
  // Since the capture window must be visible to be painted, it must be opened
  // off screen to avoid flashing. But if it is opened completely off-screen
  // (e.g. at 0xFFFFx0xFFFF) then on Windows Vista it will not paint even if it
  // _is_ visible. So we need to find the right/bottommost monitor, and
  // position it so that 1x1 pixel is on-screen on that monitor which is enough
  // to convince Vista to paint it. Don't ask why this is so - this appears to
  // be a regression over XP.
  gfx::Point point(0, 0);
  EnumDisplayMonitors(NULL, NULL, &MonitorEnumProc,
  return gfx::Point(point.x() - 1, point.y() - 1);


// NativeViewPhotoboothWin, public:

// static
NativeViewPhotobooth* NativeViewPhotobooth::Create(
    gfx::NativeView initial_view) {
  return new NativeViewPhotoboothWin(initial_view);

NativeViewPhotoboothWin::NativeViewPhotoboothWin(HWND initial_hwnd)
    : capture_window_(NULL),
      current_hwnd_(initial_hwnd) {

NativeViewPhotoboothWin::~NativeViewPhotoboothWin() {
  // Detach the attached HWND. The creator of the photo-booth is responsible
  // for destroying it.

void NativeViewPhotoboothWin::Replace(HWND new_hwnd) {
  if (IsWindow(current_hwnd_) &&
      GetParent(current_hwnd_) == capture_window_->GetNativeView()) {
    // We need to hide the window too, so it doesn't show up in the TaskBar or
    // be parented to the desktop.
    ShowWindow(current_hwnd_, SW_HIDE);
    SetParent(current_hwnd_, NULL);
  current_hwnd_ = new_hwnd;

  if (IsWindow(new_hwnd)) {
    // Insert the TabContents into the capture window.
    SetParent(current_hwnd_, capture_window_->GetNativeView());

    // Show the window (it may not be visible). This is the only safe way of
    // doing this. ShowWindow does not work.
    SetWindowPos(current_hwnd_, NULL, 0, 0, 0, 0,
                     SWP_SHOWWINDOW | SWP_NOSIZE);

void NativeViewPhotoboothWin::PaintScreenshotIntoCanvas(
    gfx::Canvas* canvas,
    const gfx::Rect& target_bounds) {
  // Our contained window may have been re-parented. Make sure it belongs to
  // us until someone calls Replace(NULL).
  if (IsWindow(current_hwnd_) &&
      GetParent(current_hwnd_) != capture_window_->GetNativeView()) {

  // We compel the contained HWND to paint now, synchronously. We do this to
  // populate the device context with valid and current data.
  RedrawWindow(current_hwnd_, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);

  // Transfer the contents of the layered capture window to the screen-shot
  // canvas' DIB.
  HDC target_dc = canvas->beginPlatformPaint();
  HDC source_dc = GetDC(current_hwnd_);
  RECT window_rect = {0};
  GetWindowRect(current_hwnd_, &window_rect);
  BitBlt(target_dc, target_bounds.x(), target_bounds.y(),
         target_bounds.width(), target_bounds.height(), source_dc, 0, 0,
  // Windows screws up the alpha channel on all text it draws, and so we need
  // to call makeOpaque _after_ the blit to correct for this.
  ReleaseDC(current_hwnd_, source_dc);

// NativeViewPhotoboothWin, private:

void NativeViewPhotoboothWin::CreateCaptureWindow(HWND initial_hwnd) {
  // Snapshotting a HWND is tricky - if the HWND is clipped (e.g. positioned
  // partially off-screen) then just blitting from the HWND' DC to the capture
  // bitmap would be incorrect, since the capture bitmap would show only the
  // visible area of the HWND.
  // The approach turns out to be to create a second layered window in
  // hyperspace the to act as a "photo booth." The window is created with the
  // size of the unclipped HWND, and we attach the HWND as a child, refresh the
  // HWND' by calling |Paint| on it, and then blitting from the HWND's DC to
  // the capture bitmap. This results in the entire unclipped HWND display
  // bitmap being captured.
  // The capture window must be layered so that Windows generates a backing
  // store for it, so that blitting from a child window's DC produces data. If
  // the window is not layered, because it is off-screen Windows does not
  // retain its contents and blitting results in blank data. The capture window
  // is a "basic" (1 level of alpha) layered window because that is the mode
  // that supports having child windows (variable alpha layered windows do not
  // support child HWNDs).
  // This function sets up the off-screen capture window, and attaches the
  // associated HWND to it. Note that the details are important here, see below
  // for further comments.
  CRect contents_rect;
  GetClientRect(initial_hwnd, &contents_rect);
  gfx::Point window_position = GetCaptureWindowPosition();
  gfx::Rect capture_bounds(window_position.x(), window_position.y(),
                           contents_rect.Width(), contents_rect.Height());
  capture_window_ = new views::WidgetWin;
  // WS_EX_TOOLWINDOW ensures the capture window doesn't produce a Taskbar
  // button.
  capture_window_->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW);
  capture_window_->Init(NULL, capture_bounds);
  // If the capture window isn't visible, blitting from the TabContents'
  // HWND's DC to the capture bitmap produces blankness.
      capture_window_->GetNativeView(), RGB(0xFF, 0xFF, 0xFF), 0xFF, LWA_ALPHA);


Generated by  Doxygen 1.6.0   Back to index