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