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}
This commit is contained in:
qiangchen 2016-09-21 16:44:55 -07:00 committed by Commit bot
parent e35d329315
commit 6f79d840ba
3 changed files with 47 additions and 18 deletions

View File

@ -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));

View File

@ -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,

View File

@ -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<DesktopConfigurationMonitor> 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<int>(nshotspot.x))),
std::max(0, std::min(size.height(), static_cast<int>(nshotspot.y))));
std::max(0,
std::min(size.width(), static_cast<int>(nshotspot.x * scale))),
std::max(0,
std::min(size.height(), static_cast<int>(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<size_t>(DesktopFrame::kBytesPerPixel * size.width()) ||
CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
CGImageGetBitsPerComponent(cg_image) != 8) {
return;
}
@ -272,8 +300,9 @@ void MouseCursorMonitorMac::CaptureImage() {
// the client.
std::unique_ptr<DesktopFrame> 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);