| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "remoting/host/differ.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "remoting/host/differ_block.h" | |
| 9 | |
| 10 namespace remoting { | |
| 11 | |
| 12 Differ::Differ(int width, int height, int bpp, int stride) { | |
| 13 // Dimensions of screen. | |
| 14 width_ = width; | |
| 15 height_ = height; | |
| 16 bytes_per_pixel_ = bpp; | |
| 17 bytes_per_row_ = stride; | |
| 18 | |
| 19 // Calc number of blocks (full and partial) required to cover entire image. | |
| 20 // One additional row/column is added as a boundary on the right & bottom. | |
| 21 diff_info_width_ = ((width_ + kBlockSize - 1) / kBlockSize) + 1; | |
| 22 diff_info_height_ = ((height_ + kBlockSize - 1) / kBlockSize) + 1; | |
| 23 diff_info_size_ = diff_info_width_ * diff_info_height_ * sizeof(DiffInfo); | |
| 24 diff_info_.reset(new DiffInfo[diff_info_size_]); | |
| 25 } | |
| 26 | |
| 27 Differ::~Differ() {} | |
| 28 | |
| 29 void Differ::CalcDirtyRegion(const void* prev_buffer, const void* curr_buffer, | |
| 30 SkRegion* region) { | |
| 31 if (!region) { | |
| 32 return; | |
| 33 } | |
| 34 region->setEmpty(); | |
| 35 | |
| 36 if (!prev_buffer || !curr_buffer) { | |
| 37 return; | |
| 38 } | |
| 39 | |
| 40 // Identify all the blocks that contain changed pixels. | |
| 41 MarkDirtyBlocks(prev_buffer, curr_buffer); | |
| 42 | |
| 43 // Now that we've identified the blocks that have changed, merge adjacent | |
| 44 // blocks to minimize the number of rects that we return. | |
| 45 MergeBlocks(region); | |
| 46 } | |
| 47 | |
| 48 void Differ::MarkDirtyBlocks(const void* prev_buffer, const void* curr_buffer) { | |
| 49 memset(diff_info_.get(), 0, diff_info_size_); | |
| 50 | |
| 51 // Calc number of full blocks. | |
| 52 int x_full_blocks = width_ / kBlockSize; | |
| 53 int y_full_blocks = height_ / kBlockSize; | |
| 54 | |
| 55 // Calc size of partial blocks which may be present on right and bottom edge. | |
| 56 int partial_column_width = width_ - (x_full_blocks * kBlockSize); | |
| 57 int partial_row_height = height_ - (y_full_blocks * kBlockSize); | |
| 58 | |
| 59 // Offset from the start of one block-column to the next. | |
| 60 int block_x_offset = bytes_per_pixel_ * kBlockSize; | |
| 61 // Offset from the start of one block-row to the next. | |
| 62 int block_y_stride = (width_ * bytes_per_pixel_) * kBlockSize; | |
| 63 // Offset from the start of one diff_info row to the next. | |
| 64 int diff_info_stride = diff_info_width_ * sizeof(DiffInfo); | |
| 65 | |
| 66 const uint8* prev_block_row_start = static_cast<const uint8*>(prev_buffer); | |
| 67 const uint8* curr_block_row_start = static_cast<const uint8*>(curr_buffer); | |
| 68 DiffInfo* diff_info_row_start = static_cast<DiffInfo*>(diff_info_.get()); | |
| 69 | |
| 70 for (int y = 0; y < y_full_blocks; y++) { | |
| 71 const uint8* prev_block = prev_block_row_start; | |
| 72 const uint8* curr_block = curr_block_row_start; | |
| 73 DiffInfo* diff_info = diff_info_row_start; | |
| 74 | |
| 75 for (int x = 0; x < x_full_blocks; x++) { | |
| 76 // Mark this block as being modified so that it gets incorporated into | |
| 77 // a dirty rect. | |
| 78 *diff_info = BlockDifference(prev_block, curr_block, bytes_per_row_); | |
| 79 prev_block += block_x_offset; | |
| 80 curr_block += block_x_offset; | |
| 81 diff_info += sizeof(DiffInfo); | |
| 82 } | |
| 83 | |
| 84 // If there is a partial column at the end, handle it. | |
| 85 // This condition should rarely, if ever, occur. | |
| 86 if (partial_column_width != 0) { | |
| 87 *diff_info = DiffPartialBlock(prev_block, curr_block, bytes_per_row_, | |
| 88 partial_column_width, kBlockSize); | |
| 89 diff_info += sizeof(DiffInfo); | |
| 90 } | |
| 91 | |
| 92 // Update pointers for next row. | |
| 93 prev_block_row_start += block_y_stride; | |
| 94 curr_block_row_start += block_y_stride; | |
| 95 diff_info_row_start += diff_info_stride; | |
| 96 } | |
| 97 | |
| 98 // If the screen height is not a multiple of the block size, then this | |
| 99 // handles the last partial row. This situation is far more common than the | |
| 100 // 'partial column' case. | |
| 101 if (partial_row_height != 0) { | |
| 102 const uint8* prev_block = prev_block_row_start; | |
| 103 const uint8* curr_block = curr_block_row_start; | |
| 104 DiffInfo* diff_info = diff_info_row_start; | |
| 105 for (int x = 0; x < x_full_blocks; x++) { | |
| 106 *diff_info = DiffPartialBlock(prev_block, curr_block, | |
| 107 bytes_per_row_, | |
| 108 kBlockSize, partial_row_height); | |
| 109 prev_block += block_x_offset; | |
| 110 curr_block += block_x_offset; | |
| 111 diff_info += sizeof(DiffInfo); | |
| 112 } | |
| 113 if (partial_column_width != 0) { | |
| 114 *diff_info = DiffPartialBlock(prev_block, curr_block, bytes_per_row_, | |
| 115 partial_column_width, partial_row_height); | |
| 116 diff_info += sizeof(DiffInfo); | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 DiffInfo Differ::DiffPartialBlock(const uint8* prev_buffer, | |
| 122 const uint8* curr_buffer, | |
| 123 int stride, int width, int height) { | |
| 124 int width_bytes = width * bytes_per_pixel_; | |
| 125 for (int y = 0; y < height; y++) { | |
| 126 if (memcmp(prev_buffer, curr_buffer, width_bytes) != 0) | |
| 127 return 1; | |
| 128 prev_buffer += bytes_per_row_; | |
| 129 curr_buffer += bytes_per_row_; | |
| 130 } | |
| 131 return 0; | |
| 132 } | |
| 133 | |
| 134 void Differ::MergeBlocks(SkRegion* region) { | |
| 135 DCHECK(region); | |
| 136 region->setEmpty(); | |
| 137 | |
| 138 uint8* diff_info_row_start = static_cast<uint8*>(diff_info_.get()); | |
| 139 int diff_info_stride = diff_info_width_ * sizeof(DiffInfo); | |
| 140 | |
| 141 for (int y = 0; y < diff_info_height_; y++) { | |
| 142 uint8* diff_info = diff_info_row_start; | |
| 143 for (int x = 0; x < diff_info_width_; x++) { | |
| 144 if (*diff_info != 0) { | |
| 145 // We've found a modified block. Look at blocks to the right and below | |
| 146 // to group this block with as many others as we can. | |
| 147 int left = x * kBlockSize; | |
| 148 int top = y * kBlockSize; | |
| 149 int width = 1; | |
| 150 int height = 1; | |
| 151 *diff_info = 0; | |
| 152 | |
| 153 // Group with blocks to the right. | |
| 154 // We can keep looking until we find an unchanged block because we | |
| 155 // have a boundary block which is never marked as having diffs. | |
| 156 uint8* right = diff_info + 1; | |
| 157 while (*right) { | |
| 158 *right++ = 0; | |
| 159 width++; | |
| 160 } | |
| 161 | |
| 162 // Group with blocks below. | |
| 163 // The entire width of blocks that we matched above much match for | |
| 164 // each row that we add. | |
| 165 uint8* bottom = diff_info; | |
| 166 bool found_new_row; | |
| 167 do { | |
| 168 found_new_row = true; | |
| 169 bottom += diff_info_stride; | |
| 170 right = bottom; | |
| 171 for (int x2 = 0; x2 < width; x2++) { | |
| 172 if (*right++ == 0) { | |
| 173 found_new_row = false; | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 if (found_new_row) { | |
| 178 height++; | |
| 179 | |
| 180 // We need to go back and erase the diff markers so that we don't | |
| 181 // try to add these blocks a second time. | |
| 182 right = bottom; | |
| 183 for (int x2 = 0; x2 < width; x2++) { | |
| 184 *right++ = 0; | |
| 185 } | |
| 186 } | |
| 187 } while (found_new_row); | |
| 188 | |
| 189 // Add rect to list of dirty rects. | |
| 190 width *= kBlockSize; | |
| 191 if (left + width > width_) { | |
| 192 width = width_ - left; | |
| 193 } | |
| 194 height *= kBlockSize; | |
| 195 if (top + height > height_) { | |
| 196 height = height_ - top; | |
| 197 } | |
| 198 region->op(SkIRect::MakeXYWH(left, top, width, height), | |
| 199 SkRegion::kUnion_Op); | |
| 200 } | |
| 201 | |
| 202 // Increment to next block in this row. | |
| 203 diff_info++; | |
| 204 } | |
| 205 | |
| 206 // Go to start of next row. | |
| 207 diff_info_row_start += diff_info_stride; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 } // namespace remoting | |
| OLD | NEW |