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