summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/desktop_capture/desktop_region.cc')
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_region.cc567
1 files changed, 567 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc
new file mode 100644
index 0000000000..2c87c11eb3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc
@@ -0,0 +1,567 @@
+/*
+ * 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 "modules/desktop_capture/desktop_region.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+DesktopRegion::RowSpan::RowSpan(int32_t left, int32_t right)
+ : left(left), right(right) {}
+
+DesktopRegion::Row::Row(const Row&) = default;
+DesktopRegion::Row::Row(Row&&) = default;
+
+DesktopRegion::Row::Row(int32_t top, int32_t bottom)
+ : top(top), bottom(bottom) {}
+
+DesktopRegion::Row::~Row() {}
+
+DesktopRegion::DesktopRegion() {}
+
+DesktopRegion::DesktopRegion(const DesktopRect& rect) {
+ AddRect(rect);
+}
+
+DesktopRegion::DesktopRegion(const DesktopRect* rects, int count) {
+ AddRects(rects, count);
+}
+
+DesktopRegion::DesktopRegion(const DesktopRegion& other) {
+ *this = other;
+}
+
+DesktopRegion::~DesktopRegion() {
+ Clear();
+}
+
+DesktopRegion& DesktopRegion::operator=(const DesktopRegion& other) {
+ Clear();
+ rows_ = other.rows_;
+ for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
+ // Copy each row.
+ Row* row = it->second;
+ it->second = new Row(*row);
+ }
+ return *this;
+}
+
+bool DesktopRegion::Equals(const DesktopRegion& region) const {
+ // Iterate over rows of the tow regions and compare each row.
+ Rows::const_iterator it1 = rows_.begin();
+ Rows::const_iterator it2 = region.rows_.begin();
+ while (it1 != rows_.end()) {
+ if (it2 == region.rows_.end() || it1->first != it2->first ||
+ it1->second->top != it2->second->top ||
+ it1->second->bottom != it2->second->bottom ||
+ it1->second->spans != it2->second->spans) {
+ return false;
+ }
+ ++it1;
+ ++it2;
+ }
+ return it2 == region.rows_.end();
+}
+
+void DesktopRegion::Clear() {
+ for (Rows::iterator row = rows_.begin(); row != rows_.end(); ++row) {
+ delete row->second;
+ }
+ rows_.clear();
+}
+
+void DesktopRegion::SetRect(const DesktopRect& rect) {
+ Clear();
+ AddRect(rect);
+}
+
+void DesktopRegion::AddRect(const DesktopRect& rect) {
+ if (rect.is_empty())
+ return;
+
+ // Top of the part of the `rect` that hasn't been inserted yet. Increased as
+ // we iterate over the rows until it reaches `rect.bottom()`.
+ int top = rect.top();
+
+ // Iterate over all rows that may intersect with `rect` and add new rows when
+ // necessary.
+ Rows::iterator row = rows_.upper_bound(top);
+ while (top < rect.bottom()) {
+ if (row == rows_.end() || top < row->second->top) {
+ // If `top` is above the top of the current `row` then add a new row above
+ // the current one.
+ int32_t bottom = rect.bottom();
+ if (row != rows_.end() && row->second->top < bottom)
+ bottom = row->second->top;
+ row = rows_.insert(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 the two,
+ // ready to insert a new span into.
+ RTC_DCHECK_LE(top, row->second->bottom);
+ Rows::iterator new_row = rows_.insert(
+ row, Rows::value_type(top, new Row(row->second->top, top)));
+ row->second->top = top;
+ new_row->second->spans = row->second->spans;
+ }
+
+ if (rect.bottom() < row->second->bottom) {
+ // If the bottom of the `rect` falls in the middle of the `row` split
+ // `row` into two, at `top`, and leave `row` referring to the upper of
+ // the two, ready to insert a new span into.
+ Rows::iterator new_row = rows_.insert(
+ row, Rows::value_type(rect.bottom(), new Row(top, rect.bottom())));
+ row->second->top = rect.bottom();
+ new_row->second->spans = row->second->spans;
+ row = new_row;
+ }
+
+ // Add a new span to the current row.
+ AddSpanToRow(row->second, rect.left(), rect.right());
+ top = row->second->bottom;
+
+ MergeWithPrecedingRow(row);
+
+ // Move to the next row.
+ ++row;
+ }
+
+ if (row != rows_.end())
+ MergeWithPrecedingRow(row);
+}
+
+void DesktopRegion::AddRects(const DesktopRect* rects, int count) {
+ for (int i = 0; i < count; ++i) {
+ AddRect(rects[i]);
+ }
+}
+
+void DesktopRegion::MergeWithPrecedingRow(Rows::iterator row) {
+ RTC_DCHECK(row != rows_.end());
+
+ if (row != rows_.begin()) {
+ Rows::iterator previous_row = row;
+ previous_row--;
+
+ // If `row` and `previous_row` are next to each other and contain the same
+ // set of spans then they can be merged.
+ if (previous_row->second->bottom == row->second->top &&
+ previous_row->second->spans == row->second->spans) {
+ row->second->top = previous_row->second->top;
+ delete previous_row->second;
+ rows_.erase(previous_row);
+ }
+ }
+}
+
+void DesktopRegion::AddRegion(const DesktopRegion& region) {
+ // TODO(sergeyu): This function is not optimized - potentially it can iterate
+ // over rows of the two regions similar to how it works in Intersect().
+ for (Iterator it(region); !it.IsAtEnd(); it.Advance()) {
+ AddRect(it.rect());
+ }
+}
+
+void DesktopRegion::Intersect(const DesktopRegion& region1,
+ const DesktopRegion& region2) {
+ Clear();
+
+ Rows::const_iterator it1 = region1.rows_.begin();
+ Rows::const_iterator end1 = region1.rows_.end();
+ Rows::const_iterator it2 = region2.rows_.begin();
+ Rows::const_iterator end2 = region2.rows_.end();
+ if (it1 == end1 || it2 == end2)
+ return;
+
+ while (it1 != end1 && it2 != end2) {
+ // Arrange for `it1` to always be the top-most of the rows.
+ if (it2->second->top < it1->second->top) {
+ std::swap(it1, it2);
+ std::swap(end1, end2);
+ }
+
+ // Skip `it1` if it doesn't intersect `it2` at all.
+ if (it1->second->bottom <= it2->second->top) {
+ ++it1;
+ continue;
+ }
+
+ // Top of the `it1` row is above the top of `it2`, so top of the
+ // intersection is always the top of `it2`.
+ int32_t top = it2->second->top;
+ int32_t bottom = std::min(it1->second->bottom, it2->second->bottom);
+
+ Rows::iterator new_row = rows_.insert(
+ rows_.end(), Rows::value_type(bottom, new Row(top, bottom)));
+ IntersectRows(it1->second->spans, it2->second->spans,
+ &new_row->second->spans);
+ if (new_row->second->spans.empty()) {
+ delete new_row->second;
+ rows_.erase(new_row);
+ } else {
+ MergeWithPrecedingRow(new_row);
+ }
+
+ // If `it1` was completely consumed, move to the next one.
+ if (it1->second->bottom == bottom)
+ ++it1;
+ // If `it2` was completely consumed, move to the next one.
+ if (it2->second->bottom == bottom)
+ ++it2;
+ }
+}
+
+// static
+void DesktopRegion::IntersectRows(const RowSpanSet& set1,
+ const RowSpanSet& set2,
+ RowSpanSet* output) {
+ RowSpanSet::const_iterator it1 = set1.begin();
+ RowSpanSet::const_iterator end1 = set1.end();
+ RowSpanSet::const_iterator it2 = set2.begin();
+ RowSpanSet::const_iterator end2 = set2.end();
+ RTC_DCHECK(it1 != end1 && it2 != end2);
+
+ do {
+ // Arrange for `it1` to always be the left-most of the spans.
+ if (it2->left < it1->left) {
+ std::swap(it1, it2);
+ std::swap(end1, end2);
+ }
+
+ // Skip `it1` if it doesn't intersect `it2` at all.
+ if (it1->right <= it2->left) {
+ ++it1;
+ continue;
+ }
+
+ int32_t left = it2->left;
+ int32_t right = std::min(it1->right, it2->right);
+ RTC_DCHECK_LT(left, right);
+
+ output->push_back(RowSpan(left, right));
+
+ // If `it1` was completely consumed, move to the next one.
+ if (it1->right == right)
+ ++it1;
+ // If `it2` was completely consumed, move to the next one.
+ if (it2->right == right)
+ ++it2;
+ } while (it1 != end1 && it2 != end2);
+}
+
+void DesktopRegion::IntersectWith(const DesktopRegion& region) {
+ DesktopRegion old_region;
+ Swap(&old_region);
+ Intersect(old_region, region);
+}
+
+void DesktopRegion::IntersectWith(const DesktopRect& rect) {
+ DesktopRegion region;
+ region.AddRect(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.
+ RTC_DCHECK_LE(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 (top >= row_b->second->bottom) {
+ ++row_b;
+ if (row_b != region.rows_.end())
+ top = row_b->second->top;
+ continue;
+ }
+ }
+
+ 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;
+
+ for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
+ Row* row = it->second;
+
+ row->top += dy;
+ row->bottom += dy;
+
+ if (dx != 0) {
+ // Translate each span.
+ for (RowSpanSet::iterator span = row->spans.begin();
+ span != row->spans.end(); ++span) {
+ span->left += dx;
+ span->right += dx;
+ }
+ }
+
+ if (dy != 0)
+ new_rows.insert(new_rows.end(), Rows::value_type(row->bottom, row));
+ }
+
+ if (dy != 0)
+ new_rows.swap(rows_);
+}
+
+void DesktopRegion::Swap(DesktopRegion* region) {
+ rows_.swap(region->rows_);
+}
+
+// static
+bool DesktopRegion::CompareSpanRight(const RowSpan& r, int32_t value) {
+ return r.right < value;
+}
+
+// static
+bool DesktopRegion::CompareSpanLeft(const RowSpan& r, int32_t value) {
+ return r.left < value;
+}
+
+// static
+void DesktopRegion::AddSpanToRow(Row* row, int left, int right) {
+ // First check if the new span is located to the right of all existing spans.
+ // This is an optimization to avoid binary search in the case when rectangles
+ // are inserted sequentially from left to right.
+ if (row->spans.empty() || left > row->spans.back().right) {
+ row->spans.push_back(RowSpan(left, right));
+ return;
+ }
+
+ // Find the first span that ends at or after `left`.
+ RowSpanSet::iterator start = std::lower_bound(
+ row->spans.begin(), row->spans.end(), left, CompareSpanRight);
+ RTC_DCHECK(start < row->spans.end());
+
+ // Find the first span that starts after `right`.
+ RowSpanSet::iterator end =
+ std::lower_bound(start, row->spans.end(), right + 1, CompareSpanLeft);
+ if (end == row->spans.begin()) {
+ // There are no overlaps. Just insert the new span at the beginning.
+ row->spans.insert(row->spans.begin(), RowSpan(left, right));
+ return;
+ }
+
+ // Move end to the left, so that it points the last span that ends at or
+ // before `right`.
+ end--;
+
+ // At this point [start, end] is the range of spans that intersect with the
+ // new one.
+ if (end < start) {
+ // There are no overlaps. Just insert the new span at the correct position.
+ row->spans.insert(start, RowSpan(left, right));
+ return;
+ }
+
+ left = std::min(left, start->left);
+ right = std::max(right, end->right);
+
+ // Replace range [start, end] with the new span.
+ *start = RowSpan(left, right);
+ ++start;
+ ++end;
+ if (start < end)
+ row->spans.erase(start, end);
+}
+
+// static
+bool DesktopRegion::IsSpanInRow(const Row& row, const RowSpan& span) {
+ // Find the first span that starts at or after `span.left` and then check if
+ // it's the same span.
+ RowSpanSet::const_iterator it = std::lower_bound(
+ row.spans.begin(), row.spans.end(), span.left, CompareSpanLeft);
+ return it != row.spans.end() && *it == span;
+}
+
+// static
+void DesktopRegion::SubtractRows(const RowSpanSet& set_a,
+ const RowSpanSet& set_b,
+ RowSpanSet* output) {
+ RTC_DCHECK(!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);
+ 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));
+ if (it_b->right > pos) {
+ pos = it_b->right;
+ if (pos >= it_a->right)
+ break;
+ }
+ ++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()),
+ previous_row_(region.rows_.end()) {
+ if (!IsAtEnd()) {
+ RTC_DCHECK_GT(row_->second->spans.size(), 0);
+ row_span_ = row_->second->spans.begin();
+ UpdateCurrentRect();
+ }
+}
+
+DesktopRegion::Iterator::~Iterator() {}
+
+bool DesktopRegion::Iterator::IsAtEnd() const {
+ return row_ == region_.rows_.end();
+}
+
+void DesktopRegion::Iterator::Advance() {
+ RTC_DCHECK(!IsAtEnd());
+
+ while (true) {
+ ++row_span_;
+ if (row_span_ == row_->second->spans.end()) {
+ previous_row_ = row_;
+ ++row_;
+ if (row_ != region_.rows_.end()) {
+ RTC_DCHECK_GT(row_->second->spans.size(), 0);
+ row_span_ = row_->second->spans.begin();
+ }
+ }
+
+ if (IsAtEnd())
+ return;
+
+ // If the same span exists on the previous row then skip it, as we've
+ // already returned this span merged into the previous one, via
+ // UpdateCurrentRect().
+ if (previous_row_ != region_.rows_.end() &&
+ previous_row_->second->bottom == row_->second->top &&
+ IsSpanInRow(*previous_row_->second, *row_span_)) {
+ continue;
+ }
+
+ break;
+ }
+
+ RTC_DCHECK(!IsAtEnd());
+ UpdateCurrentRect();
+}
+
+void DesktopRegion::Iterator::UpdateCurrentRect() {
+ // Merge the current rectangle with the matching spans from later rows.
+ int bottom;
+ Rows::const_iterator bottom_row = row_;
+ Rows::const_iterator previous;
+ do {
+ bottom = bottom_row->second->bottom;
+ previous = bottom_row;
+ ++bottom_row;
+ } while (bottom_row != region_.rows_.end() &&
+ previous->second->bottom == bottom_row->second->top &&
+ IsSpanInRow(*bottom_row->second, *row_span_));
+ rect_ = DesktopRect::MakeLTRB(row_span_->left, row_->second->top,
+ row_span_->right, bottom);
+}
+
+} // namespace webrtc