diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi index c939d298c..3f70d0396 100644 --- a/webrtc/modules/desktop_capture/desktop_capture.gypi +++ b/webrtc/modules/desktop_capture/desktop_capture.gypi @@ -55,8 +55,11 @@ "shared_desktop_frame.h", "shared_memory.cc", "shared_memory.h", + "win/cursor.cc", + "win/cursor.h", "win/desktop.cc", "win/desktop.h", + "win/scoped_gdi_object.h", "win/scoped_thread_desktop.cc", "win/scoped_thread_desktop.h", "window_capturer.h", @@ -143,6 +146,9 @@ "screen_capturer_mock_objects.h", "screen_capturer_unittest.cc", "window_capturer_unittest.cc", + "win/cursor_unittest.cc", + "win/cursor_unittest_resources.h", + "win/cursor_unittest_resources.rc", ], 'conditions': [ # Run screen/window capturer tests only on platforms where they are diff --git a/webrtc/modules/desktop_capture/screen_capturer_win.cc b/webrtc/modules/desktop_capture/screen_capturer_win.cc index 629537f87..969b6a5a4 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_win.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_win.cc @@ -19,6 +19,7 @@ #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" +#include "webrtc/modules/desktop_capture/win/cursor.h" #include "webrtc/modules/desktop_capture/win/desktop.h" #include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" #include "webrtc/system_wrappers/interface/logging.h" @@ -37,15 +38,6 @@ typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; -// Pixel colors used when generating cursor outlines. -const uint32_t kPixelBgraBlack = 0xff000000; -const uint32_t kPixelBgraWhite = 0xffffffff; -const uint32_t kPixelBgraTransparent = 0x00000000; - -uint8_t AlphaMul(uint8_t v, uint8_t alpha) { - return (static_cast(v) * alpha) >> 8; -} - // ScreenCapturerWin captures 32bit RGB using GDI. // // ScreenCapturerWin is double-buffered as required by ScreenCapturer. @@ -67,10 +59,6 @@ class ScreenCapturerWin : public ScreenCapturer { // Captures the current screen contents into the current buffer. void CaptureImage(); - // Expand the cursor shape to add a white outline for visibility against - // dark backgrounds. - void AddCursorOutline(int width, int height, uint32_t* dst); - // Capture the current cursor shape. void CaptureCursor(); @@ -330,178 +318,19 @@ void ScreenCapturerWin::CaptureImage() { } } -void ScreenCapturerWin::AddCursorOutline(int width, - int height, - uint32_t* dst) { - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - // If this is a transparent pixel (bgr == 0 and alpha = 0), check the - // neighbor pixels to see if this should be changed to an outline pixel. - if (*dst == kPixelBgraTransparent) { - // Change to white pixel if any neighbors (top, bottom, left, right) - // are black. - if ((y > 0 && dst[-width] == kPixelBgraBlack) || - (y < height - 1 && dst[width] == kPixelBgraBlack) || - (x > 0 && dst[-1] == kPixelBgraBlack) || - (x < width - 1 && dst[1] == kPixelBgraBlack)) { - *dst = kPixelBgraWhite; - } - } - dst++; - } - } -} - void ScreenCapturerWin::CaptureCursor() { CURSORINFO cursor_info; cursor_info.cbSize = sizeof(CURSORINFO); if (!GetCursorInfo(&cursor_info)) { - LOG_F(LS_INFO) << "Unable to get cursor info. Error = " << GetLastError(); + LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError(); return; } - // Note that this does not need to be freed. - HCURSOR hcursor = cursor_info.hCursor; - ICONINFO iinfo; - if (!GetIconInfo(hcursor, &iinfo)) { - LOG_F(LS_INFO) << "Unable to get cursor icon info. Error = " - << GetLastError(); + // Note that |cursor_info.hCursor| does not need to be freed. + scoped_ptr cursor( + CreateMouseCursorShapeFromCursor(desktop_dc_, cursor_info.hCursor)); + if (!cursor.get()) return; - } - int hotspot_x = iinfo.xHotspot; - int hotspot_y = iinfo.yHotspot; - - // Get the cursor bitmap. - HBITMAP hbitmap; - BITMAP bitmap; - bool color_bitmap; - if (iinfo.hbmColor) { - // Color cursor bitmap. - color_bitmap = true; - hbitmap = reinterpret_cast( - CopyImage(iinfo.hbmColor, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)); - if (!hbitmap) { - LOG_F(LS_INFO) << "Unable to copy color cursor image. Error = " - << GetLastError(); - return; - } - - // Free the color and mask bitmaps since we only need our copy. - DeleteObject(iinfo.hbmColor); - DeleteObject(iinfo.hbmMask); - } else { - // Black and white (xor) cursor. - color_bitmap = false; - hbitmap = iinfo.hbmMask; - } - - if (!GetObject(hbitmap, sizeof(BITMAP), &bitmap)) { - LOG_F(LS_INFO) << "Unable to get cursor bitmap. Error = " << GetLastError(); - DeleteObject(hbitmap); - return; - } - - int width = bitmap.bmWidth; - int height = bitmap.bmHeight; - // 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. - if (!color_bitmap) { - height /= 2; - } - int data_size = height * width * DesktopFrame::kBytesPerPixel; - - scoped_ptr cursor(new MouseCursorShape()); - cursor->data.resize(data_size); - uint8_t* cursor_dst_data = - reinterpret_cast(&*(cursor->data.begin())); - - // Copy/convert cursor bitmap into format needed by chromotocol. - int row_bytes = bitmap.bmWidthBytes; - if (color_bitmap) { - if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 32) { - LOG_F(LS_INFO) << "Unsupported color cursor format. Error = " - << GetLastError(); - DeleteObject(hbitmap); - return; - } - - // Copy across colour cursor imagery. - // MouseCursorShape stores imagery top-down, and premultiplied - // by the alpha channel, whereas windows stores them bottom-up - // and not premultiplied. - uint8_t* cursor_src_data = reinterpret_cast(bitmap.bmBits); - uint8_t* src = cursor_src_data + ((height - 1) * row_bytes); - uint8_t* dst = cursor_dst_data; - for (int row = 0; row < height; ++row) { - for (int column = 0; column < width; ++column) { - dst[0] = AlphaMul(src[0], src[3]); - dst[1] = AlphaMul(src[1], src[3]); - dst[2] = AlphaMul(src[2], src[3]); - dst[3] = src[3]; - dst += DesktopFrame::kBytesPerPixel; - src += DesktopFrame::kBytesPerPixel; - } - src -= row_bytes + (width * DesktopFrame::kBytesPerPixel); - } - } else { - if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 1) { - LOG(LS_VERBOSE) << "Unsupported cursor mask format. Error = " - << GetLastError(); - DeleteObject(hbitmap); - return; - } - - // x2 because there are 2 masks in the bitmap: AND and XOR. - int mask_bytes = height * row_bytes * 2; - scoped_array mask(new uint8_t[mask_bytes]); - if (!GetBitmapBits(hbitmap, mask_bytes, mask.get())) { - LOG(LS_VERBOSE) << "Unable to get cursor mask bits. Error = " - << GetLastError(); - DeleteObject(hbitmap); - return; - } - uint8_t* and_mask = mask.get(); - uint8_t* xor_mask = mask.get() + height * row_bytes; - uint8_t* dst = cursor_dst_data; - bool add_outline = false; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int byte = y * row_bytes + x / 8; - int bit = 7 - x % 8; - int and_bit = and_mask[byte] & (1 << bit); - int xor_bit = xor_mask[byte] & (1 << bit); - - // The two cursor masks combine as follows: - // AND XOR Windows Result Our result RGB Alpha - // 0 0 Black Black 00 ff - // 0 1 White White ff ff - // 1 0 Screen Transparent 00 00 - // 1 1 Reverse-screen Black 00 ff - // Since we don't support XOR cursors, we replace the "Reverse Screen" - // with black. In this case, we also add an outline around the cursor - // so that it is visible against a dark background. - int rgb = (!and_bit && xor_bit) ? 0xff : 0x00; - int alpha = (and_bit && !xor_bit) ? 0x00 : 0xff; - *dst++ = rgb; - *dst++ = rgb; - *dst++ = rgb; - *dst++ = alpha; - if (and_bit && xor_bit) { - add_outline = true; - } - } - } - if (add_outline) { - AddCursorOutline(width, height, - reinterpret_cast(cursor_dst_data)); - } - } - - DeleteObject(hbitmap); - - cursor->size.set(width, height); - cursor->hotspot.set(hotspot_x, hotspot_y); // 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. @@ -511,7 +340,8 @@ void ScreenCapturerWin::CaptureCursor() { return; } - LOG(LS_VERBOSE) << "Sending updated cursor: " << width << "x" << height; + LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x" + << cursor->size.height(); // Record the last cursor image that we sent to the client. last_cursor_ = *cursor; diff --git a/webrtc/modules/desktop_capture/win/cursor.cc b/webrtc/modules/desktop_capture/win/cursor.cc new file mode 100644 index 000000000..2c32c0cad --- /dev/null +++ b/webrtc/modules/desktop_capture/win/cursor.cc @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/cursor.h" + +#include + +#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/system_wrappers/interface/compile_assert.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +namespace { + +#if defined(WEBRTC_ARCH_LITTLE_ENDIAN) + +#define RGBA(r, g, b, a) \ + ((((a) << 24) & 0xff000000) | \ + (((b) << 16) & 0xff0000) | \ + (((g) << 8) & 0xff00) | \ + ((r) & 0xff)) + +#else // !defined(WEBRTC_ARCH_LITTLE_ENDIAN) + +#define RGBA(r, g, b, a) \ + ((((r) << 24) & 0xff000000) | \ + (((g) << 16) & 0xff0000) | \ + (((b) << 8) & 0xff00) | \ + ((a) & 0xff)) + +#endif // !defined(WEBRTC_ARCH_LITTLE_ENDIAN) + +const int kBytesPerPixel = DesktopFrame::kBytesPerPixel; + +// Pixel colors used when generating cursor outlines. +const uint32_t kPixelRgbaBlack = RGBA(0, 0, 0, 0xff); +const uint32_t kPixelRgbaWhite = RGBA(0xff, 0xff, 0xff, 0xff); +const uint32_t kPixelRgbaTransparent = RGBA(0, 0, 0, 0); + +const uint32_t kPixelRgbWhite = RGB(0xff, 0xff, 0xff); +const uint32_t kPixelRgbBlack = RGB(0, 0, 0); + +// Expands the cursor shape to add a white outline for visibility against +// dark backgrounds. +void AddCursorOutline(int width, int height, uint32_t* data) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // If this is a transparent pixel (bgr == 0 and alpha = 0), check the + // neighbor pixels to see if this should be changed to an outline pixel. + if (*data == kPixelRgbaTransparent) { + // Change to white pixel if any neighbors (top, bottom, left, right) + // are black. + if ((y > 0 && data[-width] == kPixelRgbaBlack) || + (y < height - 1 && data[width] == kPixelRgbaBlack) || + (x > 0 && data[-1] == kPixelRgbaBlack) || + (x < width - 1 && data[1] == kPixelRgbaBlack)) { + *data = kPixelRgbaWhite; + } + } + data++; + } + } +} + +// Premultiplies RGB components of a pixel by its alpha component. +uint32_t AlphaMul(uint32_t pixel) { + COMPILE_ASSERT(sizeof(uint32_t) == kBytesPerPixel); + + RGBQUAD from = *reinterpret_cast(&pixel); + RGBQUAD to = { + (static_cast(from.rgbBlue) * from.rgbReserved) / 0xff, + (static_cast(from.rgbGreen) * from.rgbReserved) / 0xff, + (static_cast(from.rgbRed) * from.rgbReserved) / 0xff, + from.rgbReserved + }; + + return *reinterpret_cast(&to); +} + +// 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) { + const RGBQUAD* plane = reinterpret_cast(data); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + if (plane->rgbReserved != 0) { + *has_alpha = true; + return true; + } + plane += 1; + } + plane += stride - width; + } + + *has_alpha = false; + return true; +} + +} // namespace + +MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) { + ICONINFO iinfo; + if (!GetIconInfo(cursor, &iinfo)) { + LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = " + << GetLastError(); + return NULL; + } + + int hotspot_x = iinfo.xHotspot; + int hotspot_y = iinfo.yHotspot; + + // Make sure the bitmaps will be freed. + win::ScopedBitmap scoped_mask(iinfo.hbmMask); + win::ScopedBitmap scoped_color(iinfo.hbmColor); + bool is_color = iinfo.hbmColor != NULL; + + // Get |scoped_mask| dimensions. + BITMAP bitmap_info; + if (!GetObject(scoped_mask, sizeof(bitmap_info), &bitmap_info)) { + LOG_F(LS_ERROR) << "Unable to get bitmap info. Error = " + << GetLastError(); + return NULL; + } + + int width = bitmap_info.bmWidth; + int height = bitmap_info.bmHeight; + scoped_array mask_data(new uint32_t[width * height]); + + // Get pixel data from |scoped_mask| converting it to 32bpp along the way. + // GetDIBits() sets the alpha component of every pixel to 0. + BITMAPV5HEADER bmi = {0}; + bmi.bV5Size = sizeof(bmi); + bmi.bV5Width = width; + bmi.bV5Height = -height; // request a top-down bitmap. + bmi.bV5Planes = 1; + bmi.bV5BitCount = kBytesPerPixel * 8; + bmi.bV5Compression = BI_RGB; + bmi.bV5AlphaMask = 0xff000000; + bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE; + bmi.bV5Intent = LCS_GM_BUSINESS; + if (!GetDIBits(dc, + scoped_mask, + 0, + height, + mask_data.get(), + reinterpret_cast(&bmi), + DIB_RGB_COLORS)) { + LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = " + << GetLastError(); + return NULL; + } + + uint32_t* mask_plane = mask_data.get(); + + scoped_array color_data; + uint32_t* color_plane = NULL; + int color_stride = 0; + bool has_alpha = false; + + if (is_color) { + // 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(), + reinterpret_cast(&bmi), + DIB_RGB_COLORS)) { + LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = " + << GetLastError(); + 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; + } 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; + + // The XOR mask becomes the color bitmap. + color_plane = mask_plane + (width * height); + color_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* mask = mask_plane; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // The two bitmaps combine as follows: + // mask color Windows Result Our result RGB Alpha + // 0 00 Black Black 00 ff + // 0 ff White White ff ff + // 1 00 Screen Transparent 00 00 + // 1 ff Reverse-screen Black 00 ff + // + // Since we don't support XOR cursors, we replace the "Reverse Screen" + // 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) { + add_outline = true; + *dst = kPixelRgbaBlack; + } else { + *dst = kPixelRgbaTransparent; + } + } else { + *dst = kPixelRgbaBlack ^ *color; + } + + ++color; + ++dst; + ++mask; + } + } + if (add_outline) { + AddCursorOutline(width, height, color_plane); + } + } + + scoped_ptr result(new MouseCursorShape()); + result->data.assign(reinterpret_cast(color_plane), + height * width * kBytesPerPixel); + result->size.set(width, height); + result->hotspot.set(hotspot_x, hotspot_y); + return result.release(); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/cursor.h b/webrtc/modules/desktop_capture/win/cursor.h new file mode 100644 index 000000000..08a6c4a0e --- /dev/null +++ b/webrtc/modules/desktop_capture/win/cursor.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_ + +#include + +#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" + +namespace webrtc { + +// Converts a cursor into a |MouseCursorShape| instance. +MouseCursorShape* CreateMouseCursorShapeFromCursor( + HDC dc, HCURSOR cursor); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_ diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest.cc b/webrtc/modules/desktop_capture/win/cursor_unittest.cc new file mode 100644 index 000000000..c1c741736 --- /dev/null +++ b/webrtc/modules/desktop_capture/win/cursor_unittest.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gmock/include/gmock/gmock.h" +#include "webrtc/modules/desktop_capture/desktop_geometry.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" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +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. +bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) { + HMODULE instance = GetModuleHandle(NULL); + + // Load |left| from the EXE module's resources. + win::ScopedCursor cursor(reinterpret_cast( + LoadImage(instance, MAKEINTRESOURCE(left), IMAGE_CURSOR, 0, 0, 0))); + EXPECT_TRUE(cursor != NULL); + + // Convert |cursor| to |mouse_shape|. + HDC dc = GetDC(NULL); + scoped_ptr mouse_shape( + CreateMouseCursorShapeFromCursor(dc, cursor)); + ReleaseDC(NULL, dc); + + EXPECT_TRUE(mouse_shape.get()); + + // Load |right|. + cursor.Set(reinterpret_cast( + LoadImage(instance, MAKEINTRESOURCE(right), IMAGE_CURSOR, 0, 0, 0))); + + ICONINFO iinfo; + EXPECT_TRUE(GetIconInfo(cursor, &iinfo)); + EXPECT_TRUE(iinfo.hbmColor); + + // Make sure the bitmaps will be freed. + win::ScopedBitmap scoped_mask(iinfo.hbmMask); + win::ScopedBitmap scoped_color(iinfo.hbmColor); + + // Get |scoped_color| dimensions. + BITMAP bitmap_info; + EXPECT_TRUE(GetObject(scoped_color, sizeof(bitmap_info), &bitmap_info)); + + int width = bitmap_info.bmWidth; + int height = bitmap_info.bmHeight; + EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->size)); + + // Get the pixels from |scoped_color|. + int size = width * height; + scoped_array data(new uint32_t[size]); + 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(), + size * sizeof(uint32_t)) == 0; +} + +} // namespace + +TEST(MouseCursorShapeTest, MatchCursors) { + EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP, + IDD_CURSOR1_32BPP)); + + EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_8BPP, + IDD_CURSOR1_32BPP)); + + EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR2_1BPP, + IDD_CURSOR2_32BPP)); + + EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR3_4BPP, + IDD_CURSOR3_32BPP)); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h new file mode 100644 index 000000000..89545c165 --- /dev/null +++ b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_ + +#define IDD_CURSOR1_24BPP 101 +#define IDD_CURSOR1_32BPP 102 +#define IDD_CURSOR1_8BPP 103 + +#define IDD_CURSOR2_1BPP 104 +#define IDD_CURSOR2_32BPP 105 + +#define IDD_CURSOR3_4BPP 106 +#define IDD_CURSOR3_32BPP 107 + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_ diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc new file mode 100644 index 000000000..4f41489ec --- /dev/null +++ b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h" + +// These cursors are matched with their less than 32bpp counterparts below. +IDD_CURSOR1_32BPP CURSOR "cursor_test_data/1_32bpp.cur" +IDD_CURSOR2_32BPP CURSOR "cursor_test_data/2_32bpp.cur" +IDD_CURSOR3_32BPP CURSOR "cursor_test_data/3_32bpp.cur" + +// Matches IDD_CURSOR1_32BPP. +IDD_CURSOR1_24BPP CURSOR "cursor_test_data/1_24bpp.cur" + +// Matches IDD_CURSOR1_32BPP. +IDD_CURSOR1_8BPP CURSOR "cursor_test_data/1_8bpp.cur" + +// Matches IDD_CURSOR2_32BPP. +IDD_CURSOR2_1BPP CURSOR "cursor_test_data/2_1bpp.cur" + +// Matches IDD_CURSOR3_32BPP. +IDD_CURSOR3_4BPP CURSOR "cursor_test_data/3_4bpp.cur" diff --git a/webrtc/modules/desktop_capture/win/scoped_gdi_object.h b/webrtc/modules/desktop_capture/win/scoped_gdi_object.h new file mode 100644 index 000000000..0ca35c526 --- /dev/null +++ b/webrtc/modules/desktop_capture/win/scoped_gdi_object.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_ + +#include + +#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace win { + +// Scoper for GDI objects. +template +class ScopedGDIObject { + public: + ScopedGDIObject() : handle_(NULL) {} + explicit ScopedGDIObject(T object) : handle_(object) {} + + ~ScopedGDIObject() { + Traits::Close(handle_); + } + + T Get() { + return handle_; + } + + void Set(T object) { + if (handle_ && object != handle_) + Traits::Close(handle_); + handle_ = object; + } + + ScopedGDIObject& operator=(T object) { + Set(object); + return *this; + } + + T release() { + T object = handle_; + handle_ = NULL; + return object; + } + + operator T() { return handle_; } + + private: + T handle_; + + DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); +}; + +// The traits class that uses DeleteObject() to close a handle. +template +class DeleteObjectTraits { + public: + // Closes the handle. + static void Close(T handle) { + if (handle) + DeleteObject(handle); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DeleteObjectTraits); +}; + +// The traits class that uses DestroyCursor() to close a handle. +class DestroyCursorTraits { + public: + // Closes the handle. + static void Close(HCURSOR handle) { + if (handle) + DestroyCursor(handle); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DestroyCursorTraits); +}; + +typedef ScopedGDIObject > ScopedBitmap; +typedef ScopedGDIObject ScopedCursor; + +} // namespace win +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_