blob: 0bb723750a255e28568febf3ff539fe271a439d5 [file] [log] [blame]
sergeyu@chromium.orgba6d56c2013-10-16 02:48:41 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
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.
9 */
10
11#include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
12
13#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/window_capturer.h"
18#include "webrtc/system_wrappers/interface/logging.h"
19#include "webrtc/system_wrappers/interface/scoped_ptr.h"
20
21namespace webrtc {
22
23namespace {
24
25const int kScreenWidth = 100;
26const int kScreenHeight = 100;
27const int kCursorWidth = 10;
28const int kCursorHeight = 10;
29
30const int kTestCursorSize = 3;
31const uint32_t kTestCursorData[kTestCursorSize][kTestCursorSize] = {
32 { 0xffffffff, 0x99990000, 0xaa222222, },
33 { 0x88008800, 0xaa0000aa, 0xaa333333, },
34 { 0x00000000, 0xaa0000aa, 0xaa333333, },
35};
36
37uint32_t GetFakeFramePixelValue(const DesktopVector& p) {
38 uint32_t r = 100 + p.x();
39 uint32_t g = 100 + p.y();
40 uint32_t b = 100 + p.x() + p.y();
41 return b + (g << 8) + (r << 16) + 0xff000000;
42}
43
44uint32_t GetFramePixel(const DesktopFrame& frame, const DesktopVector& pos) {
45 return *reinterpret_cast<uint32_t*>(frame.data() + pos.y() * frame.stride() +
46 pos.x() * DesktopFrame::kBytesPerPixel);
47}
48
49// Blends two pixel values taking into account alpha.
50uint32_t BlendPixels(uint32_t dest, uint32_t src) {
51 uint8_t alpha = 255 - ((src & 0xff000000) >> 24);
52 uint32_t r =
53 ((dest & 0x00ff0000) >> 16) * alpha / 255 + ((src & 0x00ff0000) >> 16);
54 uint32_t g =
55 ((dest & 0x0000ff00) >> 8) * alpha / 255 + ((src & 0x0000ff00) >> 8);
56 uint32_t b = (dest & 0x000000ff) * alpha / 255 + (src & 0x000000ff);
57 return b + (g << 8) + (r << 16) + 0xff000000;
58}
59
60class FakeScreenCapturer : public DesktopCapturer {
61 public:
62 FakeScreenCapturer() {}
63
64 virtual void Start(Callback* callback) OVERRIDE {
65 callback_ = callback;
66 }
67
68 virtual void Capture(const DesktopRegion& region) OVERRIDE {
69 DesktopFrame* frame =
70 new BasicDesktopFrame(DesktopSize(kScreenWidth, kScreenHeight));
71 uint32_t* data = reinterpret_cast<uint32_t*>(frame->data());
72 for (int y = 0; y < kScreenHeight; ++y) {
73 for (int x = 0; x < kScreenWidth; ++x) {
74 *(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
75 }
76 }
77 callback_->OnCaptureCompleted(frame);
78 }
79
80 private:
81 Callback* callback_;
82};
83
84class FakeMouseMonitor : public MouseCursorMonitor {
85 public:
86 FakeMouseMonitor() : changed_(true) {}
87
88 void SetState(CursorState state, const DesktopVector& pos) {
89 state_ = state;
90 position_ = pos;
91 }
92
93 void SetHotspot(const DesktopVector& hotspot) {
94 if (!hotspot_.equals(hotspot))
95 changed_ = true;
96 hotspot_ = hotspot;
97 }
98
99 virtual void Init(Callback* callback, Mode mode) OVERRIDE {
100 callback_ = callback;
101 }
102
103 virtual void Capture() OVERRIDE {
104 if (changed_) {
105 scoped_ptr<DesktopFrame> image(
106 new BasicDesktopFrame(DesktopSize(kCursorWidth, kCursorHeight)));
107 uint32_t* data = reinterpret_cast<uint32_t*>(image->data());
108 memset(data, 0, image->stride() * kCursorHeight);
109
110 // Set four pixels near the hotspot and leave all other blank.
111 for (int y = 0; y < kTestCursorSize; ++y) {
112 for (int x = 0; x < kTestCursorSize; ++x) {
113 data[(hotspot_.y() + y) * kCursorWidth + (hotspot_.x() + x)] =
114 kTestCursorData[y][x];
115 }
116 }
117
118 callback_->OnMouseCursor(new MouseCursor(image.release(), hotspot_));
119 }
120
121 callback_->OnMouseCursorPosition(state_, position_);
122 }
123
124 private:
125 Callback* callback_;
126 CursorState state_;
127 DesktopVector position_;
128 DesktopVector hotspot_;
129 bool changed_;
130};
131
132void VerifyFrame(const DesktopFrame& frame,
133 MouseCursorMonitor::CursorState state,
134 const DesktopVector& pos) {
135 // Verify that all other pixels are set to their original values.
136 DesktopRect image_rect =
137 DesktopRect::MakeWH(kTestCursorSize, kTestCursorSize);
138 image_rect.Translate(pos);
139
140 for (int y = 0; y < kScreenHeight; ++y) {
141 for (int x = 0; x < kScreenWidth; ++x) {
142 DesktopVector p(x, y);
143 if (state == MouseCursorMonitor::INSIDE && image_rect.Contains(p)) {
144 EXPECT_EQ(BlendPixels(GetFakeFramePixelValue(p),
145 kTestCursorData[y - pos.y()][x - pos.x()]),
146 GetFramePixel(frame, p));
147 } else {
148 EXPECT_EQ(GetFakeFramePixelValue(p), GetFramePixel(frame, p));
149 }
150 }
151 }
152}
153
154class DesktopAndCursorComposerTest : public testing::Test,
155 public DesktopCapturer::Callback {
156 public:
157 DesktopAndCursorComposerTest()
158 : fake_cursor_(new FakeMouseMonitor()),
159 blender_(new FakeScreenCapturer(), fake_cursor_) {
160 }
161
162 // DesktopCapturer::Callback interface
163 virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE {
164 return NULL;
165 }
166
167 virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE {
168 frame_.reset(frame);
169 }
170
171 protected:
172 // Owned by |blender_|.
173 FakeMouseMonitor* fake_cursor_;
174 DesktopAndCursorComposer blender_;
175 scoped_ptr<DesktopFrame> frame_;
176};
177
178TEST_F(DesktopAndCursorComposerTest, Blend) {
179 struct {
180 int x, y;
181 int hotspot_x, hotspot_y;
182 bool inside;
183 } tests[] = {
184 {0, 0, 0, 0, true},
185 {50, 50, 0, 0, true},
186 {100, 50, 0, 0, true},
187 {50, 100, 0, 0, true},
188 {100, 100, 0, 0, true},
189 {0, 0, 2, 5, true},
190 {1, 1, 2, 5, true},
191 {50, 50, 2, 5, true},
192 {100, 100, 2, 5, true},
193 {0, 0, 5, 2, true},
194 {50, 50, 5, 2, true},
195 {100, 100, 5, 2, true},
196 {0, 0, 0, 0, false},
197 };
198
199 blender_.Start(this);
200
201 for (size_t i = 0; i < (sizeof(tests) / sizeof(tests[0])); ++i) {
202 SCOPED_TRACE(i);
203
204 DesktopVector hotspot(tests[i].hotspot_x, tests[i].hotspot_y);
205 fake_cursor_->SetHotspot(hotspot);
206
207 MouseCursorMonitor::CursorState state = tests[i].inside
208 ? MouseCursorMonitor::INSIDE
209 : MouseCursorMonitor::OUTSIDE;
210 DesktopVector pos(tests[i].x, tests[i].y);
211 fake_cursor_->SetState(state, pos);
212
213 blender_.Capture(DesktopRegion());
214
215 VerifyFrame(*frame_, state, pos);
216 }
217}
218
219} // namespace
220
221} // namespace webrtc