From 0d1e27f00fd6bf7aeae7cb19845b8cd1ed4a40c1 Mon Sep 17 00:00:00 2001 From: braveyao Date: Thu, 1 Jun 2017 14:27:41 -0700 Subject: [PATCH] desktopCapture: scale the cursor image according to screen scale factor on OSX Before 10.12, OSX may report 1X cursor on Retina screen. (See crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina screen. (See crbug.com/671436.) So scaling the cursor if the image size doesn't meet the expected size on either Retina or non-Retina screen. Also corrects the cursor caching and change detection, so we can only do scalingat cursor changing for better performance. As to screen capture on OSX, the captured frame already contains the current cursor. So the MouseCursorMonitorMac is not needed for ScreenCapture for performance purpose. BUG=671436 Review-Url: https://codereview.webrtc.org/2908853002 Cr-Commit-Position: refs/heads/master@{#18393} --- .../mouse_cursor_monitor_mac.mm | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm index 73d5e02857..af63bfdbb4 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -30,21 +30,26 @@ 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]; - [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; +CGImageRef CreateScaledCGImage(CGImageRef image, int width, int height) { + // Create context, keeping original image properties. + CGColorSpaceRef colorspace = CGImageGetColorSpace(image); + CGContextRef context = CGBitmapContextCreate(nullptr, + width, + height, + CGImageGetBitsPerComponent(image), + width * DesktopFrame::kBytesPerPixel, + colorspace, + CGImageGetBitmapInfo(image)); + + if (!context) return nil; + + // Draw image to context, resizing it. + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); + // Extract resulting image from context. + CGImageRef imgRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); + + return imgRef; } } // namespace @@ -72,7 +77,7 @@ class MouseCursorMonitorMac : public MouseCursorMonitor { ScreenId screen_id_; Callback* callback_; Mode mode_; - std::unique_ptr last_cursor_; + __strong NSImage* last_cursor_; rtc::scoped_refptr full_screen_chrome_window_detector_; }; @@ -253,10 +258,9 @@ void MouseCursorMonitorMac::CaptureImage(float scale) { NSImage* nsimage = [nscursor image]; 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); + // No need to caputre cursor image if it's unchanged since last capture. + if ([[nsimage TIFFRepresentation] isEqual:[last_cursor_ TIFFRepresentation]]) return; + last_cursor_ = nsimage; DesktopSize size(round(nssize.width * scale), round(nssize.height * scale)); // Pixel size @@ -271,30 +275,33 @@ void MouseCursorMonitorMac::CaptureImage(float scale) { if (!cg_image) return; + // Before 10.12, OSX may report 1X cursor on Retina screen. (See + // crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina + // screen. (See crbug.com/671436.) So scaling the cursor if needed. + CGImageRef scaled_cg_image = nil; + if (CGImageGetWidth(cg_image) != static_cast(size.width())) { + scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height()); + if (scaled_cg_image != nil) { + cg_image = scaled_cg_image; + } + } if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 || CGImageGetWidth(cg_image) != static_cast(size.width()) || CGImageGetBitsPerComponent(cg_image) != 8) { + if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image); return; } CGDataProviderRef provider = CGImageGetDataProvider(cg_image); CFDataRef image_data_ref = CGDataProviderCopyData(provider); - if (image_data_ref == NULL) + if (image_data_ref == NULL) { + if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image); return; + } const uint8_t* src_data = reinterpret_cast(CFDataGetBytePtr(image_data_ref)); - // Compare the cursor with the previous one. - if (last_cursor_.get() && - last_cursor_->image()->size().equals(size) && - last_cursor_->hotspot().equals(hotspot) && - memcmp(last_cursor_->image()->data(), src_data, - last_cursor_->image()->stride() * size.height()) == 0) { - CFRelease(image_data_ref); - return; - } - // Create a MouseCursor that describes the cursor and pass it to // the client. std::unique_ptr image( @@ -304,10 +311,10 @@ void MouseCursorMonitorMac::CaptureImage(float scale) { image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size)); CFRelease(image_data_ref); + if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image); std::unique_ptr cursor( new MouseCursor(image.release(), hotspot)); - last_cursor_.reset(MouseCursor::CopyOf(*cursor)); callback_->OnMouseCursor(cursor.release()); }