Home | History | Annotate | Download | only in desktop_capture
      1 /*
      2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
     12 
     13 #include <string.h>
     14 
     15 #include "webrtc/modules/desktop_capture/desktop_capturer.h"
     16 #include "webrtc/modules/desktop_capture/desktop_frame.h"
     17 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
     18 
     19 namespace webrtc {
     20 
     21 namespace {
     22 
     23 // Helper function that blends one image into another. Source image must be
     24 // pre-multiplied with the alpha channel. Destination is assumed to be opaque.
     25 void AlphaBlend(uint8_t* dest, int dest_stride,
     26                 const uint8_t* src, int src_stride,
     27                 const DesktopSize& size) {
     28   for (int y = 0; y < size.height(); ++y) {
     29     for (int x = 0; x < size.width(); ++x) {
     30       uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3];
     31       if (base_alpha == 255) {
     32         continue;
     33       } else if (base_alpha == 0) {
     34         memcpy(dest + x * DesktopFrame::kBytesPerPixel,
     35                src + x * DesktopFrame::kBytesPerPixel,
     36                DesktopFrame::kBytesPerPixel);
     37       } else {
     38         dest[x * DesktopFrame::kBytesPerPixel] =
     39             dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 +
     40             src[x * DesktopFrame::kBytesPerPixel];
     41         dest[x * DesktopFrame::kBytesPerPixel + 1] =
     42             dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 +
     43             src[x * DesktopFrame::kBytesPerPixel + 1];
     44         dest[x * DesktopFrame::kBytesPerPixel + 2] =
     45             dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 +
     46             src[x * DesktopFrame::kBytesPerPixel + 2];
     47       }
     48     }
     49     src += src_stride;
     50     dest += dest_stride;
     51   }
     52 }
     53 
     54 // DesktopFrame wrapper that draws mouse on a frame and restores original
     55 // content before releasing the underlying frame.
     56 class DesktopFrameWithCursor : public DesktopFrame {
     57  public:
     58   // Takes ownership of |frame|.
     59   DesktopFrameWithCursor(DesktopFrame* frame,
     60                          const MouseCursor& cursor,
     61                          const DesktopVector& position);
     62   virtual ~DesktopFrameWithCursor();
     63 
     64  private:
     65   rtc::scoped_ptr<DesktopFrame> original_frame_;
     66 
     67   DesktopVector restore_position_;
     68   rtc::scoped_ptr<DesktopFrame> restore_frame_;
     69 
     70   RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
     71 };
     72 
     73 DesktopFrameWithCursor::DesktopFrameWithCursor(DesktopFrame* frame,
     74                                                const MouseCursor& cursor,
     75                                                const DesktopVector& position)
     76     : DesktopFrame(frame->size(), frame->stride(),
     77                    frame->data(), frame->shared_memory()),
     78       original_frame_(frame) {
     79   set_dpi(frame->dpi());
     80   set_capture_time_ms(frame->capture_time_ms());
     81   mutable_updated_region()->Swap(frame->mutable_updated_region());
     82 
     83   DesktopVector image_pos = position.subtract(cursor.hotspot());
     84   DesktopRect target_rect = DesktopRect::MakeSize(cursor.image()->size());
     85   target_rect.Translate(image_pos);
     86   DesktopVector target_origin = target_rect.top_left();
     87   target_rect.IntersectWith(DesktopRect::MakeSize(size()));
     88 
     89   if (target_rect.is_empty())
     90     return;
     91 
     92   // Copy original screen content under cursor to |restore_frame_|.
     93   restore_position_ = target_rect.top_left();
     94   restore_frame_.reset(new BasicDesktopFrame(target_rect.size()));
     95   restore_frame_->CopyPixelsFrom(*this, target_rect.top_left(),
     96                                  DesktopRect::MakeSize(restore_frame_->size()));
     97 
     98   // Blit the cursor.
     99   uint8_t* target_rect_data = reinterpret_cast<uint8_t*>(data()) +
    100                               target_rect.top() * stride() +
    101                               target_rect.left() * DesktopFrame::kBytesPerPixel;
    102   DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
    103   AlphaBlend(target_rect_data, stride(),
    104              cursor.image()->data() +
    105                  origin_shift.y() * cursor.image()->stride() +
    106                  origin_shift.x() * DesktopFrame::kBytesPerPixel,
    107              cursor.image()->stride(),
    108              target_rect.size());
    109 }
    110 
    111 DesktopFrameWithCursor::~DesktopFrameWithCursor() {
    112   // Restore original content of the frame.
    113   if (restore_frame_.get()) {
    114     DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
    115     target_rect.Translate(restore_position_);
    116     CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
    117                    target_rect);
    118   }
    119 }
    120 
    121 }  // namespace
    122 
    123 DesktopAndCursorComposer::DesktopAndCursorComposer(
    124     DesktopCapturer* desktop_capturer,
    125     MouseCursorMonitor* mouse_monitor)
    126     : desktop_capturer_(desktop_capturer),
    127       mouse_monitor_(mouse_monitor) {
    128 }
    129 
    130 DesktopAndCursorComposer::~DesktopAndCursorComposer() {}
    131 
    132 void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
    133   callback_ = callback;
    134   if (mouse_monitor_.get())
    135     mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
    136   desktop_capturer_->Start(this);
    137 }
    138 
    139 void DesktopAndCursorComposer::Capture(const DesktopRegion& region) {
    140   if (mouse_monitor_.get())
    141     mouse_monitor_->Capture();
    142   desktop_capturer_->Capture(region);
    143 }
    144 
    145 void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
    146   desktop_capturer_->SetExcludedWindow(window);
    147 }
    148 
    149 SharedMemory* DesktopAndCursorComposer::CreateSharedMemory(size_t size) {
    150   return callback_->CreateSharedMemory(size);
    151 }
    152 
    153 void DesktopAndCursorComposer::OnCaptureCompleted(DesktopFrame* frame) {
    154   if (frame && cursor_.get() && cursor_state_ == MouseCursorMonitor::INSIDE) {
    155     DesktopFrameWithCursor* frame_with_cursor =
    156         new DesktopFrameWithCursor(frame, *cursor_, cursor_position_);
    157     frame = frame_with_cursor;
    158   }
    159 
    160   callback_->OnCaptureCompleted(frame);
    161 }
    162 
    163 void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
    164   cursor_.reset(cursor);
    165 }
    166 
    167 void DesktopAndCursorComposer::OnMouseCursorPosition(
    168     MouseCursorMonitor::CursorState state,
    169     const DesktopVector& position) {
    170   cursor_state_ = state;
    171   cursor_position_ = position;
    172 }
    173 
    174 }  // namespace webrtc
    175