Implement DesktopRegion subtraction.

Region subtraction is used in chromoting client, so it's needed to
replace SkRegion.

R=wez@chromium.org

Review URL: https://webrtc-codereview.appspot.com/2205004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4741 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
sergeyu@chromium.org 2013-09-13 19:53:16 +00:00
parent 1f09dbe353
commit 6ab45b9dab
3 changed files with 231 additions and 1 deletions

View File

@ -107,7 +107,7 @@ void DesktopRegion::AddRect(const DesktopRect& rect) {
row, Rows::value_type(bottom, new Row(top, bottom)));
} else if (top > row->second->top) {
// If the |top| falls in the middle of the |row| then split |row| into
// two, at |top|, and leave |row| referring to the lower of of the two,
// two, at |top|, and leave |row| referring to the lower of the two,
// ready to insert a new span into.
assert(top <= row->second->bottom);
Rows::iterator new_row = rows_.insert(
@ -272,6 +272,95 @@ void DesktopRegion::IntersectWith(const DesktopRect& rect) {
IntersectWith(region);
}
void DesktopRegion::Subtract(const DesktopRegion& region) {
if (region.rows_.empty())
return;
// |row_b| refers to the current row being subtracted.
Rows::const_iterator row_b = region.rows_.begin();
// Current vertical position at which subtraction is happening.
int top = row_b->second->top;
// |row_a| refers to the current row we are subtracting from. Skip all rows
// above |top|.
Rows::iterator row_a = rows_.upper_bound(top);
// Step through rows of the both regions subtracting content of |row_b| from
// |row_a|.
while (row_a != rows_.end() && row_b != region.rows_.end()) {
// Skip |row_a| if it doesn't intersect with the |row_b|.
if (row_a->second->bottom <= top) {
// Each output row is merged with previously-processed rows before further
// rows are processed.
MergeWithPrecedingRow(row_a);
++row_a;
continue;
}
if (top > row_a->second->top) {
// If |top| falls in the middle of |row_a| then split |row_a| into two, at
// |top|, and leave |row_a| referring to the lower of the two, ready to
// subtract spans from.
assert(top <= row_a->second->bottom);
Rows::iterator new_row = rows_.insert(
row_a, Rows::value_type(top, new Row(row_a->second->top, top)));
row_a->second->top = top;
new_row->second->spans = row_a->second->spans;
} else if (top < row_a->second->top) {
// If the |top| is above |row_a| then skip the range between |top| and
// top of |row_a| because it's empty.
top = row_a->second->top;
}
if (row_b->second->bottom < row_a->second->bottom) {
// If the bottom of |row_b| falls in the middle of the |row_a| split
// |row_a| into two, at |top|, and leave |row_a| referring to the upper of
// the two, ready to subtract spans from.
int bottom = row_b->second->bottom;
Rows::iterator new_row =
rows_.insert(row_a, Rows::value_type(bottom, new Row(top, bottom)));
row_a->second->top = bottom;
new_row->second->spans = row_a->second->spans;
row_a = new_row;
}
// At this point the vertical range covered by |row_a| lays within the
// range covered by |row_b|. Subtract |row_b| spans from |row_a|.
RowSpanSet new_spans;
SubtractRows(row_a->second->spans, row_b->second->spans, &new_spans);
new_spans.swap(row_a->second->spans);
top = row_a->second->bottom;
if (top >= row_b->second->bottom) {
row_b++;
if (row_b != region.rows_.end())
top = row_b->second->top;
}
// Check if the row is empty after subtraction and delete it. Otherwise move
// to the next one.
if (row_a->second->spans.empty()) {
Rows::iterator row_to_delete = row_a;
++row_a;
delete row_to_delete->second;
rows_.erase(row_to_delete);
} else {
MergeWithPrecedingRow(row_a);
row_a++;
}
}
if (row_a != rows_.end())
MergeWithPrecedingRow(row_a);
}
void DesktopRegion::Subtract(const DesktopRect& rect) {
DesktopRegion region;
region.AddRect(rect);
Subtract(region);
}
void DesktopRegion::Translate(int32_t dx, int32_t dy) {
Rows new_rows;
@ -370,6 +459,39 @@ bool DesktopRegion::IsSpanInRow(const Row& row, const RowSpan& span) {
return it != row.spans.end() && *it == span;
}
// static
void DesktopRegion::SubtractRows(const RowSpanSet& set_a,
const RowSpanSet& set_b,
RowSpanSet* output) {
assert(!set_a.empty() && !set_b.empty());
RowSpanSet::const_iterator it_b = set_b.begin();
// Iterate over all spans in |set_a| adding parts of it that do not intersect
// with |set_b| to the |output|.
for (RowSpanSet::const_iterator it_a = set_a.begin(); it_a != set_a.end();
++it_a) {
// If there is no intersection then append the current span and continue.
if (it_b == set_b.end() || it_a->right < it_b->left) {
output->push_back(*it_a);
it_a++;
continue;
}
// Iterate over |set_b| spans that may intersect with |it_a|.
int pos = it_a->left;
while (it_b != set_b.end() && it_b->left < it_a->right) {
if (it_b->left > pos) {
output->push_back(RowSpan(pos, it_b->left));
}
pos = it_b->right;
++it_b;
}
if (pos < it_a->right)
output->push_back(RowSpan(pos, it_a->right));
}
}
DesktopRegion::Iterator::Iterator(const DesktopRegion& region)
: region_(region),
row_(region.rows_.begin()),

View File

@ -119,6 +119,12 @@ class DesktopRegion {
// Clips the region by the |rect|.
void IntersectWith(const DesktopRect& rect);
// Subtracts |region| from the current content of the region.
void Subtract(const DesktopRegion& region);
// Subtracts |rect| from the current content of the region.
void Subtract(const DesktopRect& rect);
// Adds (dx, dy) to the position of the region.
void Translate(int32_t dx, int32_t dy);
@ -141,6 +147,10 @@ class DesktopRegion {
const RowSpanSet& set2,
RowSpanSet* output);
static void SubtractRows(const RowSpanSet& set_a,
const RowSpanSet& set_b,
RowSpanSet* output);
// Merges |row| with the row above it if they contain the same spans. Doesn't
// do anything if called with |row| set to rows_.begin() (i.e. first row of
// the region). If the rows were merged |row| remains a valid iterator to the

View File

@ -444,6 +444,104 @@ TEST(DesktopRegionTest, Intersect) {
}
}
TEST(DesktopRegionTest, Subtract) {
struct Case {
int input1_count;
DesktopRect input1_rects[4];
int input2_count;
DesktopRect input2_rects[4];
int expected_count;
DesktopRect expected_rects[5];
} cases[] = {
// Subtract one rect from another.
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(50, 50, 150, 150) },
2, { DesktopRect::MakeLTRB(0, 0, 100, 50),
DesktopRect::MakeLTRB(0, 50, 50, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(-50, -50, 50, 50) },
2, { DesktopRect::MakeLTRB(50, 0, 100, 50),
DesktopRect::MakeLTRB(0, 50, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(-50, 50, 50, 150) },
2, { DesktopRect::MakeLTRB(0, 0, 100, 50),
DesktopRect::MakeLTRB(50, 50, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(50, 50, 150, 70) },
3, { DesktopRect::MakeLTRB(0, 0, 100, 50),
DesktopRect::MakeLTRB(0, 50, 50, 70),
DesktopRect::MakeLTRB(0, 70, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(50, 50, 70, 70) },
4, { DesktopRect::MakeLTRB(0, 0, 100, 50),
DesktopRect::MakeLTRB(0, 50, 50, 70),
DesktopRect::MakeLTRB(70, 50, 100, 70),
DesktopRect::MakeLTRB(0, 70, 100, 100) } },
// Empty result.
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
0, {} },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(-10, -10, 110, 110) },
0, {} },
{ 2, { DesktopRect::MakeLTRB(0, 0, 100, 100),
DesktopRect::MakeLTRB(50, 50, 150, 150) },
2, { DesktopRect::MakeLTRB(0, 0, 100, 100),
DesktopRect::MakeLTRB(50, 50, 150, 150) },
0, {} },
// One rect out of disjoint set.
{ 3, { DesktopRect::MakeLTRB(0, 0, 10, 10),
DesktopRect::MakeLTRB(20, 20, 30, 30),
DesktopRect::MakeLTRB(40, 0, 50, 10) },
1, { DesktopRect::MakeLTRB(20, 20, 30, 30) },
2, { DesktopRect::MakeLTRB(0, 0, 10, 10),
DesktopRect::MakeLTRB(40, 0, 50, 10) } },
// Row merging.
{ 3, { DesktopRect::MakeLTRB(0, 0, 100, 50),
DesktopRect::MakeLTRB(0, 50, 150, 70),
DesktopRect::MakeLTRB(0, 70, 100, 100) },
1, { DesktopRect::MakeLTRB(100, 50, 150, 70) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) } },
// No-op subtraction.
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(100, 0, 200, 100) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(-100, 0, 0, 100) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(0, 100, 0, 200) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) } },
{ 1, { DesktopRect::MakeLTRB(0, 0, 100, 100) },
1, { DesktopRect::MakeLTRB(0, -100, 100, 0) },
1, { DesktopRect::MakeLTRB(0, 0, 100, 100) } },
};
for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
SCOPED_TRACE(i);
DesktopRegion r1(cases[i].input1_rects, cases[i].input1_count);
DesktopRegion r2(cases[i].input2_rects, cases[i].input2_count);
r1.Subtract(r2);
CompareRegion(r1, cases[i].expected_rects, cases[i].expected_count);
}
}
TEST(DesktopRegionTest, DISABLED_Performance) {
for (int c = 0; c < 1000; ++c) {
DesktopRegion r;