Home | History | Annotate | Download | only in desktop_capture
      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