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
25 namespace webrtc {
26
27 namespace {
28
29 // Helper function that blends one image into another. Source image must be
30 // 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)31 void AlphaBlend(uint8_t* dest,
32 int dest_stride,
33 const uint8_t* src,
34 int src_stride,
35 const DesktopSize& size) {
36 for (int y = 0; y < size.height(); ++y) {
37 for (int x = 0; x < size.width(); ++x) {
38 uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3];
39 if (base_alpha == 255) {
40 continue;
41 } else if (base_alpha == 0) {
42 memcpy(dest + x * DesktopFrame::kBytesPerPixel,
43 src + x * DesktopFrame::kBytesPerPixel,
44 DesktopFrame::kBytesPerPixel);
45 } else {
46 dest[x * DesktopFrame::kBytesPerPixel] =
47 dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 +
48 src[x * DesktopFrame::kBytesPerPixel];
49 dest[x * DesktopFrame::kBytesPerPixel + 1] =
50 dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 +
51 src[x * DesktopFrame::kBytesPerPixel + 1];
52 dest[x * DesktopFrame::kBytesPerPixel + 2] =
53 dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 +
54 src[x * DesktopFrame::kBytesPerPixel + 2];
55 }
56 }
57 src += src_stride;
58 dest += dest_stride;
59 }
60 }
61
62 // DesktopFrame wrapper that draws mouse on a frame and restores original
63 // content before releasing the underlying frame.
64 class DesktopFrameWithCursor : public DesktopFrame {
65 public:
66 // Takes ownership of `frame`.
67 DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame,
68 const MouseCursor& cursor,
69 const DesktopVector& position,
70 const DesktopRect& previous_cursor_rect,
71 bool cursor_changed);
72 ~DesktopFrameWithCursor() override;
73
74 DesktopFrameWithCursor(const DesktopFrameWithCursor&) = delete;
75 DesktopFrameWithCursor& operator=(const DesktopFrameWithCursor&) = delete;
76
cursor_rect() const77 DesktopRect cursor_rect() const { return cursor_rect_; }
78
79 private:
80 const std::unique_ptr<DesktopFrame> original_frame_;
81
82 DesktopVector restore_position_;
83 std::unique_ptr<DesktopFrame> restore_frame_;
84 DesktopRect cursor_rect_;
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 // TODO(crbug:1323241) Update this code to properly handle the case where
109 // |previous_cursor_rect| is outside of the boundaries of |frame|.
110 // Any boundary check has to take into account the fact that
111 // |previous_cursor_rect| can be in DPI or in pixels, based on the platform
112 // we're running on.
113 mutable_updated_region()->AddRect(previous_cursor_rect);
114 } else if (cursor_changed) {
115 mutable_updated_region()->AddRect(cursor_rect_);
116 }
117
118 if (cursor_rect_.is_empty())
119 return;
120
121 // Copy original screen content under cursor to `restore_frame_`.
122 restore_position_ = cursor_rect_.top_left();
123 restore_frame_.reset(new BasicDesktopFrame(cursor_rect_.size()));
124 restore_frame_->CopyPixelsFrom(*this, cursor_rect_.top_left(),
125 DesktopRect::MakeSize(restore_frame_->size()));
126
127 // Blit the cursor.
128 uint8_t* cursor_rect_data =
129 reinterpret_cast<uint8_t*>(data()) + cursor_rect_.top() * stride() +
130 cursor_rect_.left() * DesktopFrame::kBytesPerPixel;
131 DesktopVector origin_shift = cursor_rect_.top_left().subtract(cursor_origin);
132 AlphaBlend(cursor_rect_data, stride(),
133 cursor.image()->data() +
134 origin_shift.y() * cursor.image()->stride() +
135 origin_shift.x() * DesktopFrame::kBytesPerPixel,
136 cursor.image()->stride(), cursor_rect_.size());
137 }
138
~DesktopFrameWithCursor()139 DesktopFrameWithCursor::~DesktopFrameWithCursor() {
140 // Restore original content of the frame.
141 if (restore_frame_) {
142 DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
143 target_rect.Translate(restore_position_);
144 CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
145 target_rect);
146 }
147 }
148
149 } // namespace
150
DesktopAndCursorComposer(std::unique_ptr<DesktopCapturer> desktop_capturer,const DesktopCaptureOptions & options)151 DesktopAndCursorComposer::DesktopAndCursorComposer(
152 std::unique_ptr<DesktopCapturer> desktop_capturer,
153 const DesktopCaptureOptions& options)
154 : DesktopAndCursorComposer(desktop_capturer.release(),
155 MouseCursorMonitor::Create(options).release()) {}
156
DesktopAndCursorComposer(DesktopCapturer * desktop_capturer,MouseCursorMonitor * mouse_monitor)157 DesktopAndCursorComposer::DesktopAndCursorComposer(
158 DesktopCapturer* desktop_capturer,
159 MouseCursorMonitor* mouse_monitor)
160 : desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) {
161 RTC_DCHECK(desktop_capturer_);
162 }
163
164 DesktopAndCursorComposer::~DesktopAndCursorComposer() = default;
165
166 std::unique_ptr<DesktopAndCursorComposer>
CreateWithoutMouseCursorMonitor(std::unique_ptr<DesktopCapturer> desktop_capturer)167 DesktopAndCursorComposer::CreateWithoutMouseCursorMonitor(
168 std::unique_ptr<DesktopCapturer> desktop_capturer) {
169 return std::unique_ptr<DesktopAndCursorComposer>(
170 new DesktopAndCursorComposer(desktop_capturer.release(), nullptr));
171 }
172
Start(DesktopCapturer::Callback * callback)173 void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
174 callback_ = callback;
175 if (mouse_monitor_)
176 mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
177 desktop_capturer_->Start(this);
178 }
179
SetSharedMemoryFactory(std::unique_ptr<SharedMemoryFactory> shared_memory_factory)180 void DesktopAndCursorComposer::SetSharedMemoryFactory(
181 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
182 desktop_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
183 }
184
CaptureFrame()185 void DesktopAndCursorComposer::CaptureFrame() {
186 if (mouse_monitor_)
187 mouse_monitor_->Capture();
188 desktop_capturer_->CaptureFrame();
189 }
190
SetExcludedWindow(WindowId window)191 void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
192 desktop_capturer_->SetExcludedWindow(window);
193 }
194
GetSourceList(SourceList * sources)195 bool DesktopAndCursorComposer::GetSourceList(SourceList* sources) {
196 return desktop_capturer_->GetSourceList(sources);
197 }
198
SelectSource(SourceId id)199 bool DesktopAndCursorComposer::SelectSource(SourceId id) {
200 return desktop_capturer_->SelectSource(id);
201 }
202
FocusOnSelectedSource()203 bool DesktopAndCursorComposer::FocusOnSelectedSource() {
204 return desktop_capturer_->FocusOnSelectedSource();
205 }
206
IsOccluded(const DesktopVector & pos)207 bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) {
208 return desktop_capturer_->IsOccluded(pos);
209 }
210
211 #if defined(WEBRTC_USE_GIO)
GetMetadata()212 DesktopCaptureMetadata DesktopAndCursorComposer::GetMetadata() {
213 return desktop_capturer_->GetMetadata();
214 }
215 #endif // defined(WEBRTC_USE_GIO)
216
OnCaptureResult(DesktopCapturer::Result result,std::unique_ptr<DesktopFrame> frame)217 void DesktopAndCursorComposer::OnCaptureResult(
218 DesktopCapturer::Result result,
219 std::unique_ptr<DesktopFrame> frame) {
220 if (frame && cursor_) {
221 if (!frame->may_contain_cursor() &&
222 frame->rect().Contains(cursor_position_) &&
223 !desktop_capturer_->IsOccluded(cursor_position_)) {
224 DesktopVector relative_position =
225 cursor_position_.subtract(frame->top_left());
226 #if defined(WEBRTC_MAC) || defined(CHROMEOS)
227 // On OSX, the logical(DIP) and physical coordinates are used mixingly.
228 // For example, the captured cursor has its size in physical pixels(2x)
229 // and location in logical(DIP) pixels on Retina monitor. This will cause
230 // problem when the desktop is mixed with Retina and non-Retina monitors.
231 // So we use DIP pixel for all location info and compensate with the scale
232 // factor of current frame to the `relative_position`.
233 const float scale = frame->scale_factor();
234 relative_position.set(relative_position.x() * scale,
235 relative_position.y() * scale);
236 #endif
237 auto frame_with_cursor = std::make_unique<DesktopFrameWithCursor>(
238 std::move(frame), *cursor_, relative_position, previous_cursor_rect_,
239 cursor_changed_);
240 previous_cursor_rect_ = frame_with_cursor->cursor_rect();
241 cursor_changed_ = false;
242 frame = std::move(frame_with_cursor);
243 frame->set_may_contain_cursor(true);
244 }
245 }
246
247 callback_->OnCaptureResult(result, std::move(frame));
248 }
249
OnMouseCursor(MouseCursor * cursor)250 void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
251 cursor_changed_ = true;
252 cursor_.reset(cursor);
253 }
254
OnMouseCursorPosition(const DesktopVector & position)255 void DesktopAndCursorComposer::OnMouseCursorPosition(
256 const DesktopVector& position) {
257 cursor_position_ = position;
258 }
259
260 } // namespace webrtc
261