Index: remoting/host/differ.cc |
=================================================================== |
--- remoting/host/differ.cc (revision 0) |
+++ remoting/host/differ.cc (revision 0) |
@@ -0,0 +1,226 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "remoting/host/differ.h" |
+ |
+#include "base/logging.h" |
+ |
+namespace remoting { |
+ |
+Differ::Differ(int width, int height, int bpp) { |
+ // Dimensions of screen. |
+ width_ = width; |
+ height_ = height; |
+ bytes_per_pixel_ = bpp; |
+ bytes_per_row_ = width_ * bytes_per_pixel_; |
+ |
+ // Calc number of blocks (full and partial) required to cover entire image. |
+ // One additional row/column is added as a boundary on the right & bottom. |
+ diff_info_width_ = (width_ + kBlockSize - 1) / kBlockSize + 1; |
+ diff_info_height_ = (height_ + kBlockSize - 1) / kBlockSize + 1; |
+ diff_info_size_ = diff_info_width_ * diff_info_height_ * sizeof(DiffInfo); |
+ diff_info_.reset(new uint8[diff_info_size_]); |
+} |
+ |
+void Differ::CalcDirtyRects(const void* prev_buffer, const void* curr_buffer, |
+ DirtyRects* rects) { |
+ if (!rects) { |
+ return; |
+ } |
+ rects->clear(); |
+ |
+ if (!prev_buffer || !curr_buffer) { |
+ return; |
+ } |
+ |
+ // Identify all the blocks that contain changed pixels. |
+ MarkDirtyBlocks(prev_buffer, curr_buffer); |
+ |
+ // Now that we've identified the blocks that have changed, merge adjacent |
+ // blocks to minimize the number of rects that we return. |
+ MergeBlocks(rects); |
+} |
+ |
+void Differ::MarkDirtyBlocks(const void* prev_buffer, const void* curr_buffer) { |
+ memset(diff_info_.get(), 0, diff_info_size_); |
+ |
+ // Calc number of full blocks. |
+ int x_full_blocks = width_ / kBlockSize; |
+ int y_full_blocks = height_ / kBlockSize; |
+ |
+ // Calc size of partial blocks which may be present on right and bottom edge. |
+ int partial_column_width = width_ - (x_full_blocks * kBlockSize); |
+ int partial_row_height = height_ - (y_full_blocks * kBlockSize); |
+ |
+ // Offset from the start of one block-column to the next. |
+ int block_x_offset = bytes_per_pixel_ * kBlockSize; |
+ // Offset from the start of one block-row to the next. |
+ int block_y_stride = (width_ * bytes_per_pixel_) * kBlockSize; |
+ // Offset from the start of one diff_info row to the next. |
+ int diff_info_stride = diff_info_width_ * sizeof(DiffInfo); |
+ |
+ const uint8* prev_block_row_start = static_cast<const uint8*>(prev_buffer); |
+ const uint8* curr_block_row_start = static_cast<const uint8*>(curr_buffer); |
+ uint8* diff_info_row_start = static_cast<uint8*>(diff_info_.get()); |
+ |
+ for (int y = 0; y < y_full_blocks; y++) { |
+ const uint8* prev_block = prev_block_row_start; |
+ const uint8* curr_block = curr_block_row_start; |
+ uint8* diff_info = diff_info_row_start; |
+ |
+ for (int x = 0; x < x_full_blocks; x++) { |
+ DiffInfo diff = DiffBlock(prev_block, curr_block, bytes_per_row_); |
+ if (diff != 0) { |
+ // Mark this block as being modified so that it gets incorporated into |
+ // a dirty rect. |
+ *diff_info = diff; |
+ } |
+ prev_block += block_x_offset; |
+ curr_block += block_x_offset; |
+ diff_info += sizeof(DiffInfo); |
+ } |
+ |
+ // If there is a partial column at the end, handle it. |
+ if (partial_column_width != 0) { |
+ // TODO(garykac): Handle last partial column. |
+ } |
+ |
+ // Update pointers for next row. |
+ prev_block_row_start += block_y_stride; |
+ curr_block_row_start += block_y_stride; |
+ diff_info_row_start += diff_info_stride; |
+ } |
+ if (partial_row_height != 0) { |
+ // TODO(garykac): Handle last partial row. |
+ } |
+} |
+ |
+DiffInfo Differ::DiffBlock(const uint8* prev_buffer, const uint8* curr_buffer, |
+ int stride) { |
+ const uint8* prev_row_start = prev_buffer; |
+ const uint8* curr_row_start = curr_buffer; |
+ |
+ // Number of uint64s in each row of the block. |
+ // This must be an integral number. |
+ int int64s_per_row = (kBlockSize * bytes_per_pixel_) / sizeof(uint64); |
+ DCHECK(((kBlockSize * bytes_per_pixel_) % sizeof(uint64)) == 0); |
+ |
+ for (int y = 0; y < kBlockSize; y++) { |
+ const uint64* prev = reinterpret_cast<const uint64*>(prev_row_start); |
+ const uint64* curr = reinterpret_cast<const uint64*>(curr_row_start); |
+ |
+ // Check each row in uint64-sized chunks. |
+ // Note that this check may straddle multiple pixels. This is OK because |
+ // we're interested in identifying whether or not there was change - we |
+ // don't care what the actual change is. |
+ for (int x = 0; x < int64s_per_row; x++) { |
+ if (*prev++ != *curr++) { |
+ return 1; |
+ } |
+ } |
+ prev_row_start += stride; |
+ curr_row_start += stride; |
+ } |
+ return 0; |
+} |
+ |
+DiffInfo Differ::DiffPartialBlock(const uint8* prev_buffer, |
+ const uint8* curr_buffer, |
+ int stride, int width, int height) { |
+ const uint8* prev_row_start = prev_buffer; |
+ const uint8* curr_row_start = curr_buffer; |
+ |
+ for (int y = 0; y < height; y++) { |
+ const uint8* prev = prev_row_start; |
+ const uint8* curr = curr_row_start; |
+ for (int x = 0; x < width; x++) { |
+ for (int b = 0; b < bytes_per_pixel_; b++) { |
+ if (*prev++ != *curr++) { |
+ return 1; |
+ } |
+ } |
+ } |
+ prev_row_start += bytes_per_row_; |
+ curr_row_start += bytes_per_row_; |
+ } |
+ return 0; |
+} |
+ |
+void Differ::MergeBlocks(DirtyRects* rects) { |
+ DCHECK(rects); |
+ rects->clear(); |
+ |
+ uint8* diff_info_row_start = static_cast<uint8*>(diff_info_.get()); |
+ int diff_info_stride = diff_info_width_ * sizeof(DiffInfo); |
+ |
+ for (int y = 0; y < diff_info_height_; y++) { |
+ uint8* diff_info = diff_info_row_start; |
+ for (int x = 0; x < diff_info_width_; x++) { |
+ if (*diff_info != 0) { |
+ // We've found a modified block. Look at blocks to the right and below |
+ // to group this block with as many others as we can. |
+ int left = x * kBlockSize; |
+ int top = y * kBlockSize; |
+ int width = 1; |
+ int height = 1; |
+ *diff_info = 0; |
+ |
+ // Group with blocks to the right. |
+ // We can keep looking until we find an unchanged block because we |
+ // have a boundary block which is never marked as having diffs. |
+ uint8* right = diff_info + 1; |
+ while (*right) { |
+ *right++ = 0; |
+ width++; |
+ } |
+ |
+ // Group with blocks below. |
+ // The entire width of blocks that we matched above much match for |
+ // each row that we add. |
+ uint8* bottom = diff_info; |
+ bool found_new_row; |
+ do { |
+ found_new_row = true; |
+ bottom += diff_info_stride; |
+ right = bottom; |
+ for (int x2 = 0; x2 < width; x2++) { |
+ if (*right++ == 0) { |
+ found_new_row = false; |
+ } |
+ } |
+ |
+ if (found_new_row) { |
+ height++; |
+ |
+ // We need to go back and erase the diff markers so that we don't |
+ // try to add these blocks a second time. |
+ right = bottom; |
+ for (int x2 = 0; x2 < width; x2++) { |
+ *right++ = 0; |
+ } |
+ } |
+ } while (found_new_row); |
+ |
+ // Add rect to list of dirty rects. |
+ width *= kBlockSize; |
+ if (left + width > width_) { |
+ width = width_ - left; |
+ } |
+ height *= kBlockSize; |
+ if (top + height > height_) { |
+ height = height_ - top; |
+ } |
+ rects->push_back(gfx::Rect(left, top, width, height)); |
+ } |
+ |
+ // Increment to next block in this row. |
+ diff_info++; |
+ } |
+ |
+ // Go to start of next row. |
+ diff_info_row_start += diff_info_stride; |
+ } |
+} |
+ |
+} // namespace remoting |
Property changes on: remoting/host/differ.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |