MouseCursorMonitor implementation for OSX and Windows.
BUG=crbug.com/173265 R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/2388004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4994 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
6b426ba5c5
commit
2df89c0c8b
@ -10,6 +10,8 @@
|
||||
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
DesktopFrame::DesktopFrame(DesktopSize size,
|
||||
@ -35,6 +37,20 @@ BasicDesktopFrame::~BasicDesktopFrame() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) {
|
||||
DesktopFrame* result = new BasicDesktopFrame(frame.size());
|
||||
for (int y = 0; y < frame.size().height(); ++y) {
|
||||
memcpy(result->data() + y * result->stride(),
|
||||
frame.data() + y * frame.stride(),
|
||||
frame.size().width() * kBytesPerPixel);
|
||||
}
|
||||
result->set_dpi(frame.dpi());
|
||||
result->set_capture_time_ms(frame.capture_time_ms());
|
||||
*result->mutable_updated_region() = frame.updated_region();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(
|
||||
DesktopSize size,
|
||||
int stride,
|
||||
|
@ -83,6 +83,9 @@ class BasicDesktopFrame : public DesktopFrame {
|
||||
explicit BasicDesktopFrame(DesktopSize size);
|
||||
virtual ~BasicDesktopFrame();
|
||||
|
||||
// Creates a BasicDesktopFrame that contains copy of |frame|.
|
||||
static DesktopFrame* CopyOf(const DesktopFrame& frame);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(BasicDesktopFrame);
|
||||
};
|
||||
|
@ -23,4 +23,10 @@ MouseCursor::MouseCursor(DesktopFrame* image, const DesktopVector& hotspot)
|
||||
|
||||
MouseCursor::~MouseCursor() {}
|
||||
|
||||
// static
|
||||
MouseCursor* MouseCursor::CopyOf(const MouseCursor& cursor) {
|
||||
return new MouseCursor(BasicDesktopFrame::CopyOf(cursor.image()),
|
||||
cursor.hotspot());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -25,8 +25,10 @@ class MouseCursor {
|
||||
MouseCursor(DesktopFrame* image, const DesktopVector& hotspot);
|
||||
~MouseCursor();
|
||||
|
||||
const DesktopFrame& image() { return *image_; }
|
||||
const DesktopVector& hotspot() { return hotspot_; }
|
||||
static MouseCursor* CopyOf(const MouseCursor& cursor);
|
||||
|
||||
const DesktopFrame& image() const { return *image_; }
|
||||
const DesktopVector& hotspot() const { return hotspot_; }
|
||||
|
||||
private:
|
||||
scoped_ptr<DesktopFrame> image_;
|
||||
|
@ -10,19 +10,209 @@
|
||||
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <assert.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(sergeyu): Implement MouseCursorMonitor for Mac.
|
||||
class MouseCursorMonitorMac : public MouseCursorMonitor {
|
||||
public:
|
||||
MouseCursorMonitorMac(CGWindowID window_id);
|
||||
virtual ~MouseCursorMonitorMac();
|
||||
|
||||
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
|
||||
virtual void Capture() OVERRIDE;
|
||||
|
||||
private:
|
||||
void CaptureImage();
|
||||
|
||||
CGWindowID window_id_;
|
||||
|
||||
Callback* callback_;
|
||||
Mode mode_;
|
||||
|
||||
scoped_ptr<MouseCursor> last_cursor_;
|
||||
};
|
||||
|
||||
MouseCursorMonitorMac::MouseCursorMonitorMac(CGWindowID window_id)
|
||||
: window_id_(window_id),
|
||||
callback_(NULL),
|
||||
mode_(SHAPE_AND_POSITION) {
|
||||
}
|
||||
|
||||
MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
|
||||
|
||||
void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
|
||||
assert(!callback_);
|
||||
assert(callback);
|
||||
|
||||
callback_ = callback;
|
||||
mode_ = mode;
|
||||
}
|
||||
|
||||
void MouseCursorMonitorMac::Capture() {
|
||||
assert(callback_);
|
||||
|
||||
CaptureImage();
|
||||
|
||||
if (mode_ != SHAPE_AND_POSITION)
|
||||
return;
|
||||
|
||||
CursorState state = INSIDE;
|
||||
|
||||
CGEventRef event = CGEventCreate(NULL);
|
||||
CGPoint gc_position = CGEventGetLocation(event);
|
||||
CFRelease(event);
|
||||
|
||||
DesktopVector position(gc_position.x, gc_position.y);
|
||||
|
||||
// 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.
|
||||
if (window_id_ != kCGNullWindowID) {
|
||||
// Get list of windows that may be covering parts of |window_id_|.
|
||||
// CGWindowListCopyWindowInfo() returns windows in order from front to back,
|
||||
// so |window_id_| is expected to be the last in the list.
|
||||
CFArrayRef window_array =
|
||||
CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly |
|
||||
kCGWindowListOptionOnScreenAboveWindow |
|
||||
kCGWindowListOptionIncludingWindow,
|
||||
window_id_);
|
||||
bool found_window = false;
|
||||
if (window_array) {
|
||||
CFIndex count = CFArrayGetCount(window_array);
|
||||
for (CFIndex i = 0; i < count; ++i) {
|
||||
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
|
||||
CFArrayGetValueAtIndex(window_array, i));
|
||||
|
||||
// Skip the Dock window. Dock window covers the whole screen, but it is
|
||||
// transparent.
|
||||
CFStringRef window_name = reinterpret_cast<CFStringRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowName));
|
||||
if (window_name && CFStringCompare(window_name, CFSTR("Dock"), 0) == 0)
|
||||
continue;
|
||||
|
||||
CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowBounds));
|
||||
CFNumberRef window_number = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowNumber));
|
||||
|
||||
if (window_bounds && window_number) {
|
||||
CGRect gc_window_rect;
|
||||
if (!CGRectMakeWithDictionaryRepresentation(window_bounds,
|
||||
&gc_window_rect)) {
|
||||
continue;
|
||||
}
|
||||
DesktopRect window_rect =
|
||||
DesktopRect::MakeXYWH(gc_window_rect.origin.x,
|
||||
gc_window_rect.origin.y,
|
||||
gc_window_rect.size.width,
|
||||
gc_window_rect.size.height);
|
||||
|
||||
CGWindowID window_id;
|
||||
if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id))
|
||||
continue;
|
||||
|
||||
if (window_id == window_id_) {
|
||||
found_window = true;
|
||||
if (!window_rect.Contains(position))
|
||||
state = OUTSIDE;
|
||||
position = position.subtract(window_rect.top_left());
|
||||
|
||||
assert(i == count - 1);
|
||||
break;
|
||||
} else if (window_rect.Contains(position)) {
|
||||
state = OUTSIDE;
|
||||
position.set(-1, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(window_array);
|
||||
}
|
||||
|
||||
if (!found_window) {
|
||||
// If we failed to get list of windows or the window wasn't in the list
|
||||
// pretend that the cursor is outside the window. This can happen, e.g. if
|
||||
// the window was closed.
|
||||
state = OUTSIDE;
|
||||
position.set(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
callback_->OnMouseCursorPosition(state, position);
|
||||
}
|
||||
|
||||
void MouseCursorMonitorMac::CaptureImage() {
|
||||
NSCursor* nscursor = [NSCursor currentSystemCursor];
|
||||
|
||||
NSImage* nsimage = [nscursor image];
|
||||
NSSize nssize = [nsimage size];
|
||||
DesktopSize size(nssize.width, nssize.height);
|
||||
NSPoint nshotspot = [nscursor hotSpot];
|
||||
DesktopVector hotspot(
|
||||
std::min(0, std::max(size.width(), static_cast<int>(nshotspot.x))),
|
||||
std::min(0, std::max(size.height(), static_cast<int>(nshotspot.y))));
|
||||
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()) ||
|
||||
CGImageGetBitsPerComponent(cg_image) != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
|
||||
CFDataRef image_data_ref = CGDataProviderCopyData(provider);
|
||||
if (image_data_ref == NULL)
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a MouseCursor that describes the cursor and pass it to
|
||||
// the client.
|
||||
scoped_ptr<DesktopFrame> image(
|
||||
new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
|
||||
memcpy(image->data(), src_data,
|
||||
size.width() * size.height() * DesktopFrame::kBytesPerPixel);
|
||||
|
||||
CFRelease(image_data_ref);
|
||||
|
||||
scoped_ptr<MouseCursor> cursor(new MouseCursor(image.release(), hotspot));
|
||||
last_cursor_.reset(MouseCursor::CopyOf(*cursor));
|
||||
|
||||
callback_->OnMouseCursor(cursor.release());
|
||||
}
|
||||
|
||||
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
||||
const DesktopCaptureOptions& options, WindowId window) {
|
||||
return NULL;
|
||||
return new MouseCursorMonitorMac(window);
|
||||
}
|
||||
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return NULL;
|
||||
return new MouseCursorMonitorMac(kCGNullWindowID);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -46,8 +46,11 @@ class MouseCursorMonitorTest : public testing::Test,
|
||||
bool position_received_;
|
||||
};
|
||||
|
||||
// TODO(sergeyu): Enable tests on all platforms.
|
||||
#if defined(USE_X11)
|
||||
// TODO(sergeyu): On Mac we need to initialize NSApplication before running the
|
||||
// tests. Figure out how to do that without breaking other tests in
|
||||
// modules_unittests and enable these tests on Mac.
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2532
|
||||
#if !defined(WEBRTC_MAC)
|
||||
#define MAYBE(x) x
|
||||
#else
|
||||
#define MAYBE(x) DISABLED_##x
|
||||
|
@ -10,19 +10,103 @@
|
||||
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||
#include "webrtc/modules/desktop_capture/win/cursor.h"
|
||||
#include "webrtc/system_wrappers/interface/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// TODO(sergeyu): Implement MouseCursorMonitor for Windows.
|
||||
class MouseCursorMonitorWin : public MouseCursorMonitor {
|
||||
public:
|
||||
explicit MouseCursorMonitorWin(HWND window);
|
||||
virtual ~MouseCursorMonitorWin();
|
||||
|
||||
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
|
||||
virtual void Capture() OVERRIDE;
|
||||
|
||||
private:
|
||||
HWND window_;
|
||||
|
||||
Callback* callback_;
|
||||
Mode mode_;
|
||||
|
||||
HDC desktop_dc_;
|
||||
|
||||
HCURSOR last_cursor_;
|
||||
};
|
||||
|
||||
MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
|
||||
: window_(window),
|
||||
callback_(NULL),
|
||||
mode_(SHAPE_AND_POSITION),
|
||||
desktop_dc_(NULL),
|
||||
last_cursor_(NULL) {
|
||||
}
|
||||
|
||||
MouseCursorMonitorWin::~MouseCursorMonitorWin() {
|
||||
if (desktop_dc_)
|
||||
ReleaseDC(NULL, desktop_dc_);
|
||||
}
|
||||
|
||||
void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
|
||||
assert(!callback_);
|
||||
assert(callback);
|
||||
|
||||
callback_ = callback;
|
||||
mode_ = mode;
|
||||
|
||||
desktop_dc_ = GetDC(NULL);
|
||||
}
|
||||
|
||||
void MouseCursorMonitorWin::Capture() {
|
||||
assert(callback_);
|
||||
|
||||
CURSORINFO cursor_info;
|
||||
cursor_info.cbSize = sizeof(CURSORINFO);
|
||||
if (!GetCursorInfo(&cursor_info)) {
|
||||
LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_cursor_ != cursor_info.hCursor) {
|
||||
last_cursor_ = cursor_info.hCursor;
|
||||
// Note that |cursor_info.hCursor| does not need to be freed.
|
||||
scoped_ptr<MouseCursor> cursor(
|
||||
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||
if (cursor.get())
|
||||
callback_->OnMouseCursor(cursor.release());
|
||||
}
|
||||
|
||||
if (mode_ != SHAPE_AND_POSITION)
|
||||
return;
|
||||
|
||||
DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
|
||||
bool inside = cursor_info.flags == CURSOR_SHOWING;
|
||||
|
||||
if (window_) {
|
||||
RECT rect;
|
||||
if (!GetWindowRect(window_, &rect)) {
|
||||
position.set(0, 0);
|
||||
inside = false;
|
||||
} else {
|
||||
position = position.subtract(DesktopVector(rect.left, rect.top));
|
||||
if (inside)
|
||||
inside = (window_ == WindowFromPoint(cursor_info.ptScreenPos));
|
||||
}
|
||||
}
|
||||
|
||||
callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position);
|
||||
}
|
||||
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
||||
const DesktopCaptureOptions& options, WindowId window) {
|
||||
return NULL;
|
||||
return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
|
||||
}
|
||||
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return NULL;
|
||||
return new MouseCursorMonitorWin(NULL);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||
#include "webrtc/modules/desktop_capture/differ.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
|
||||
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
|
||||
#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
|
||||
@ -328,11 +329,19 @@ void ScreenCapturerWin::CaptureCursor() {
|
||||
}
|
||||
|
||||
// Note that |cursor_info.hCursor| does not need to be freed.
|
||||
scoped_ptr<MouseCursorShape> cursor(
|
||||
CreateMouseCursorShapeFromCursor(desktop_dc_, cursor_info.hCursor));
|
||||
if (!cursor.get())
|
||||
scoped_ptr<MouseCursor> cursor_image(
|
||||
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||
if (!cursor_image.get())
|
||||
return;
|
||||
|
||||
scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
|
||||
cursor->hotspot = cursor_image->hotspot();
|
||||
cursor->size = cursor_image->image().size();
|
||||
cursor->data.assign(
|
||||
cursor_image->image().data(),
|
||||
cursor_image->image().data() +
|
||||
cursor_image->image().stride() * DesktopFrame::kBytesPerPixel);
|
||||
|
||||
// Compare the current cursor with the last one we sent to the client. If
|
||||
// they're the same, then don't bother sending the cursor again.
|
||||
if (last_cursor_.size.equals(cursor->size) &&
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||
#include "webrtc/system_wrappers/interface/compile_assert.h"
|
||||
#include "webrtc/system_wrappers/interface/logging.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
@ -92,29 +93,24 @@ void AlphaMul(uint32_t* data, int width, int height) {
|
||||
}
|
||||
|
||||
// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
|
||||
// |*has_alpha| is set to true if non-zero alpha is found. |stride| is expressed
|
||||
// in pixels.
|
||||
bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height,
|
||||
bool* has_alpha) {
|
||||
// Returns true if non-zero alpha is found. |stride| is expressed in pixels.
|
||||
bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
|
||||
const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
if (plane->rgbReserved != 0) {
|
||||
*has_alpha = true;
|
||||
if (plane->rgbReserved != 0)
|
||||
return true;
|
||||
}
|
||||
plane += 1;
|
||||
}
|
||||
plane += stride - width;
|
||||
}
|
||||
|
||||
*has_alpha = false;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
||||
MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
|
||||
ICONINFO iinfo;
|
||||
if (!GetIconInfo(cursor, &iinfo)) {
|
||||
LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
|
||||
@ -167,20 +163,18 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
||||
}
|
||||
|
||||
uint32_t* mask_plane = mask_data.get();
|
||||
|
||||
scoped_array<uint32_t> color_data;
|
||||
uint32_t* color_plane = NULL;
|
||||
int color_stride = 0;
|
||||
scoped_ptr<DesktopFrame> image(
|
||||
new BasicDesktopFrame(DesktopSize(width, height)));
|
||||
bool has_alpha = false;
|
||||
|
||||
if (is_color) {
|
||||
image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
|
||||
// Get the pixels from the color bitmap.
|
||||
color_data.reset(new uint32_t[width * height]);
|
||||
if (!GetDIBits(dc,
|
||||
scoped_color,
|
||||
0,
|
||||
height,
|
||||
color_data.get(),
|
||||
image->data(),
|
||||
reinterpret_cast<BITMAPINFO*>(&bmi),
|
||||
DIB_RGB_COLORS)) {
|
||||
LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
|
||||
@ -188,30 +182,28 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
color_plane = color_data.get();
|
||||
color_stride = width;
|
||||
|
||||
// GetDIBits() does not provide any indication whether the bitmap has alpha
|
||||
// channel, so we use HasAlphaChannel() below to find it out.
|
||||
if (!HasAlphaChannel(color_plane, color_stride, width, height, &has_alpha))
|
||||
return NULL;
|
||||
has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
|
||||
width, width, height);
|
||||
} else {
|
||||
// For non-color cursors, the mask contains both an AND and an XOR mask and
|
||||
// the height includes both. Thus, the width is correct, but we need to
|
||||
// divide by 2 to get the correct mask height.
|
||||
height /= 2;
|
||||
|
||||
image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
|
||||
|
||||
// The XOR mask becomes the color bitmap.
|
||||
color_plane = mask_plane + (width * height);
|
||||
color_stride = width;
|
||||
memcpy(
|
||||
image->data(), mask_plane + (width * height), image->stride() * width);
|
||||
}
|
||||
|
||||
// Reconstruct transparency from the mask if the color image does not has
|
||||
// alpha channel.
|
||||
if (!has_alpha) {
|
||||
bool add_outline = false;
|
||||
uint32_t* color = color_plane;
|
||||
uint32_t* dst = color_plane;
|
||||
uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
|
||||
uint32_t* mask = mask_plane;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
@ -226,36 +218,32 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) {
|
||||
// with black. In this case, we also add an outline around the cursor
|
||||
// so that it is visible against a dark background.
|
||||
if (*mask == kPixelRgbWhite) {
|
||||
if (*color != 0) {
|
||||
if (*dst != 0) {
|
||||
add_outline = true;
|
||||
*dst = kPixelRgbaBlack;
|
||||
} else {
|
||||
*dst = kPixelRgbaTransparent;
|
||||
}
|
||||
} else {
|
||||
*dst = kPixelRgbaBlack ^ *color;
|
||||
*dst = kPixelRgbaBlack ^ *dst;
|
||||
}
|
||||
|
||||
++color;
|
||||
++dst;
|
||||
++mask;
|
||||
}
|
||||
}
|
||||
if (add_outline) {
|
||||
AddCursorOutline(width, height, color_plane);
|
||||
AddCursorOutline(
|
||||
width, height, reinterpret_cast<uint32_t*>(image->data()));
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-multiply the resulting pixels since MouseCursorShape uses premultiplied
|
||||
// Pre-multiply the resulting pixels since MouseCursor uses premultiplied
|
||||
// images.
|
||||
AlphaMul(color_plane, width, height);
|
||||
AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
|
||||
|
||||
scoped_ptr<MouseCursorShape> result(new MouseCursorShape());
|
||||
result->data.assign(reinterpret_cast<char*>(color_plane),
|
||||
height * width * kBytesPerPixel);
|
||||
result->size.set(width, height);
|
||||
result->hotspot.set(hotspot_x, hotspot_y);
|
||||
return result.release();
|
||||
return new MouseCursor(
|
||||
image.release(), DesktopVector(hotspot_x, hotspot_y));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -13,13 +13,12 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Converts a cursor into a |MouseCursorShape| instance.
|
||||
MouseCursorShape* CreateMouseCursorShapeFromCursor(
|
||||
HDC dc, HCURSOR cursor);
|
||||
class MouseCursor;
|
||||
|
||||
// Converts an HCURSOR into a |MouseCursor| instance.
|
||||
MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -9,7 +9,9 @@
|
||||
*/
|
||||
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
|
||||
#include "webrtc/modules/desktop_capture/win/cursor.h"
|
||||
#include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h"
|
||||
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
|
||||
@ -19,9 +21,9 @@ namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
// Loads |left| from resources, converts it to a |MouseCursorShape| instance
|
||||
// and compares pixels with |right|. Returns true of MouseCursorShape bits
|
||||
// match |right|. |right| must be a 32bpp cursor with alpha channel.
|
||||
// Loads |left| from resources, converts it to a |MouseCursor| instance and
|
||||
// compares pixels with |right|. Returns true of MouseCursor bits match |right|.
|
||||
// |right| must be a 32bpp cursor with alpha channel.
|
||||
bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
||||
HMODULE instance = GetModuleHandle(NULL);
|
||||
|
||||
@ -32,8 +34,8 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
||||
|
||||
// Convert |cursor| to |mouse_shape|.
|
||||
HDC dc = GetDC(NULL);
|
||||
scoped_ptr<MouseCursorShape> mouse_shape(
|
||||
CreateMouseCursorShapeFromCursor(dc, cursor));
|
||||
scoped_ptr<MouseCursor> mouse_shape(
|
||||
CreateMouseCursorFromHCursor(dc, cursor));
|
||||
ReleaseDC(NULL, dc);
|
||||
|
||||
EXPECT_TRUE(mouse_shape.get());
|
||||
@ -56,7 +58,7 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
||||
|
||||
int width = bitmap_info.bmWidth;
|
||||
int height = bitmap_info.bmHeight;
|
||||
EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->size));
|
||||
EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image().size()));
|
||||
|
||||
// Get the pixels from |scoped_color|.
|
||||
int size = width * height;
|
||||
@ -64,13 +66,13 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
|
||||
EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
|
||||
|
||||
// Compare the 32bpp image in |mouse_shape| with the one loaded from |right|.
|
||||
return memcmp(data.get(), mouse_shape->data.data(),
|
||||
return memcmp(data.get(), mouse_shape->image().data(),
|
||||
size * sizeof(uint32_t)) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(MouseCursorShapeTest, MatchCursors) {
|
||||
TEST(MouseCursorTest, MatchCursors) {
|
||||
EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP,
|
||||
IDD_CURSOR1_32BPP));
|
||||
|
||||
|
@ -41,29 +41,6 @@ bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DesktopFrame that stores data in CFData.
|
||||
class CFDataDesktopFrame : public DesktopFrame {
|
||||
public:
|
||||
// Consumes |cf_data| reference.
|
||||
//
|
||||
// TODO(sergeyu): Here we const_cast<> the buffer used in CFDataRef. CFDataRef
|
||||
// buffer is immutable, but DesktopFrame is always mutable. This shouldn't be
|
||||
// a problem because frames generated by WindowCapturers are normally not
|
||||
// mutated. To avoid this hack consider making DesktopFrame immutable and add
|
||||
// MutableDesktopFrame.
|
||||
CFDataDesktopFrame(DesktopSize size, int stride, CFDataRef cf_data)
|
||||
: DesktopFrame(size, stride,
|
||||
const_cast<uint8_t*>(CFDataGetBytePtr(cf_data)), NULL),
|
||||
cf_data_(cf_data) {
|
||||
}
|
||||
virtual ~CFDataDesktopFrame() {
|
||||
CFRelease(cf_data_);
|
||||
}
|
||||
|
||||
private:
|
||||
CFDataRef cf_data_;
|
||||
};
|
||||
|
||||
class WindowCapturerMac : public WindowCapturer {
|
||||
public:
|
||||
WindowCapturerMac();
|
||||
@ -185,9 +162,18 @@ void WindowCapturerMac::Capture(const DesktopRegion& region) {
|
||||
int width = CGImageGetWidth(window_image);
|
||||
int height = CGImageGetHeight(window_image);
|
||||
CGDataProviderRef provider = CGImageGetDataProvider(window_image);
|
||||
DesktopFrame* frame = new CFDataDesktopFrame(
|
||||
DesktopSize(width, height), CGImageGetBytesPerRow(window_image),
|
||||
CGDataProviderCopyData(provider));
|
||||
CFDataRef cf_data = CGDataProviderCopyData(provider);
|
||||
DesktopFrame* frame = new BasicDesktopFrame(
|
||||
DesktopSize(width, height));
|
||||
|
||||
int src_stride = CGImageGetBytesPerRow(window_image);
|
||||
const uint8_t* src_data = CFDataGetBytePtr(cf_data);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
|
||||
DesktopFrame::kBytesPerPixel * width);
|
||||
}
|
||||
|
||||
CFRelease(cf_data);
|
||||
CFRelease(window_image);
|
||||
|
||||
callback_->OnCaptureCompleted(frame);
|
||||
|
Loading…
x
Reference in New Issue
Block a user