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 "testing/gmock/include/gmock/gmock.h" 12 #include "webrtc/base/scoped_ptr.h" 13 #include "webrtc/modules/desktop_capture/differ.h" 14 #include "webrtc/modules/desktop_capture/differ_block.h" 15 16 namespace webrtc { 17 18 // 96x96 screen gives a 4x4 grid of blocks. 19 const int kScreenWidth= 96; 20 const int kScreenHeight = 96; 21 22 // To test partial blocks, we need a width and height that are not multiples 23 // of 16 (or 32, depending on current block size). 24 const int kPartialScreenWidth = 70; 25 const int kPartialScreenHeight = 70; 26 27 class DifferTest : public testing::Test { 28 public: 29 DifferTest() { 30 } 31 32 protected: 33 void InitDiffer(int width, int height) { 34 width_ = width; 35 height_ = height; 36 bytes_per_pixel_ = kBytesPerPixel; 37 stride_ = (kBytesPerPixel * width); 38 buffer_size_ = width_ * height_ * bytes_per_pixel_; 39 40 differ_.reset(new Differ(width_, height_, bytes_per_pixel_, stride_)); 41 42 prev_.reset(new uint8_t[buffer_size_]); 43 memset(prev_.get(), 0, buffer_size_); 44 45 curr_.reset(new uint8_t[buffer_size_]); 46 memset(curr_.get(), 0, buffer_size_); 47 } 48 49 void ClearBuffer(uint8_t* buffer) { 50 memset(buffer, 0, buffer_size_); 51 } 52 53 // Here in DifferTest so that tests can access private methods of Differ. 54 void MarkDirtyBlocks(const uint8_t* prev_buffer, const uint8_t* curr_buffer) { 55 differ_->MarkDirtyBlocks(prev_buffer, curr_buffer); 56 } 57 58 void MergeBlocks(DesktopRegion* dirty) { 59 differ_->MergeBlocks(dirty); 60 } 61 62 // Convenience method to count rectangles in a region. 63 int RegionRectCount(const DesktopRegion& region) { 64 int count = 0; 65 for (DesktopRegion::Iterator iter(region); 66 !iter.IsAtEnd(); iter.Advance()) { 67 ++count; 68 } 69 return count; 70 } 71 72 // Convenience wrapper for Differ's DiffBlock that calculates the appropriate 73 // offset to the start of the desired block. 74 bool DiffBlock(int block_x, int block_y) { 75 // Offset from upper-left of buffer to upper-left of requested block. 76 int block_offset = ((block_y * stride_) + (block_x * bytes_per_pixel_)) 77 * kBlockSize; 78 return BlockDifference(prev_.get() + block_offset, 79 curr_.get() + block_offset, 80 stride_); 81 } 82 83 // Write the pixel |value| into the specified block in the |buffer|. 84 // This is a convenience wrapper around WritePixel(). 85 void WriteBlockPixel(uint8_t* buffer, int block_x, int block_y, 86 int pixel_x, int pixel_y, uint32_t value) { 87 WritePixel(buffer, (block_x * kBlockSize) + pixel_x, 88 (block_y * kBlockSize) + pixel_y, value); 89 } 90 91 // Write the test pixel |value| into the |buffer| at the specified |x|,|y| 92 // location. 93 // Only the low-order bytes from |value| are written (assuming little-endian). 94 // So, for |value| = 0xaabbccdd: 95 // If bytes_per_pixel = 4, then ddccbbaa will be written as the pixel value. 96 // If = 3, ddccbb 97 // If = 2, ddcc 98 // If = 1, dd 99 void WritePixel(uint8_t* buffer, int x, int y, uint32_t value) { 100 uint8_t* pixel = reinterpret_cast<uint8_t*>(&value); 101 buffer += (y * stride_) + (x * bytes_per_pixel_); 102 for (int b = bytes_per_pixel_ - 1; b >= 0; b--) { 103 *buffer++ = pixel[b]; 104 } 105 } 106 107 // DiffInfo utility routines. 108 // These are here so that we don't have to make each DifferText_Xxx_Test 109 // class a friend class to Differ. 110 111 // Clear out the entire |diff_info_| buffer. 112 void ClearDiffInfo() { 113 memset(differ_->diff_info_.get(), 0, differ_->diff_info_size_); 114 } 115 116 // Get the value in the |diff_info_| array at (x,y). 117 bool GetDiffInfo(int x, int y) { 118 bool* diff_info = differ_->diff_info_.get(); 119 return diff_info[(y * GetDiffInfoWidth()) + x]; 120 } 121 122 // Width of |diff_info_| array. 123 int GetDiffInfoWidth() { 124 return differ_->diff_info_width_; 125 } 126 127 // Height of |diff_info_| array. 128 int GetDiffInfoHeight() { 129 return differ_->diff_info_height_; 130 } 131 132 // Size of |diff_info_| array. 133 int GetDiffInfoSize() { 134 return differ_->diff_info_size_; 135 } 136 137 void SetDiffInfo(int x, int y, bool value) { 138 bool* diff_info = differ_->diff_info_.get(); 139 diff_info[(y * GetDiffInfoWidth()) + x] = value; 140 } 141 142 // Mark the range of blocks specified. 143 void MarkBlocks(int x_origin, int y_origin, int width, int height) { 144 for (int y = 0; y < height; y++) { 145 for (int x = 0; x < width; x++) { 146 SetDiffInfo(x_origin + x, y_origin + y, true); 147 } 148 } 149 } 150 151 // Verify that |region| contains a rectangle defined by |x|, |y|, |width| and 152 // |height|. 153 // |x|, |y|, |width| and |height| are specified in block (not pixel) units. 154 bool CheckDirtyRegionContainsRect(const DesktopRegion& region, 155 int x, int y, 156 int width, int height) { 157 DesktopRect r = 158 DesktopRect::MakeXYWH(x * kBlockSize, y * kBlockSize, 159 width * kBlockSize, height * kBlockSize); 160 for (DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { 161 if (i.rect().equals(r)) 162 return true; 163 } 164 return false; 165 } 166 167 // Mark the range of blocks specified and then verify that they are 168 // merged correctly. 169 // Only one rectangular region of blocks can be checked with this routine. 170 bool MarkBlocksAndCheckMerge(int x_origin, int y_origin, 171 int width, int height) { 172 ClearDiffInfo(); 173 MarkBlocks(x_origin, y_origin, width, height); 174 175 DesktopRegion dirty; 176 MergeBlocks(&dirty); 177 178 179 DesktopRect expected_rect = DesktopRect::MakeXYWH( 180 x_origin * kBlockSize, y_origin * kBlockSize, 181 width * kBlockSize, height * kBlockSize); 182 183 // Verify that the region contains expected_rect and it's the only 184 // rectangle. 185 DesktopRegion::Iterator it(dirty); 186 return !it.IsAtEnd() && expected_rect.equals(it.rect()) && 187 (it.Advance(), it.IsAtEnd()); 188 } 189 190 // The differ class we're testing. 191 rtc::scoped_ptr<Differ> differ_; 192 193 // Screen/buffer info. 194 int width_; 195 int height_; 196 int bytes_per_pixel_; 197 int stride_; 198 199 // Size of each screen buffer. 200 int buffer_size_; 201 202 // Previous and current screen buffers. 203 rtc::scoped_ptr<uint8_t[]> prev_; 204 rtc::scoped_ptr<uint8_t[]> curr_; 205 206 private: 207 RTC_DISALLOW_COPY_AND_ASSIGN(DifferTest); 208 }; 209 210 TEST_F(DifferTest, Setup) { 211 InitDiffer(kScreenWidth, kScreenHeight); 212 // 96x96 pixels results in 3x3 array. Add 1 to each dimension as boundary. 213 // +---+---+---+---+ 214 // | o | o | o | _ | 215 // +---+---+---+---+ o = blocks mapped to screen pixels 216 // | o | o | o | _ | 217 // +---+---+---+---+ _ = boundary blocks 218 // | o | o | o | _ | 219 // +---+---+---+---+ 220 // | _ | _ | _ | _ | 221 // +---+---+---+---+ 222 EXPECT_EQ(4, GetDiffInfoWidth()); 223 EXPECT_EQ(4, GetDiffInfoHeight()); 224 EXPECT_EQ(16, GetDiffInfoSize()); 225 } 226 227 TEST_F(DifferTest, MarkDirtyBlocks_All) { 228 InitDiffer(kScreenWidth, kScreenHeight); 229 ClearDiffInfo(); 230 231 // Update a pixel in each block. 232 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 233 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 234 WriteBlockPixel(curr_.get(), x, y, 10, 10, 0xff00ff); 235 } 236 } 237 238 MarkDirtyBlocks(prev_.get(), curr_.get()); 239 240 // Make sure each block is marked as dirty. 241 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 242 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 243 EXPECT_TRUE(GetDiffInfo(x, y)) 244 << "when x = " << x << ", and y = " << y; 245 } 246 } 247 } 248 249 TEST_F(DifferTest, MarkDirtyBlocks_Sampling) { 250 InitDiffer(kScreenWidth, kScreenHeight); 251 ClearDiffInfo(); 252 253 // Update some pixels in image. 254 WriteBlockPixel(curr_.get(), 1, 0, 10, 10, 0xff00ff); 255 WriteBlockPixel(curr_.get(), 2, 1, 10, 10, 0xff00ff); 256 WriteBlockPixel(curr_.get(), 0, 2, 10, 10, 0xff00ff); 257 258 MarkDirtyBlocks(prev_.get(), curr_.get()); 259 260 // Make sure corresponding blocks are updated. 261 EXPECT_FALSE(GetDiffInfo(0, 0)); 262 EXPECT_FALSE(GetDiffInfo(0, 1)); 263 EXPECT_TRUE(GetDiffInfo(0, 2)); 264 EXPECT_TRUE(GetDiffInfo(1, 0)); 265 EXPECT_FALSE(GetDiffInfo(1, 1)); 266 EXPECT_FALSE(GetDiffInfo(1, 2)); 267 EXPECT_FALSE(GetDiffInfo(2, 0)); 268 EXPECT_TRUE(GetDiffInfo(2, 1)); 269 EXPECT_FALSE(GetDiffInfo(2, 2)); 270 } 271 272 TEST_F(DifferTest, DiffBlock) { 273 InitDiffer(kScreenWidth, kScreenHeight); 274 275 // Verify no differences at start. 276 EXPECT_FALSE(DiffBlock(0, 0)); 277 EXPECT_FALSE(DiffBlock(1, 1)); 278 279 // Write new data into the 4 corners of the middle block and verify that 280 // neighboring blocks are not affected. 281 int max = kBlockSize - 1; 282 WriteBlockPixel(curr_.get(), 1, 1, 0, 0, 0xffffff); 283 WriteBlockPixel(curr_.get(), 1, 1, 0, max, 0xffffff); 284 WriteBlockPixel(curr_.get(), 1, 1, max, 0, 0xffffff); 285 WriteBlockPixel(curr_.get(), 1, 1, max, max, 0xffffff); 286 EXPECT_FALSE(DiffBlock(0, 0)); 287 EXPECT_FALSE(DiffBlock(0, 1)); 288 EXPECT_FALSE(DiffBlock(0, 2)); 289 EXPECT_FALSE(DiffBlock(1, 0)); 290 EXPECT_TRUE(DiffBlock(1, 1)); // Only this block should change. 291 EXPECT_FALSE(DiffBlock(1, 2)); 292 EXPECT_FALSE(DiffBlock(2, 0)); 293 EXPECT_FALSE(DiffBlock(2, 1)); 294 EXPECT_FALSE(DiffBlock(2, 2)); 295 } 296 297 TEST_F(DifferTest, Partial_Setup) { 298 InitDiffer(kPartialScreenWidth, kPartialScreenHeight); 299 // 70x70 pixels results in 3x3 array: 2x2 full blocks + partials around 300 // the edge. One more is added to each dimension as a boundary. 301 // +---+---+---+---+ 302 // | o | o | + | _ | 303 // +---+---+---+---+ o = blocks mapped to screen pixels 304 // | o | o | + | _ | 305 // +---+---+---+---+ + = partial blocks (top/left mapped to screen pixels) 306 // | + | + | + | _ | 307 // +---+---+---+---+ _ = boundary blocks 308 // | _ | _ | _ | _ | 309 // +---+---+---+---+ 310 EXPECT_EQ(4, GetDiffInfoWidth()); 311 EXPECT_EQ(4, GetDiffInfoHeight()); 312 EXPECT_EQ(16, GetDiffInfoSize()); 313 } 314 315 TEST_F(DifferTest, Partial_FirstPixel) { 316 InitDiffer(kPartialScreenWidth, kPartialScreenHeight); 317 ClearDiffInfo(); 318 319 // Update the first pixel in each block. 320 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 321 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 322 WriteBlockPixel(curr_.get(), x, y, 0, 0, 0xff00ff); 323 } 324 } 325 326 MarkDirtyBlocks(prev_.get(), curr_.get()); 327 328 // Make sure each block is marked as dirty. 329 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 330 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 331 EXPECT_TRUE(GetDiffInfo(x, y)) 332 << "when x = " << x << ", and y = " << y; 333 } 334 } 335 } 336 337 TEST_F(DifferTest, Partial_BorderPixel) { 338 InitDiffer(kPartialScreenWidth, kPartialScreenHeight); 339 ClearDiffInfo(); 340 341 // Update the right/bottom border pixels. 342 for (int y = 0; y < height_; y++) { 343 WritePixel(curr_.get(), width_ - 1, y, 0xff00ff); 344 } 345 for (int x = 0; x < width_; x++) { 346 WritePixel(curr_.get(), x, height_ - 1, 0xff00ff); 347 } 348 349 MarkDirtyBlocks(prev_.get(), curr_.get()); 350 351 // Make sure last (partial) block in each row/column is marked as dirty. 352 int x_last = GetDiffInfoWidth() - 2; 353 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 354 EXPECT_TRUE(GetDiffInfo(x_last, y)) 355 << "when x = " << x_last << ", and y = " << y; 356 } 357 int y_last = GetDiffInfoHeight() - 2; 358 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 359 EXPECT_TRUE(GetDiffInfo(x, y_last)) 360 << "when x = " << x << ", and y = " << y_last; 361 } 362 // All other blocks are clean. 363 for (int y = 0; y < GetDiffInfoHeight() - 2; y++) { 364 for (int x = 0; x < GetDiffInfoWidth() - 2; x++) { 365 EXPECT_FALSE(GetDiffInfo(x, y)) << "when x = " << x << ", and y = " << y; 366 } 367 } 368 } 369 370 TEST_F(DifferTest, MergeBlocks_Empty) { 371 InitDiffer(kScreenWidth, kScreenHeight); 372 373 // No blocks marked: 374 // +---+---+---+---+ 375 // | | | | _ | 376 // +---+---+---+---+ 377 // | | | | _ | 378 // +---+---+---+---+ 379 // | | | | _ | 380 // +---+---+---+---+ 381 // | _ | _ | _ | _ | 382 // +---+---+---+---+ 383 ClearDiffInfo(); 384 385 DesktopRegion dirty; 386 MergeBlocks(&dirty); 387 388 EXPECT_TRUE(dirty.is_empty()); 389 } 390 391 TEST_F(DifferTest, MergeBlocks_SingleBlock) { 392 InitDiffer(kScreenWidth, kScreenHeight); 393 // Mark a single block and make sure that there is a single merged 394 // rect with the correct bounds. 395 for (int y = 0; y < GetDiffInfoHeight() - 1; y++) { 396 for (int x = 0; x < GetDiffInfoWidth() - 1; x++) { 397 ASSERT_TRUE(MarkBlocksAndCheckMerge(x, y, 1, 1)) << "x: " << x 398 << "y: " << y; 399 } 400 } 401 } 402 403 TEST_F(DifferTest, MergeBlocks_BlockRow) { 404 InitDiffer(kScreenWidth, kScreenHeight); 405 406 // +---+---+---+---+ 407 // | X | X | | _ | 408 // +---+---+---+---+ 409 // | | | | _ | 410 // +---+---+---+---+ 411 // | | | | _ | 412 // +---+---+---+---+ 413 // | _ | _ | _ | _ | 414 // +---+---+---+---+ 415 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 0, 2, 1)); 416 417 // +---+---+---+---+ 418 // | | | | _ | 419 // +---+---+---+---+ 420 // | X | X | X | _ | 421 // +---+---+---+---+ 422 // | | | | _ | 423 // +---+---+---+---+ 424 // | _ | _ | _ | _ | 425 // +---+---+---+---+ 426 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 1, 3, 1)); 427 428 // +---+---+---+---+ 429 // | | | | _ | 430 // +---+---+---+---+ 431 // | | | | _ | 432 // +---+---+---+---+ 433 // | | X | X | _ | 434 // +---+---+---+---+ 435 // | _ | _ | _ | _ | 436 // +---+---+---+---+ 437 ASSERT_TRUE(MarkBlocksAndCheckMerge(1, 2, 2, 1)); 438 } 439 440 TEST_F(DifferTest, MergeBlocks_BlockColumn) { 441 InitDiffer(kScreenWidth, kScreenHeight); 442 443 // +---+---+---+---+ 444 // | X | | | _ | 445 // +---+---+---+---+ 446 // | X | | | _ | 447 // +---+---+---+---+ 448 // | | | | _ | 449 // +---+---+---+---+ 450 // | _ | _ | _ | _ | 451 // +---+---+---+---+ 452 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 0, 1, 2)); 453 454 // +---+---+---+---+ 455 // | | | | _ | 456 // +---+---+---+---+ 457 // | | X | | _ | 458 // +---+---+---+---+ 459 // | | X | | _ | 460 // +---+---+---+---+ 461 // | _ | _ | _ | _ | 462 // +---+---+---+---+ 463 ASSERT_TRUE(MarkBlocksAndCheckMerge(1, 1, 1, 2)); 464 465 // +---+---+---+---+ 466 // | | | X | _ | 467 // +---+---+---+---+ 468 // | | | X | _ | 469 // +---+---+---+---+ 470 // | | | X | _ | 471 // +---+---+---+---+ 472 // | _ | _ | _ | _ | 473 // +---+---+---+---+ 474 ASSERT_TRUE(MarkBlocksAndCheckMerge(2, 0, 1, 3)); 475 } 476 477 TEST_F(DifferTest, MergeBlocks_BlockRect) { 478 InitDiffer(kScreenWidth, kScreenHeight); 479 480 // +---+---+---+---+ 481 // | X | X | | _ | 482 // +---+---+---+---+ 483 // | X | X | | _ | 484 // +---+---+---+---+ 485 // | | | | _ | 486 // +---+---+---+---+ 487 // | _ | _ | _ | _ | 488 // +---+---+---+---+ 489 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 0, 2, 2)); 490 491 // +---+---+---+---+ 492 // | | | | _ | 493 // +---+---+---+---+ 494 // | | X | X | _ | 495 // +---+---+---+---+ 496 // | | X | X | _ | 497 // +---+---+---+---+ 498 // | _ | _ | _ | _ | 499 // +---+---+---+---+ 500 ASSERT_TRUE(MarkBlocksAndCheckMerge(1, 1, 2, 2)); 501 502 // +---+---+---+---+ 503 // | | X | X | _ | 504 // +---+---+---+---+ 505 // | | X | X | _ | 506 // +---+---+---+---+ 507 // | | X | X | _ | 508 // +---+---+---+---+ 509 // | _ | _ | _ | _ | 510 // +---+---+---+---+ 511 ASSERT_TRUE(MarkBlocksAndCheckMerge(1, 0, 2, 3)); 512 513 // +---+---+---+---+ 514 // | | | | _ | 515 // +---+---+---+---+ 516 // | X | X | X | _ | 517 // +---+---+---+---+ 518 // | X | X | X | _ | 519 // +---+---+---+---+ 520 // | _ | _ | _ | _ | 521 // +---+---+---+---+ 522 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 1, 3, 2)); 523 524 // +---+---+---+---+ 525 // | X | X | X | _ | 526 // +---+---+---+---+ 527 // | X | X | X | _ | 528 // +---+---+---+---+ 529 // | X | X | X | _ | 530 // +---+---+---+---+ 531 // | _ | _ | _ | _ | 532 // +---+---+---+---+ 533 ASSERT_TRUE(MarkBlocksAndCheckMerge(0, 0, 3, 3)); 534 } 535 536 // This tests marked regions that require more than 1 single dirty rect. 537 // The exact rects returned depend on the current implementation, so these 538 // may need to be updated if we modify how we merge blocks. 539 TEST_F(DifferTest, MergeBlocks_MultiRect) { 540 InitDiffer(kScreenWidth, kScreenHeight); 541 DesktopRegion dirty; 542 543 // +---+---+---+---+ +---+---+---+ 544 // | | X | | _ | | | 0 | | 545 // +---+---+---+---+ +---+---+---+ 546 // | X | | | _ | | 1 | | | 547 // +---+---+---+---+ => +---+---+---+ 548 // | | | X | _ | | | | 2 | 549 // +---+---+---+---+ +---+---+---+ 550 // | _ | _ | _ | _ | 551 // +---+---+---+---+ 552 ClearDiffInfo(); 553 MarkBlocks(1, 0, 1, 1); 554 MarkBlocks(0, 1, 1, 1); 555 MarkBlocks(2, 2, 1, 1); 556 557 dirty.Clear(); 558 MergeBlocks(&dirty); 559 560 ASSERT_EQ(3, RegionRectCount(dirty)); 561 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 1, 0, 1, 1)); 562 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 1, 1, 1)); 563 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 2, 2, 1, 1)); 564 565 // +---+---+---+---+ +---+---+---+ 566 // | | | X | _ | | | | 0 | 567 // +---+---+---+---+ +---+---+---+ 568 // | X | X | X | _ | | 1 1 1 | 569 // +---+---+---+---+ => + + 570 // | X | X | X | _ | | 1 1 1 | 571 // +---+---+---+---+ +---+---+---+ 572 // | _ | _ | _ | _ | 573 // +---+---+---+---+ 574 ClearDiffInfo(); 575 MarkBlocks(2, 0, 1, 1); 576 MarkBlocks(0, 1, 3, 2); 577 578 dirty.Clear(); 579 MergeBlocks(&dirty); 580 581 ASSERT_EQ(2, RegionRectCount(dirty)); 582 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 2, 0, 1, 1)); 583 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 1, 3, 2)); 584 585 // +---+---+---+---+ +---+---+---+ 586 // | | | | _ | | | | | 587 // +---+---+---+---+ +---+---+---+ 588 // | X | | X | _ | | 0 | | 1 | 589 // +---+---+---+---+ => +---+---+---+ 590 // | X | X | X | _ | | 2 2 2 | 591 // +---+---+---+---+ +---+---+---+ 592 // | _ | _ | _ | _ | 593 // +---+---+---+---+ 594 ClearDiffInfo(); 595 MarkBlocks(0, 1, 1, 1); 596 MarkBlocks(2, 1, 1, 1); 597 MarkBlocks(0, 2, 3, 1); 598 599 dirty.Clear(); 600 MergeBlocks(&dirty); 601 602 ASSERT_EQ(3, RegionRectCount(dirty)); 603 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 1, 1, 1)); 604 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 2, 1, 1, 1)); 605 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 2, 3, 1)); 606 607 // +---+---+---+---+ +---+---+---+ 608 // | X | X | X | _ | | 0 0 0 | 609 // +---+---+---+---+ +---+---+---+ 610 // | X | | X | _ | | 1 | | 2 | 611 // +---+---+---+---+ => +---+---+---+ 612 // | X | X | X | _ | | 3 3 3 | 613 // +---+---+---+---+ +---+---+---+ 614 // | _ | _ | _ | _ | 615 // +---+---+---+---+ 616 ClearDiffInfo(); 617 MarkBlocks(0, 0, 3, 1); 618 MarkBlocks(0, 1, 1, 1); 619 MarkBlocks(2, 1, 1, 1); 620 MarkBlocks(0, 2, 3, 1); 621 622 dirty.Clear(); 623 MergeBlocks(&dirty); 624 625 ASSERT_EQ(4, RegionRectCount(dirty)); 626 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 0, 3, 1)); 627 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 1, 1, 1)); 628 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 2, 1, 1, 1)); 629 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 2, 3, 1)); 630 631 // +---+---+---+---+ +---+---+---+ 632 // | X | X | | _ | | 0 0 | | 633 // +---+---+---+---+ + +---+ 634 // | X | X | | _ | | 0 0 | | 635 // +---+---+---+---+ => +---+---+---+ 636 // | | X | | _ | | | 1 | | 637 // +---+---+---+---+ +---+---+---+ 638 // | _ | _ | _ | _ | 639 // +---+---+---+---+ 640 ClearDiffInfo(); 641 MarkBlocks(0, 0, 2, 2); 642 MarkBlocks(1, 2, 1, 1); 643 644 dirty.Clear(); 645 MergeBlocks(&dirty); 646 647 ASSERT_EQ(2, RegionRectCount(dirty)); 648 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 0, 0, 2, 2)); 649 ASSERT_TRUE(CheckDirtyRegionContainsRect(dirty, 1, 2, 1, 1)); 650 } 651 652 } // namespace webrtc 653