
This prevents the compiler from warning about possible type truncations at the callsites. BUG=chromium:81439 TEST=none R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/41059004 Cr-Commit-Position: refs/heads/master@{#9211}
212 lines
7.3 KiB
C++
212 lines
7.3 KiB
C++
/*
|
|
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "webrtc/modules/desktop_capture/differ.h"
|
|
|
|
#include "string.h"
|
|
|
|
#include "webrtc/modules/desktop_capture/differ_block.h"
|
|
#include "webrtc/system_wrappers/interface/logging.h"
|
|
|
|
namespace webrtc {
|
|
|
|
Differ::Differ(int width, int height, int bpp, int stride) {
|
|
// Dimensions of screen.
|
|
width_ = width;
|
|
height_ = height;
|
|
bytes_per_pixel_ = bpp;
|
|
bytes_per_row_ = stride;
|
|
|
|
// 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(bool);
|
|
diff_info_.reset(new bool[diff_info_size_]);
|
|
}
|
|
|
|
Differ::~Differ() {}
|
|
|
|
void Differ::CalcDirtyRegion(const uint8_t* prev_buffer,
|
|
const uint8_t* curr_buffer,
|
|
DesktopRegion* region) {
|
|
// 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(region);
|
|
}
|
|
|
|
void Differ::MarkDirtyBlocks(const uint8_t* prev_buffer,
|
|
const uint8_t* 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(bool);
|
|
|
|
const uint8_t* prev_block_row_start = prev_buffer;
|
|
const uint8_t* curr_block_row_start = curr_buffer;
|
|
bool* diff_info_row_start = diff_info_.get();
|
|
|
|
for (int y = 0; y < y_full_blocks; y++) {
|
|
const uint8_t* prev_block = prev_block_row_start;
|
|
const uint8_t* curr_block = curr_block_row_start;
|
|
bool* diff_info = diff_info_row_start;
|
|
|
|
for (int x = 0; x < x_full_blocks; x++) {
|
|
// Mark this block as being modified so that it gets incorporated into
|
|
// a dirty rect.
|
|
*diff_info = BlockDifference(prev_block, curr_block, bytes_per_row_);
|
|
prev_block += block_x_offset;
|
|
curr_block += block_x_offset;
|
|
diff_info += sizeof(bool);
|
|
}
|
|
|
|
// If there is a partial column at the end, handle it.
|
|
// This condition should rarely, if ever, occur.
|
|
if (partial_column_width != 0) {
|
|
*diff_info = !PartialBlocksEqual(prev_block, curr_block, bytes_per_row_,
|
|
partial_column_width, kBlockSize);
|
|
diff_info += sizeof(bool);
|
|
}
|
|
|
|
// 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 the screen height is not a multiple of the block size, then this
|
|
// handles the last partial row. This situation is far more common than the
|
|
// 'partial column' case.
|
|
if (partial_row_height != 0) {
|
|
const uint8_t* prev_block = prev_block_row_start;
|
|
const uint8_t* curr_block = curr_block_row_start;
|
|
bool* diff_info = diff_info_row_start;
|
|
for (int x = 0; x < x_full_blocks; x++) {
|
|
*diff_info = !PartialBlocksEqual(prev_block, curr_block,
|
|
bytes_per_row_,
|
|
kBlockSize, partial_row_height);
|
|
prev_block += block_x_offset;
|
|
curr_block += block_x_offset;
|
|
diff_info += sizeof(bool);
|
|
}
|
|
if (partial_column_width != 0) {
|
|
*diff_info = !PartialBlocksEqual(prev_block, curr_block, bytes_per_row_,
|
|
partial_column_width,
|
|
partial_row_height);
|
|
diff_info += sizeof(bool);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Differ::PartialBlocksEqual(const uint8_t* prev_buffer,
|
|
const uint8_t* curr_buffer,
|
|
int stride, int width, int height) {
|
|
int width_bytes = width * bytes_per_pixel_;
|
|
for (int y = 0; y < height; y++) {
|
|
if (memcmp(prev_buffer, curr_buffer, width_bytes) != 0)
|
|
return false;
|
|
prev_buffer += bytes_per_row_;
|
|
curr_buffer += bytes_per_row_;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Differ::MergeBlocks(DesktopRegion* region) {
|
|
region->Clear();
|
|
|
|
bool* diff_info_row_start = diff_info_.get();
|
|
int diff_info_stride = diff_info_width_ * sizeof(bool);
|
|
|
|
for (int y = 0; y < diff_info_height_; y++) {
|
|
bool* diff_info = diff_info_row_start;
|
|
for (int x = 0; x < diff_info_width_; x++) {
|
|
if (*diff_info) {
|
|
// 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 = false;
|
|
|
|
// 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.
|
|
bool* right = diff_info + 1;
|
|
while (*right) {
|
|
*right++ = false;
|
|
width++;
|
|
}
|
|
|
|
// Group with blocks below.
|
|
// The entire width of blocks that we matched above much match for
|
|
// each row that we add.
|
|
bool* 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++) {
|
|
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++ = false;
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
region->AddRect(DesktopRect::MakeXYWH(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 webrtc
|