2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
11 #include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
15 #include "webrtc/modules/desktop_capture/desktop_frame.h"
16 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
17 #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
18 #include "webrtc/modules/desktop_capture/window_capturer.h"
19 #include "webrtc/system_wrappers/interface/logging.h"
20 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
26 const int kScreenWidth = 100;
27 const int kScreenHeight = 100;
28 const int kCursorWidth = 10;
29 const int kCursorHeight = 10;
31 const int kTestCursorSize = 3;
32 const uint32_t kTestCursorData[kTestCursorSize][kTestCursorSize] = {
33 { 0xffffffff, 0x99990000, 0xaa222222, },
34 { 0x88008800, 0xaa0000aa, 0xaa333333, },
35 { 0x00000000, 0xaa0000aa, 0xaa333333, },
38 uint32_t GetFakeFramePixelValue(const DesktopVector& p) {
39 uint32_t r = 100 + p.x();
40 uint32_t g = 100 + p.y();
41 uint32_t b = 100 + p.x() + p.y();
42 return b + (g << 8) + (r << 16) + 0xff000000;
45 uint32_t GetFramePixel(const DesktopFrame& frame, const DesktopVector& pos) {
46 return *reinterpret_cast<uint32_t*>(frame.data() + pos.y() * frame.stride() +
47 pos.x() * DesktopFrame::kBytesPerPixel);
50 // Blends two pixel values taking into account alpha.
51 uint32_t BlendPixels(uint32_t dest, uint32_t src) {
52 uint8_t alpha = 255 - ((src & 0xff000000) >> 24);
54 ((dest & 0x00ff0000) >> 16) * alpha / 255 + ((src & 0x00ff0000) >> 16);
56 ((dest & 0x0000ff00) >> 8) * alpha / 255 + ((src & 0x0000ff00) >> 8);
57 uint32_t b = (dest & 0x000000ff) * alpha / 255 + (src & 0x000000ff);
58 return b + (g << 8) + (r << 16) + 0xff000000;
61 DesktopFrame* CreateTestFrame() {
63 new BasicDesktopFrame(DesktopSize(kScreenWidth, kScreenHeight));
64 uint32_t* data = reinterpret_cast<uint32_t*>(frame->data());
65 for (int y = 0; y < kScreenHeight; ++y) {
66 for (int x = 0; x < kScreenWidth; ++x) {
67 *(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
73 class FakeScreenCapturer : public DesktopCapturer {
75 FakeScreenCapturer() {}
77 virtual void Start(Callback* callback) OVERRIDE {
81 virtual void Capture(const DesktopRegion& region) OVERRIDE {
82 callback_->OnCaptureCompleted(next_frame_.release());
85 void SetNextFrame(DesktopFrame* next_frame) {
86 next_frame_.reset(next_frame);
92 scoped_ptr<DesktopFrame> next_frame_;
95 class FakeMouseMonitor : public MouseCursorMonitor {
97 FakeMouseMonitor() : changed_(true) {}
99 void SetState(CursorState state, const DesktopVector& pos) {
104 void SetHotspot(const DesktopVector& hotspot) {
105 if (!hotspot_.equals(hotspot))
110 virtual void Init(Callback* callback, Mode mode) OVERRIDE {
111 callback_ = callback;
114 virtual void Capture() OVERRIDE {
116 scoped_ptr<DesktopFrame> image(
117 new BasicDesktopFrame(DesktopSize(kCursorWidth, kCursorHeight)));
118 uint32_t* data = reinterpret_cast<uint32_t*>(image->data());
119 memset(data, 0, image->stride() * kCursorHeight);
121 // Set four pixels near the hotspot and leave all other blank.
122 for (int y = 0; y < kTestCursorSize; ++y) {
123 for (int x = 0; x < kTestCursorSize; ++x) {
124 data[(hotspot_.y() + y) * kCursorWidth + (hotspot_.x() + x)] =
125 kTestCursorData[y][x];
129 callback_->OnMouseCursor(new MouseCursor(image.release(), hotspot_));
132 callback_->OnMouseCursorPosition(state_, position_);
138 DesktopVector position_;
139 DesktopVector hotspot_;
143 void VerifyFrame(const DesktopFrame& frame,
144 MouseCursorMonitor::CursorState state,
145 const DesktopVector& pos) {
146 // Verify that all other pixels are set to their original values.
147 DesktopRect image_rect =
148 DesktopRect::MakeWH(kTestCursorSize, kTestCursorSize);
149 image_rect.Translate(pos);
151 for (int y = 0; y < kScreenHeight; ++y) {
152 for (int x = 0; x < kScreenWidth; ++x) {
153 DesktopVector p(x, y);
154 if (state == MouseCursorMonitor::INSIDE && image_rect.Contains(p)) {
155 EXPECT_EQ(BlendPixels(GetFakeFramePixelValue(p),
156 kTestCursorData[y - pos.y()][x - pos.x()]),
157 GetFramePixel(frame, p));
159 EXPECT_EQ(GetFakeFramePixelValue(p), GetFramePixel(frame, p));
165 class DesktopAndCursorComposerTest : public testing::Test,
166 public DesktopCapturer::Callback {
168 DesktopAndCursorComposerTest()
169 : fake_screen_(new FakeScreenCapturer()),
170 fake_cursor_(new FakeMouseMonitor()),
171 blender_(fake_screen_, fake_cursor_) {
174 // DesktopCapturer::Callback interface
175 virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE {
179 virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE {
184 // Owned by |blender_|.
185 FakeScreenCapturer* fake_screen_;
186 FakeMouseMonitor* fake_cursor_;
188 DesktopAndCursorComposer blender_;
189 scoped_ptr<DesktopFrame> frame_;
192 // Verify DesktopAndCursorComposer can handle the case when the screen capturer
194 TEST_F(DesktopAndCursorComposerTest, Error) {
195 blender_.Start(this);
197 fake_cursor_->SetHotspot(DesktopVector());
198 fake_cursor_->SetState(MouseCursorMonitor::INSIDE, DesktopVector());
199 fake_screen_->SetNextFrame(NULL);
201 blender_.Capture(DesktopRegion());
203 EXPECT_EQ(frame_, static_cast<DesktopFrame*>(NULL));
206 TEST_F(DesktopAndCursorComposerTest, Blend) {
209 int hotspot_x, hotspot_y;
213 {50, 50, 0, 0, true},
214 {100, 50, 0, 0, true},
215 {50, 100, 0, 0, true},
216 {100, 100, 0, 0, true},
219 {50, 50, 2, 5, true},
220 {100, 100, 2, 5, true},
222 {50, 50, 5, 2, true},
223 {100, 100, 5, 2, true},
227 blender_.Start(this);
229 for (size_t i = 0; i < (sizeof(tests) / sizeof(tests[0])); ++i) {
232 DesktopVector hotspot(tests[i].hotspot_x, tests[i].hotspot_y);
233 fake_cursor_->SetHotspot(hotspot);
235 MouseCursorMonitor::CursorState state = tests[i].inside
236 ? MouseCursorMonitor::INSIDE
237 : MouseCursorMonitor::OUTSIDE;
238 DesktopVector pos(tests[i].x, tests[i].y);
239 fake_cursor_->SetState(state, pos);
241 scoped_ptr<SharedDesktopFrame> frame(
242 SharedDesktopFrame::Wrap(CreateTestFrame()));
243 fake_screen_->SetNextFrame(frame->Share());
245 blender_.Capture(DesktopRegion());
247 VerifyFrame(*frame_, state, pos);
249 // Verify that the cursor is erased before the frame buffer is returned to
250 // the screen capturer.
252 VerifyFrame(*frame, MouseCursorMonitor::OUTSIDE, DesktopVector());
258 } // namespace webrtc