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}
This commit is contained in:
braveyao 2017-06-01 14:27:41 -07:00 committed by Commit Bot
parent 8e857d10fd
commit 0d1e27f00f

View File

@ -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<MouseCursor> last_cursor_;
__strong NSImage* last_cursor_;
rtc::scoped_refptr<FullScreenChromeWindowDetector>
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_t>(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_t>(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<const uint8_t*>(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<DesktopFrame> 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<MouseCursor> cursor(
new MouseCursor(image.release(), hotspot));
last_cursor_.reset(MouseCursor::CopyOf(*cursor));
callback_->OnMouseCursor(cursor.release());
}