diff --git a/webrtc/modules/desktop_capture/desktop_region.cc b/webrtc/modules/desktop_capture/desktop_region.cc index 77e04725c0..4159cd453b 100644 --- a/webrtc/modules/desktop_capture/desktop_region.cc +++ b/webrtc/modules/desktop_capture/desktop_region.cc @@ -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()), diff --git a/webrtc/modules/desktop_capture/desktop_region.h b/webrtc/modules/desktop_capture/desktop_region.h index 04ea5e14d0..fc7c6ed9e2 100644 --- a/webrtc/modules/desktop_capture/desktop_region.h +++ b/webrtc/modules/desktop_capture/desktop_region.h @@ -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 diff --git a/webrtc/modules/desktop_capture/desktop_region_unittest.cc b/webrtc/modules/desktop_capture/desktop_region_unittest.cc index de4b3262da..2c2166184a 100644 --- a/webrtc/modules/desktop_capture/desktop_region_unittest.cc +++ b/webrtc/modules/desktop_capture/desktop_region_unittest.cc @@ -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;