xref: /aosp_15_r20/external/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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