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