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()); }