1 /* 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 "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" 21 22 namespace webrtc { 23 24 namespace { 25 26 const int kScreenWidth = 100; 27 const int kScreenHeight = 100; 28 const int kCursorWidth = 10; 29 const int kCursorHeight = 10; 30 31 const int kTestCursorSize = 3; 32 const uint32_t kTestCursorData[kTestCursorSize][kTestCursorSize] = { 33 { 0xffffffff, 0x99990000, 0xaa222222, }, 34 { 0x88008800, 0xaa0000aa, 0xaa333333, }, 35 { 0x00000000, 0xaa0000aa, 0xaa333333, }, 36 }; 37 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; 43 } 44 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); 48 } 49 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); 53 uint32_t r = 54 ((dest & 0x00ff0000) >> 16) * alpha / 255 + ((src & 0x00ff0000) >> 16); 55 uint32_t g = 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; 59 } 60 61 DesktopFrame* CreateTestFrame() { 62 DesktopFrame* frame = 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)); 68 } 69 } 70 return frame; 71 } 72 73 class FakeScreenCapturer : public DesktopCapturer { 74 public: 75 FakeScreenCapturer() {} 76 77 virtual void Start(Callback* callback) OVERRIDE { 78 callback_ = callback; 79 } 80 81 virtual void Capture(const DesktopRegion& region) OVERRIDE { 82 callback_->OnCaptureCompleted(next_frame_.release()); 83 } 84 85 void SetNextFrame(DesktopFrame* next_frame) { 86 next_frame_.reset(next_frame); 87 } 88 89 private: 90 Callback* callback_; 91 92 scoped_ptr<DesktopFrame> next_frame_; 93 }; 94 95 class FakeMouseMonitor : public MouseCursorMonitor { 96 public: 97 FakeMouseMonitor() : changed_(true) {} 98 99 void SetState(CursorState state, const DesktopVector& pos) { 100 state_ = state; 101 position_ = pos; 102 } 103 104 void SetHotspot(const DesktopVector& hotspot) { 105 if (!hotspot_.equals(hotspot)) 106 changed_ = true; 107 hotspot_ = hotspot; 108 } 109 110 virtual void Init(Callback* callback, Mode mode) OVERRIDE { 111 callback_ = callback; 112 } 113 114 virtual void Capture() OVERRIDE { 115 if (changed_) { 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); 120 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]; 126 } 127 } 128 129 callback_->OnMouseCursor(new MouseCursor(image.release(), hotspot_)); 130 } 131 132 callback_->OnMouseCursorPosition(state_, position_); 133 } 134 135 private: 136 Callback* callback_; 137 CursorState state_; 138 DesktopVector position_; 139 DesktopVector hotspot_; 140 bool changed_; 141 }; 142 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); 150 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)); 158 } else { 159 EXPECT_EQ(GetFakeFramePixelValue(p), GetFramePixel(frame, p)); 160 } 161 } 162 } 163 } 164 165 class DesktopAndCursorComposerTest : public testing::Test, 166 public DesktopCapturer::Callback { 167 public: 168 DesktopAndCursorComposerTest() 169 : fake_screen_(new FakeScreenCapturer()), 170 fake_cursor_(new FakeMouseMonitor()), 171 blender_(fake_screen_, fake_cursor_) { 172 } 173 174 // DesktopCapturer::Callback interface 175 virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE { 176 return NULL; 177 } 178 179 virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE { 180 frame_.reset(frame); 181 } 182 183 protected: 184 // Owned by |blender_|. 185 FakeScreenCapturer* fake_screen_; 186 FakeMouseMonitor* fake_cursor_; 187 188 DesktopAndCursorComposer blender_; 189 scoped_ptr<DesktopFrame> frame_; 190 }; 191 192 // Verify DesktopAndCursorComposer can handle the case when the screen capturer 193 // fails. 194 TEST_F(DesktopAndCursorComposerTest, Error) { 195 blender_.Start(this); 196 197 fake_cursor_->SetHotspot(DesktopVector()); 198 fake_cursor_->SetState(MouseCursorMonitor::INSIDE, DesktopVector()); 199 fake_screen_->SetNextFrame(NULL); 200 201 blender_.Capture(DesktopRegion()); 202 203 EXPECT_EQ(frame_, static_cast<DesktopFrame*>(NULL)); 204 } 205 206 TEST_F(DesktopAndCursorComposerTest, Blend) { 207 struct { 208 int x, y; 209 int hotspot_x, hotspot_y; 210 bool inside; 211 } tests[] = { 212 {0, 0, 0, 0, true}, 213 {50, 50, 0, 0, true}, 214 {100, 50, 0, 0, true}, 215 {50, 100, 0, 0, true}, 216 {100, 100, 0, 0, true}, 217 {0, 0, 2, 5, true}, 218 {1, 1, 2, 5, true}, 219 {50, 50, 2, 5, true}, 220 {100, 100, 2, 5, true}, 221 {0, 0, 5, 2, true}, 222 {50, 50, 5, 2, true}, 223 {100, 100, 5, 2, true}, 224 {0, 0, 0, 0, false}, 225 }; 226 227 blender_.Start(this); 228 229 for (size_t i = 0; i < (sizeof(tests) / sizeof(tests[0])); ++i) { 230 SCOPED_TRACE(i); 231 232 DesktopVector hotspot(tests[i].hotspot_x, tests[i].hotspot_y); 233 fake_cursor_->SetHotspot(hotspot); 234 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); 240 241 scoped_ptr<SharedDesktopFrame> frame( 242 SharedDesktopFrame::Wrap(CreateTestFrame())); 243 fake_screen_->SetNextFrame(frame->Share()); 244 245 blender_.Capture(DesktopRegion()); 246 247 VerifyFrame(*frame_, state, pos); 248 249 // Verify that the cursor is erased before the frame buffer is returned to 250 // the screen capturer. 251 frame_.reset(); 252 VerifyFrame(*frame, MouseCursorMonitor::OUTSIDE, DesktopVector()); 253 } 254 } 255 256 } // namespace 257 258 } // namespace webrtc 259