/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h" #include #include "webrtc/modules/desktop_capture/desktop_capturer.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" namespace webrtc { namespace { // Helper function that blends one image into another. Source image must be // pre-multiplied with the alpha channel. Destination is assumed to be opaque. void AlphaBlend(uint8_t* dest, int dest_stride, const uint8_t* src, int src_stride, const DesktopSize& size) { for (int y = 0; y < size.height(); ++y) { for (int x = 0; x < size.width(); ++x) { uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3]; if (base_alpha == 255) { continue; } else if (base_alpha == 0) { memcpy(dest + x * DesktopFrame::kBytesPerPixel, src + x * DesktopFrame::kBytesPerPixel, DesktopFrame::kBytesPerPixel); } else { dest[x * DesktopFrame::kBytesPerPixel] = dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 + src[x * DesktopFrame::kBytesPerPixel]; dest[x * DesktopFrame::kBytesPerPixel + 1] = dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 + src[x * DesktopFrame::kBytesPerPixel + 1]; dest[x * DesktopFrame::kBytesPerPixel + 2] = dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 + src[x * DesktopFrame::kBytesPerPixel + 2]; } } src += src_stride; dest += dest_stride; } } // DesktopFrame wrapper that draws mouse on a frame and restores original // content before releasing the underlying frame. class DesktopFrameWithCursor : public DesktopFrame { public: // Takes ownership of |frame|. DesktopFrameWithCursor(DesktopFrame* frame, const MouseCursor& cursor, const DesktopVector& position); virtual ~DesktopFrameWithCursor(); private: scoped_ptr original_frame_; DesktopVector restore_position_; scoped_ptr restore_frame_; DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor); }; DesktopFrameWithCursor::DesktopFrameWithCursor(DesktopFrame* frame, const MouseCursor& cursor, const DesktopVector& position) : DesktopFrame(frame->size(), frame->stride(), frame->data(), frame->shared_memory()), original_frame_(frame) { set_dpi(frame->dpi()); set_capture_time_ms(frame->capture_time_ms()); mutable_updated_region()->Swap(frame->mutable_updated_region()); DesktopVector image_pos = position.subtract(cursor.hotspot()); DesktopRect target_rect = DesktopRect::MakeSize(cursor.image().size()); target_rect.Translate(image_pos); DesktopVector target_origin = target_rect.top_left(); target_rect.IntersectWith(DesktopRect::MakeSize(size())); if (target_rect.is_empty()) return; // Copy original screen content under cursor to |restore_frame_|. restore_position_ = target_rect.top_left(); restore_frame_.reset(new BasicDesktopFrame(target_rect.size())); restore_frame_->CopyPixelsFrom(*this, target_rect.top_left(), DesktopRect::MakeSize(restore_frame_->size())); // Blit the cursor. uint8_t* target_rect_data = reinterpret_cast(data()) + target_rect.top() * stride() + target_rect.left() * DesktopFrame::kBytesPerPixel; DesktopVector origin_shift = target_rect.top_left().subtract(target_origin); AlphaBlend(target_rect_data, stride(), cursor.image().data() + origin_shift.y() * cursor.image().stride() + origin_shift.x() * DesktopFrame::kBytesPerPixel, cursor.image().stride(), target_rect.size()); } DesktopFrameWithCursor::~DesktopFrameWithCursor() { // Restore original content of the frame. if (restore_frame_.get()) { DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size()); target_rect.Translate(restore_position_); CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(), target_rect); } } } // namespace DesktopAndCursorComposer::DesktopAndCursorComposer( DesktopCapturer* desktop_capturer, MouseCursorMonitor* mouse_monitor) : desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) { } DesktopAndCursorComposer::~DesktopAndCursorComposer() {} void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) { callback_ = callback; if (mouse_monitor_.get()) mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION); desktop_capturer_->Start(this); } void DesktopAndCursorComposer::Capture(const DesktopRegion& region) { if (mouse_monitor_.get()) mouse_monitor_->Capture(); desktop_capturer_->Capture(region); } SharedMemory* DesktopAndCursorComposer::CreateSharedMemory(size_t size) { return callback_->CreateSharedMemory(size); } void DesktopAndCursorComposer::OnCaptureCompleted(DesktopFrame* frame) { if (cursor_.get() && cursor_state_ == MouseCursorMonitor::INSIDE) { DesktopFrameWithCursor* frame_with_cursor = new DesktopFrameWithCursor(frame, *cursor_, cursor_position_); frame = frame_with_cursor; } callback_->OnCaptureCompleted(frame); } void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) { cursor_.reset(cursor); } void DesktopAndCursorComposer::OnMouseCursorPosition( MouseCursorMonitor::CursorState state, const DesktopVector& position) { cursor_state_ = state; cursor_position_ = position; } } // namespace webrtc