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
19namespace webrtc {
20
21namespace {
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.
25void 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.
56class 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  scoped_ptr<DesktopFrame> original_frame_;
66
67  DesktopVector restore_position_;
68  scoped_ptr<DesktopFrame> restore_frame_;
69
70  DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
71};
72
73DesktopFrameWithCursor::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
111DesktopFrameWithCursor::~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
123DesktopAndCursorComposer::DesktopAndCursorComposer(
124    DesktopCapturer* desktop_capturer,
125    MouseCursorMonitor* mouse_monitor)
126    : desktop_capturer_(desktop_capturer),
127      mouse_monitor_(mouse_monitor) {
128}
129
130DesktopAndCursorComposer::~DesktopAndCursorComposer() {}
131
132void 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
139void DesktopAndCursorComposer::Capture(const DesktopRegion& region) {
140  if (mouse_monitor_.get())
141    mouse_monitor_->Capture();
142  desktop_capturer_->Capture(region);
143}
144
145void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
146  desktop_capturer_->SetExcludedWindow(window);
147}
148
149SharedMemory* DesktopAndCursorComposer::CreateSharedMemory(size_t size) {
150  return callback_->CreateSharedMemory(size);
151}
152
153void 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
163void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
164  cursor_.reset(cursor);
165}
166
167void DesktopAndCursorComposer::OnMouseCursorPosition(
168    MouseCursorMonitor::CursorState state,
169    const DesktopVector& position) {
170  cursor_state_ = state;
171  cursor_position_ = position;
172}
173
174}  // namespace webrtc
175