• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "modules/desktop_capture/desktop_and_cursor_composer.h"
12 
13 #include <stdint.h>
14 #include <string.h>
15 
16 #include <memory>
17 #include <utility>
18 
19 #include "modules/desktop_capture/desktop_capturer.h"
20 #include "modules/desktop_capture/desktop_frame.h"
21 #include "modules/desktop_capture/mouse_cursor.h"
22 #include "modules/desktop_capture/mouse_cursor_monitor.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/constructor_magic.h"
25 
26 namespace webrtc {
27 
28 namespace {
29 
30 // Helper function that blends one image into another. Source image must be
31 // pre-multiplied with the alpha channel. Destination is assumed to be opaque.
AlphaBlend(uint8_t * dest,int dest_stride,const uint8_t * src,int src_stride,const DesktopSize & size)32 void AlphaBlend(uint8_t* dest,
33                 int dest_stride,
34                 const uint8_t* src,
35                 int src_stride,
36                 const DesktopSize& size) {
37   for (int y = 0; y < size.height(); ++y) {
38     for (int x = 0; x < size.width(); ++x) {
39       uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3];
40       if (base_alpha == 255) {
41         continue;
42       } else if (base_alpha == 0) {
43         memcpy(dest + x * DesktopFrame::kBytesPerPixel,
44                src + x * DesktopFrame::kBytesPerPixel,
45                DesktopFrame::kBytesPerPixel);
46       } else {
47         dest[x * DesktopFrame::kBytesPerPixel] =
48             dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 +
49             src[x * DesktopFrame::kBytesPerPixel];
50         dest[x * DesktopFrame::kBytesPerPixel + 1] =
51             dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 +
52             src[x * DesktopFrame::kBytesPerPixel + 1];
53         dest[x * DesktopFrame::kBytesPerPixel + 2] =
54             dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 +
55             src[x * DesktopFrame::kBytesPerPixel + 2];
56       }
57     }
58     src += src_stride;
59     dest += dest_stride;
60   }
61 }
62 
63 // DesktopFrame wrapper that draws mouse on a frame and restores original
64 // content before releasing the underlying frame.
65 class DesktopFrameWithCursor : public DesktopFrame {
66  public:
67   // Takes ownership of |frame|.
68   DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame,
69                          const MouseCursor& cursor,
70                          const DesktopVector& position,
71                          const DesktopRect& previous_cursor_rect,
72                          bool cursor_changed);
73   ~DesktopFrameWithCursor() override;
74 
cursor_rect() const75   DesktopRect cursor_rect() const { return cursor_rect_; }
76 
77  private:
78   const std::unique_ptr<DesktopFrame> original_frame_;
79 
80   DesktopVector restore_position_;
81   std::unique_ptr<DesktopFrame> restore_frame_;
82   DesktopRect cursor_rect_;
83 
84   RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
85 };
86 
DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame,const MouseCursor & cursor,const DesktopVector & position,const DesktopRect & previous_cursor_rect,bool cursor_changed)87 DesktopFrameWithCursor::DesktopFrameWithCursor(
88     std::unique_ptr<DesktopFrame> frame,
89     const MouseCursor& cursor,
90     const DesktopVector& position,
91     const DesktopRect& previous_cursor_rect,
92     bool cursor_changed)
93     : DesktopFrame(frame->size(),
94                    frame->stride(),
95                    frame->data(),
96                    frame->shared_memory()),
97       original_frame_(std::move(frame)) {
98   MoveFrameInfoFrom(original_frame_.get());
99 
100   DesktopVector image_pos = position.subtract(cursor.hotspot());
101   cursor_rect_ = DesktopRect::MakeSize(cursor.image()->size());
102   cursor_rect_.Translate(image_pos);
103   DesktopVector cursor_origin = cursor_rect_.top_left();
104   cursor_rect_.IntersectWith(DesktopRect::MakeSize(size()));
105 
106   if (!previous_cursor_rect.equals(cursor_rect_)) {
107     mutable_updated_region()->AddRect(cursor_rect_);
108     mutable_updated_region()->AddRect(previous_cursor_rect);
109   } else if (cursor_changed) {
110     mutable_updated_region()->AddRect(cursor_rect_);
111   }
112 
113   if (cursor_rect_.is_empty())
114     return;
115 
116   // Copy original screen content under cursor to |restore_frame_|.
117   restore_position_ = cursor_rect_.top_left();
118   restore_frame_.reset(new BasicDesktopFrame(cursor_rect_.size()));
119   restore_frame_->CopyPixelsFrom(*this, cursor_rect_.top_left(),
120                                  DesktopRect::MakeSize(restore_frame_->size()));
121 
122   // Blit the cursor.
123   uint8_t* cursor_rect_data =
124       reinterpret_cast<uint8_t*>(data()) + cursor_rect_.top() * stride() +
125       cursor_rect_.left() * DesktopFrame::kBytesPerPixel;
126   DesktopVector origin_shift = cursor_rect_.top_left().subtract(cursor_origin);
127   AlphaBlend(cursor_rect_data, stride(),
128              cursor.image()->data() +
129                  origin_shift.y() * cursor.image()->stride() +
130                  origin_shift.x() * DesktopFrame::kBytesPerPixel,
131              cursor.image()->stride(), cursor_rect_.size());
132 }
133 
~DesktopFrameWithCursor()134 DesktopFrameWithCursor::~DesktopFrameWithCursor() {
135   // Restore original content of the frame.
136   if (restore_frame_) {
137     DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
138     target_rect.Translate(restore_position_);
139     CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
140                    target_rect);
141   }
142 }
143 
144 }  // namespace
145 
DesktopAndCursorComposer(std::unique_ptr<DesktopCapturer> desktop_capturer,const DesktopCaptureOptions & options)146 DesktopAndCursorComposer::DesktopAndCursorComposer(
147     std::unique_ptr<DesktopCapturer> desktop_capturer,
148     const DesktopCaptureOptions& options)
149     : DesktopAndCursorComposer(desktop_capturer.release(),
150                                MouseCursorMonitor::Create(options).release()) {}
151 
DesktopAndCursorComposer(DesktopCapturer * desktop_capturer,MouseCursorMonitor * mouse_monitor)152 DesktopAndCursorComposer::DesktopAndCursorComposer(
153     DesktopCapturer* desktop_capturer,
154     MouseCursorMonitor* mouse_monitor)
155     : desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) {
156   RTC_DCHECK(desktop_capturer_);
157 }
158 
159 DesktopAndCursorComposer::~DesktopAndCursorComposer() = default;
160 
161 std::unique_ptr<DesktopAndCursorComposer>
CreateWithoutMouseCursorMonitor(std::unique_ptr<DesktopCapturer> desktop_capturer)162 DesktopAndCursorComposer::CreateWithoutMouseCursorMonitor(
163     std::unique_ptr<DesktopCapturer> desktop_capturer) {
164   return std::unique_ptr<DesktopAndCursorComposer>(
165       new DesktopAndCursorComposer(desktop_capturer.release(), nullptr));
166 }
167 
Start(DesktopCapturer::Callback * callback)168 void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
169   callback_ = callback;
170   if (mouse_monitor_)
171     mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
172   desktop_capturer_->Start(this);
173 }
174 
SetSharedMemoryFactory(std::unique_ptr<SharedMemoryFactory> shared_memory_factory)175 void DesktopAndCursorComposer::SetSharedMemoryFactory(
176     std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
177   desktop_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
178 }
179 
CaptureFrame()180 void DesktopAndCursorComposer::CaptureFrame() {
181   if (mouse_monitor_)
182     mouse_monitor_->Capture();
183   desktop_capturer_->CaptureFrame();
184 }
185 
SetExcludedWindow(WindowId window)186 void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
187   desktop_capturer_->SetExcludedWindow(window);
188 }
189 
GetSourceList(SourceList * sources)190 bool DesktopAndCursorComposer::GetSourceList(SourceList* sources) {
191   return desktop_capturer_->GetSourceList(sources);
192 }
193 
SelectSource(SourceId id)194 bool DesktopAndCursorComposer::SelectSource(SourceId id) {
195   return desktop_capturer_->SelectSource(id);
196 }
197 
FocusOnSelectedSource()198 bool DesktopAndCursorComposer::FocusOnSelectedSource() {
199   return desktop_capturer_->FocusOnSelectedSource();
200 }
201 
IsOccluded(const DesktopVector & pos)202 bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) {
203   return desktop_capturer_->IsOccluded(pos);
204 }
205 
OnCaptureResult(DesktopCapturer::Result result,std::unique_ptr<DesktopFrame> frame)206 void DesktopAndCursorComposer::OnCaptureResult(
207     DesktopCapturer::Result result,
208     std::unique_ptr<DesktopFrame> frame) {
209   if (frame && cursor_) {
210     if (frame->rect().Contains(cursor_position_) &&
211         !desktop_capturer_->IsOccluded(cursor_position_)) {
212       DesktopVector relative_position =
213           cursor_position_.subtract(frame->top_left());
214 #if defined(WEBRTC_MAC)
215       // On OSX, the logical(DIP) and physical coordinates are used mixingly.
216       // For example, the captured cursor has its size in physical pixels(2x)
217       // and location in logical(DIP) pixels on Retina monitor. This will cause
218       // problem when the desktop is mixed with Retina and non-Retina monitors.
219       // So we use DIP pixel for all location info and compensate with the scale
220       // factor of current frame to the |relative_position|.
221       const float scale = frame->scale_factor();
222       relative_position.set(relative_position.x() * scale,
223                             relative_position.y() * scale);
224 #endif
225       auto frame_with_cursor = std::make_unique<DesktopFrameWithCursor>(
226           std::move(frame), *cursor_, relative_position, previous_cursor_rect_,
227           cursor_changed_);
228       previous_cursor_rect_ = frame_with_cursor->cursor_rect();
229       cursor_changed_ = false;
230       frame = std::move(frame_with_cursor);
231     }
232   }
233 
234   callback_->OnCaptureResult(result, std::move(frame));
235 }
236 
OnMouseCursor(MouseCursor * cursor)237 void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
238   cursor_changed_ = true;
239   cursor_.reset(cursor);
240 }
241 
OnMouseCursorPosition(const DesktopVector & position)242 void DesktopAndCursorComposer::OnMouseCursorPosition(
243     const DesktopVector& position) {
244   cursor_position_ = position;
245 }
246 
247 }  // namespace webrtc
248