From 6f79d840ba238b8122d7ab354f54a444bb22a4ca Mon Sep 17 00:00:00 2001 From: qiangchen Date: Wed, 21 Sep 2016 16:44:55 -0700 Subject: [PATCH] Bug Fix: Mac Retina Screen Capture's Mouse Cursor Too Small On retina display, when we do screen capture, the mouse cursor looks too small. The reason is that we painted the cursor with low resolution on to the frame directly. This CL fixes the bug. BUG=632995 Review-Url: https://codereview.webrtc.org/2350743003 Cr-Commit-Position: refs/heads/master@{#14340} --- .../modules/desktop_capture/desktop_frame.cc | 2 +- .../modules/desktop_capture/desktop_frame.h | 2 +- .../mouse_cursor_monitor_mac.mm | 61 ++++++++++++++----- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/webrtc/modules/desktop_capture/desktop_frame.cc b/webrtc/modules/desktop_capture/desktop_frame.cc index 51cf568aa0..dfc0928b66 100644 --- a/webrtc/modules/desktop_capture/desktop_frame.cc +++ b/webrtc/modules/desktop_capture/desktop_frame.cc @@ -31,7 +31,7 @@ DesktopFrame::DesktopFrame(DesktopSize size, DesktopFrame::~DesktopFrame() {} -void DesktopFrame::CopyPixelsFrom(uint8_t* src_buffer, int src_stride, +void DesktopFrame::CopyPixelsFrom(const uint8_t* src_buffer, int src_stride, const DesktopRect& dest_rect) { RTC_CHECK(DesktopRect::MakeSize(size()).ContainsRect(dest_rect)); diff --git a/webrtc/modules/desktop_capture/desktop_frame.h b/webrtc/modules/desktop_capture/desktop_frame.h index 632bcfc2e7..d491135029 100644 --- a/webrtc/modules/desktop_capture/desktop_frame.h +++ b/webrtc/modules/desktop_capture/desktop_frame.h @@ -58,7 +58,7 @@ class DesktopFrame { // Copies pixels from a buffer or another frame. |dest_rect| rect must lay // within bounds of this frame. - void CopyPixelsFrom(uint8_t* src_buffer, + void CopyPixelsFrom(const uint8_t* src_buffer, int src_stride, const DesktopRect& dest_rect); void CopyPixelsFrom(const DesktopFrame& src_frame, diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm index 5d6a9b0089..6e079b6662 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -30,6 +30,25 @@ namespace webrtc { +namespace { +// Paint the image, so that we can get a bitmap representation compatible with +// current context. For example, in the retina display, we are going to get an +// image with same visual size but underlying pixel size conforms to the retina +// setting. +NSImage* PaintInCurrentContext(NSImage* source) { + NSSize size = [source size]; + NSImage* new_image = [[[NSImage alloc] initWithSize:size] autorelease]; + [new_image lockFocus]; + NSRect frame = NSMakeRect(0, 0, size.width, size.height); + [source drawInRect:frame + fromRect:frame + operation:NSCompositeCopy + fraction:1.0]; + [new_image unlockFocus]; + return new_image; +} +} // namespace + class MouseCursorMonitorMac : public MouseCursorMonitor { public: MouseCursorMonitorMac(const DesktopCaptureOptions& options, @@ -47,7 +66,7 @@ class MouseCursorMonitorMac : public MouseCursorMonitor { void DisplaysReconfigured(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags); - void CaptureImage(); + void CaptureImage(float scale); rtc::scoped_refptr configuration_monitor_; CGWindowID window_id_; @@ -91,11 +110,6 @@ void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) { void MouseCursorMonitorMac::Capture() { assert(callback_); - CaptureImage(); - - if (mode_ != SHAPE_AND_POSITION) - return; - CursorState state = INSIDE; CGEventRef event = CGEventCreate(NULL); @@ -113,12 +127,18 @@ void MouseCursorMonitorMac::Capture() { // Find the dpi to physical pixel scale for the screen where the mouse cursor // is. for (MacDisplayConfigurations::iterator it = configuration.displays.begin(); - it != configuration.displays.end(); ++it) { + it != configuration.displays.end(); ++it) { if (it->bounds.Contains(position)) { scale = it->dip_to_pixel_scale; break; } } + + CaptureImage(scale); + + if (mode_ != SHAPE_AND_POSITION) + return; + // If we are capturing cursor for a specific window then we need to figure out // if the current mouse position is covered by another window and also adjust // |position| to make it relative to the window origin. @@ -228,24 +248,32 @@ void MouseCursorMonitorMac::Capture() { callback_->OnMouseCursorPosition(state, position); } -void MouseCursorMonitorMac::CaptureImage() { +void MouseCursorMonitorMac::CaptureImage(float scale) { NSCursor* nscursor = [NSCursor currentSystemCursor]; NSImage* nsimage = [nscursor image]; - NSSize nssize = [nsimage size]; - DesktopSize size(nssize.width, nssize.height); + NSSize nssize = [nsimage size]; // DIP size + + // For retina screen, we need to paint the cursor in current graphic context + // to get retina representation. + if (scale != 1.0) + nsimage = PaintInCurrentContext(nsimage); + + DesktopSize size(round(nssize.width * scale), + round(nssize.height * scale)); // Pixel size NSPoint nshotspot = [nscursor hotSpot]; DesktopVector hotspot( - std::max(0, std::min(size.width(), static_cast(nshotspot.x))), - std::max(0, std::min(size.height(), static_cast(nshotspot.y)))); + std::max(0, + std::min(size.width(), static_cast(nshotspot.x * scale))), + std::max(0, + std::min(size.height(), static_cast(nshotspot.y * scale)))); CGImageRef cg_image = [nsimage CGImageForProposedRect:NULL context:nil hints:nil]; if (!cg_image) return; if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 || - CGImageGetBytesPerRow(cg_image) != - static_cast(DesktopFrame::kBytesPerPixel * size.width()) || + CGImageGetWidth(cg_image) != static_cast(size.width()) || CGImageGetBitsPerComponent(cg_image) != 8) { return; } @@ -272,8 +300,9 @@ void MouseCursorMonitorMac::CaptureImage() { // the client. std::unique_ptr image( new BasicDesktopFrame(DesktopSize(size.width(), size.height()))); - memcpy(image->data(), src_data, - size.width() * size.height() * DesktopFrame::kBytesPerPixel); + + int src_stride = CGImageGetBytesPerRow(cg_image); + image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size)); CFRelease(image_data_ref);