1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "modules/desktop_capture/desktop_region.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <utility>
15*d9f75844SAndroid Build Coastguard Worker
16*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
19*d9f75844SAndroid Build Coastguard Worker
RowSpan(int32_t left,int32_t right)20*d9f75844SAndroid Build Coastguard Worker DesktopRegion::RowSpan::RowSpan(int32_t left, int32_t right)
21*d9f75844SAndroid Build Coastguard Worker : left(left), right(right) {}
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Row::Row(const Row&) = default;
24*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Row::Row(Row&&) = default;
25*d9f75844SAndroid Build Coastguard Worker
Row(int32_t top,int32_t bottom)26*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Row::Row(int32_t top, int32_t bottom)
27*d9f75844SAndroid Build Coastguard Worker : top(top), bottom(bottom) {}
28*d9f75844SAndroid Build Coastguard Worker
~Row()29*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Row::~Row() {}
30*d9f75844SAndroid Build Coastguard Worker
DesktopRegion()31*d9f75844SAndroid Build Coastguard Worker DesktopRegion::DesktopRegion() {}
32*d9f75844SAndroid Build Coastguard Worker
DesktopRegion(const DesktopRect & rect)33*d9f75844SAndroid Build Coastguard Worker DesktopRegion::DesktopRegion(const DesktopRect& rect) {
34*d9f75844SAndroid Build Coastguard Worker AddRect(rect);
35*d9f75844SAndroid Build Coastguard Worker }
36*d9f75844SAndroid Build Coastguard Worker
DesktopRegion(const DesktopRect * rects,int count)37*d9f75844SAndroid Build Coastguard Worker DesktopRegion::DesktopRegion(const DesktopRect* rects, int count) {
38*d9f75844SAndroid Build Coastguard Worker AddRects(rects, count);
39*d9f75844SAndroid Build Coastguard Worker }
40*d9f75844SAndroid Build Coastguard Worker
DesktopRegion(const DesktopRegion & other)41*d9f75844SAndroid Build Coastguard Worker DesktopRegion::DesktopRegion(const DesktopRegion& other) {
42*d9f75844SAndroid Build Coastguard Worker *this = other;
43*d9f75844SAndroid Build Coastguard Worker }
44*d9f75844SAndroid Build Coastguard Worker
~DesktopRegion()45*d9f75844SAndroid Build Coastguard Worker DesktopRegion::~DesktopRegion() {
46*d9f75844SAndroid Build Coastguard Worker Clear();
47*d9f75844SAndroid Build Coastguard Worker }
48*d9f75844SAndroid Build Coastguard Worker
operator =(const DesktopRegion & other)49*d9f75844SAndroid Build Coastguard Worker DesktopRegion& DesktopRegion::operator=(const DesktopRegion& other) {
50*d9f75844SAndroid Build Coastguard Worker Clear();
51*d9f75844SAndroid Build Coastguard Worker rows_ = other.rows_;
52*d9f75844SAndroid Build Coastguard Worker for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
53*d9f75844SAndroid Build Coastguard Worker // Copy each row.
54*d9f75844SAndroid Build Coastguard Worker Row* row = it->second;
55*d9f75844SAndroid Build Coastguard Worker it->second = new Row(*row);
56*d9f75844SAndroid Build Coastguard Worker }
57*d9f75844SAndroid Build Coastguard Worker return *this;
58*d9f75844SAndroid Build Coastguard Worker }
59*d9f75844SAndroid Build Coastguard Worker
Equals(const DesktopRegion & region) const60*d9f75844SAndroid Build Coastguard Worker bool DesktopRegion::Equals(const DesktopRegion& region) const {
61*d9f75844SAndroid Build Coastguard Worker // Iterate over rows of the tow regions and compare each row.
62*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator it1 = rows_.begin();
63*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator it2 = region.rows_.begin();
64*d9f75844SAndroid Build Coastguard Worker while (it1 != rows_.end()) {
65*d9f75844SAndroid Build Coastguard Worker if (it2 == region.rows_.end() || it1->first != it2->first ||
66*d9f75844SAndroid Build Coastguard Worker it1->second->top != it2->second->top ||
67*d9f75844SAndroid Build Coastguard Worker it1->second->bottom != it2->second->bottom ||
68*d9f75844SAndroid Build Coastguard Worker it1->second->spans != it2->second->spans) {
69*d9f75844SAndroid Build Coastguard Worker return false;
70*d9f75844SAndroid Build Coastguard Worker }
71*d9f75844SAndroid Build Coastguard Worker ++it1;
72*d9f75844SAndroid Build Coastguard Worker ++it2;
73*d9f75844SAndroid Build Coastguard Worker }
74*d9f75844SAndroid Build Coastguard Worker return it2 == region.rows_.end();
75*d9f75844SAndroid Build Coastguard Worker }
76*d9f75844SAndroid Build Coastguard Worker
Clear()77*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Clear() {
78*d9f75844SAndroid Build Coastguard Worker for (Rows::iterator row = rows_.begin(); row != rows_.end(); ++row) {
79*d9f75844SAndroid Build Coastguard Worker delete row->second;
80*d9f75844SAndroid Build Coastguard Worker }
81*d9f75844SAndroid Build Coastguard Worker rows_.clear();
82*d9f75844SAndroid Build Coastguard Worker }
83*d9f75844SAndroid Build Coastguard Worker
SetRect(const DesktopRect & rect)84*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::SetRect(const DesktopRect& rect) {
85*d9f75844SAndroid Build Coastguard Worker Clear();
86*d9f75844SAndroid Build Coastguard Worker AddRect(rect);
87*d9f75844SAndroid Build Coastguard Worker }
88*d9f75844SAndroid Build Coastguard Worker
AddRect(const DesktopRect & rect)89*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::AddRect(const DesktopRect& rect) {
90*d9f75844SAndroid Build Coastguard Worker if (rect.is_empty())
91*d9f75844SAndroid Build Coastguard Worker return;
92*d9f75844SAndroid Build Coastguard Worker
93*d9f75844SAndroid Build Coastguard Worker // Top of the part of the `rect` that hasn't been inserted yet. Increased as
94*d9f75844SAndroid Build Coastguard Worker // we iterate over the rows until it reaches `rect.bottom()`.
95*d9f75844SAndroid Build Coastguard Worker int top = rect.top();
96*d9f75844SAndroid Build Coastguard Worker
97*d9f75844SAndroid Build Coastguard Worker // Iterate over all rows that may intersect with `rect` and add new rows when
98*d9f75844SAndroid Build Coastguard Worker // necessary.
99*d9f75844SAndroid Build Coastguard Worker Rows::iterator row = rows_.upper_bound(top);
100*d9f75844SAndroid Build Coastguard Worker while (top < rect.bottom()) {
101*d9f75844SAndroid Build Coastguard Worker if (row == rows_.end() || top < row->second->top) {
102*d9f75844SAndroid Build Coastguard Worker // If `top` is above the top of the current `row` then add a new row above
103*d9f75844SAndroid Build Coastguard Worker // the current one.
104*d9f75844SAndroid Build Coastguard Worker int32_t bottom = rect.bottom();
105*d9f75844SAndroid Build Coastguard Worker if (row != rows_.end() && row->second->top < bottom)
106*d9f75844SAndroid Build Coastguard Worker bottom = row->second->top;
107*d9f75844SAndroid Build Coastguard Worker row = rows_.insert(row, Rows::value_type(bottom, new Row(top, bottom)));
108*d9f75844SAndroid Build Coastguard Worker } else if (top > row->second->top) {
109*d9f75844SAndroid Build Coastguard Worker // If the `top` falls in the middle of the `row` then split `row` into
110*d9f75844SAndroid Build Coastguard Worker // two, at `top`, and leave `row` referring to the lower of the two,
111*d9f75844SAndroid Build Coastguard Worker // ready to insert a new span into.
112*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LE(top, row->second->bottom);
113*d9f75844SAndroid Build Coastguard Worker Rows::iterator new_row = rows_.insert(
114*d9f75844SAndroid Build Coastguard Worker row, Rows::value_type(top, new Row(row->second->top, top)));
115*d9f75844SAndroid Build Coastguard Worker row->second->top = top;
116*d9f75844SAndroid Build Coastguard Worker new_row->second->spans = row->second->spans;
117*d9f75844SAndroid Build Coastguard Worker }
118*d9f75844SAndroid Build Coastguard Worker
119*d9f75844SAndroid Build Coastguard Worker if (rect.bottom() < row->second->bottom) {
120*d9f75844SAndroid Build Coastguard Worker // If the bottom of the `rect` falls in the middle of the `row` split
121*d9f75844SAndroid Build Coastguard Worker // `row` into two, at `top`, and leave `row` referring to the upper of
122*d9f75844SAndroid Build Coastguard Worker // the two, ready to insert a new span into.
123*d9f75844SAndroid Build Coastguard Worker Rows::iterator new_row = rows_.insert(
124*d9f75844SAndroid Build Coastguard Worker row, Rows::value_type(rect.bottom(), new Row(top, rect.bottom())));
125*d9f75844SAndroid Build Coastguard Worker row->second->top = rect.bottom();
126*d9f75844SAndroid Build Coastguard Worker new_row->second->spans = row->second->spans;
127*d9f75844SAndroid Build Coastguard Worker row = new_row;
128*d9f75844SAndroid Build Coastguard Worker }
129*d9f75844SAndroid Build Coastguard Worker
130*d9f75844SAndroid Build Coastguard Worker // Add a new span to the current row.
131*d9f75844SAndroid Build Coastguard Worker AddSpanToRow(row->second, rect.left(), rect.right());
132*d9f75844SAndroid Build Coastguard Worker top = row->second->bottom;
133*d9f75844SAndroid Build Coastguard Worker
134*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(row);
135*d9f75844SAndroid Build Coastguard Worker
136*d9f75844SAndroid Build Coastguard Worker // Move to the next row.
137*d9f75844SAndroid Build Coastguard Worker ++row;
138*d9f75844SAndroid Build Coastguard Worker }
139*d9f75844SAndroid Build Coastguard Worker
140*d9f75844SAndroid Build Coastguard Worker if (row != rows_.end())
141*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(row);
142*d9f75844SAndroid Build Coastguard Worker }
143*d9f75844SAndroid Build Coastguard Worker
AddRects(const DesktopRect * rects,int count)144*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::AddRects(const DesktopRect* rects, int count) {
145*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
146*d9f75844SAndroid Build Coastguard Worker AddRect(rects[i]);
147*d9f75844SAndroid Build Coastguard Worker }
148*d9f75844SAndroid Build Coastguard Worker }
149*d9f75844SAndroid Build Coastguard Worker
MergeWithPrecedingRow(Rows::iterator row)150*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::MergeWithPrecedingRow(Rows::iterator row) {
151*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(row != rows_.end());
152*d9f75844SAndroid Build Coastguard Worker
153*d9f75844SAndroid Build Coastguard Worker if (row != rows_.begin()) {
154*d9f75844SAndroid Build Coastguard Worker Rows::iterator previous_row = row;
155*d9f75844SAndroid Build Coastguard Worker previous_row--;
156*d9f75844SAndroid Build Coastguard Worker
157*d9f75844SAndroid Build Coastguard Worker // If `row` and `previous_row` are next to each other and contain the same
158*d9f75844SAndroid Build Coastguard Worker // set of spans then they can be merged.
159*d9f75844SAndroid Build Coastguard Worker if (previous_row->second->bottom == row->second->top &&
160*d9f75844SAndroid Build Coastguard Worker previous_row->second->spans == row->second->spans) {
161*d9f75844SAndroid Build Coastguard Worker row->second->top = previous_row->second->top;
162*d9f75844SAndroid Build Coastguard Worker delete previous_row->second;
163*d9f75844SAndroid Build Coastguard Worker rows_.erase(previous_row);
164*d9f75844SAndroid Build Coastguard Worker }
165*d9f75844SAndroid Build Coastguard Worker }
166*d9f75844SAndroid Build Coastguard Worker }
167*d9f75844SAndroid Build Coastguard Worker
AddRegion(const DesktopRegion & region)168*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::AddRegion(const DesktopRegion& region) {
169*d9f75844SAndroid Build Coastguard Worker // TODO(sergeyu): This function is not optimized - potentially it can iterate
170*d9f75844SAndroid Build Coastguard Worker // over rows of the two regions similar to how it works in Intersect().
171*d9f75844SAndroid Build Coastguard Worker for (Iterator it(region); !it.IsAtEnd(); it.Advance()) {
172*d9f75844SAndroid Build Coastguard Worker AddRect(it.rect());
173*d9f75844SAndroid Build Coastguard Worker }
174*d9f75844SAndroid Build Coastguard Worker }
175*d9f75844SAndroid Build Coastguard Worker
Intersect(const DesktopRegion & region1,const DesktopRegion & region2)176*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Intersect(const DesktopRegion& region1,
177*d9f75844SAndroid Build Coastguard Worker const DesktopRegion& region2) {
178*d9f75844SAndroid Build Coastguard Worker Clear();
179*d9f75844SAndroid Build Coastguard Worker
180*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator it1 = region1.rows_.begin();
181*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator end1 = region1.rows_.end();
182*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator it2 = region2.rows_.begin();
183*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator end2 = region2.rows_.end();
184*d9f75844SAndroid Build Coastguard Worker if (it1 == end1 || it2 == end2)
185*d9f75844SAndroid Build Coastguard Worker return;
186*d9f75844SAndroid Build Coastguard Worker
187*d9f75844SAndroid Build Coastguard Worker while (it1 != end1 && it2 != end2) {
188*d9f75844SAndroid Build Coastguard Worker // Arrange for `it1` to always be the top-most of the rows.
189*d9f75844SAndroid Build Coastguard Worker if (it2->second->top < it1->second->top) {
190*d9f75844SAndroid Build Coastguard Worker std::swap(it1, it2);
191*d9f75844SAndroid Build Coastguard Worker std::swap(end1, end2);
192*d9f75844SAndroid Build Coastguard Worker }
193*d9f75844SAndroid Build Coastguard Worker
194*d9f75844SAndroid Build Coastguard Worker // Skip `it1` if it doesn't intersect `it2` at all.
195*d9f75844SAndroid Build Coastguard Worker if (it1->second->bottom <= it2->second->top) {
196*d9f75844SAndroid Build Coastguard Worker ++it1;
197*d9f75844SAndroid Build Coastguard Worker continue;
198*d9f75844SAndroid Build Coastguard Worker }
199*d9f75844SAndroid Build Coastguard Worker
200*d9f75844SAndroid Build Coastguard Worker // Top of the `it1` row is above the top of `it2`, so top of the
201*d9f75844SAndroid Build Coastguard Worker // intersection is always the top of `it2`.
202*d9f75844SAndroid Build Coastguard Worker int32_t top = it2->second->top;
203*d9f75844SAndroid Build Coastguard Worker int32_t bottom = std::min(it1->second->bottom, it2->second->bottom);
204*d9f75844SAndroid Build Coastguard Worker
205*d9f75844SAndroid Build Coastguard Worker Rows::iterator new_row = rows_.insert(
206*d9f75844SAndroid Build Coastguard Worker rows_.end(), Rows::value_type(bottom, new Row(top, bottom)));
207*d9f75844SAndroid Build Coastguard Worker IntersectRows(it1->second->spans, it2->second->spans,
208*d9f75844SAndroid Build Coastguard Worker &new_row->second->spans);
209*d9f75844SAndroid Build Coastguard Worker if (new_row->second->spans.empty()) {
210*d9f75844SAndroid Build Coastguard Worker delete new_row->second;
211*d9f75844SAndroid Build Coastguard Worker rows_.erase(new_row);
212*d9f75844SAndroid Build Coastguard Worker } else {
213*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(new_row);
214*d9f75844SAndroid Build Coastguard Worker }
215*d9f75844SAndroid Build Coastguard Worker
216*d9f75844SAndroid Build Coastguard Worker // If `it1` was completely consumed, move to the next one.
217*d9f75844SAndroid Build Coastguard Worker if (it1->second->bottom == bottom)
218*d9f75844SAndroid Build Coastguard Worker ++it1;
219*d9f75844SAndroid Build Coastguard Worker // If `it2` was completely consumed, move to the next one.
220*d9f75844SAndroid Build Coastguard Worker if (it2->second->bottom == bottom)
221*d9f75844SAndroid Build Coastguard Worker ++it2;
222*d9f75844SAndroid Build Coastguard Worker }
223*d9f75844SAndroid Build Coastguard Worker }
224*d9f75844SAndroid Build Coastguard Worker
225*d9f75844SAndroid Build Coastguard Worker // static
IntersectRows(const RowSpanSet & set1,const RowSpanSet & set2,RowSpanSet * output)226*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::IntersectRows(const RowSpanSet& set1,
227*d9f75844SAndroid Build Coastguard Worker const RowSpanSet& set2,
228*d9f75844SAndroid Build Coastguard Worker RowSpanSet* output) {
229*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator it1 = set1.begin();
230*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator end1 = set1.end();
231*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator it2 = set2.begin();
232*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator end2 = set2.end();
233*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(it1 != end1 && it2 != end2);
234*d9f75844SAndroid Build Coastguard Worker
235*d9f75844SAndroid Build Coastguard Worker do {
236*d9f75844SAndroid Build Coastguard Worker // Arrange for `it1` to always be the left-most of the spans.
237*d9f75844SAndroid Build Coastguard Worker if (it2->left < it1->left) {
238*d9f75844SAndroid Build Coastguard Worker std::swap(it1, it2);
239*d9f75844SAndroid Build Coastguard Worker std::swap(end1, end2);
240*d9f75844SAndroid Build Coastguard Worker }
241*d9f75844SAndroid Build Coastguard Worker
242*d9f75844SAndroid Build Coastguard Worker // Skip `it1` if it doesn't intersect `it2` at all.
243*d9f75844SAndroid Build Coastguard Worker if (it1->right <= it2->left) {
244*d9f75844SAndroid Build Coastguard Worker ++it1;
245*d9f75844SAndroid Build Coastguard Worker continue;
246*d9f75844SAndroid Build Coastguard Worker }
247*d9f75844SAndroid Build Coastguard Worker
248*d9f75844SAndroid Build Coastguard Worker int32_t left = it2->left;
249*d9f75844SAndroid Build Coastguard Worker int32_t right = std::min(it1->right, it2->right);
250*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LT(left, right);
251*d9f75844SAndroid Build Coastguard Worker
252*d9f75844SAndroid Build Coastguard Worker output->push_back(RowSpan(left, right));
253*d9f75844SAndroid Build Coastguard Worker
254*d9f75844SAndroid Build Coastguard Worker // If `it1` was completely consumed, move to the next one.
255*d9f75844SAndroid Build Coastguard Worker if (it1->right == right)
256*d9f75844SAndroid Build Coastguard Worker ++it1;
257*d9f75844SAndroid Build Coastguard Worker // If `it2` was completely consumed, move to the next one.
258*d9f75844SAndroid Build Coastguard Worker if (it2->right == right)
259*d9f75844SAndroid Build Coastguard Worker ++it2;
260*d9f75844SAndroid Build Coastguard Worker } while (it1 != end1 && it2 != end2);
261*d9f75844SAndroid Build Coastguard Worker }
262*d9f75844SAndroid Build Coastguard Worker
IntersectWith(const DesktopRegion & region)263*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::IntersectWith(const DesktopRegion& region) {
264*d9f75844SAndroid Build Coastguard Worker DesktopRegion old_region;
265*d9f75844SAndroid Build Coastguard Worker Swap(&old_region);
266*d9f75844SAndroid Build Coastguard Worker Intersect(old_region, region);
267*d9f75844SAndroid Build Coastguard Worker }
268*d9f75844SAndroid Build Coastguard Worker
IntersectWith(const DesktopRect & rect)269*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::IntersectWith(const DesktopRect& rect) {
270*d9f75844SAndroid Build Coastguard Worker DesktopRegion region;
271*d9f75844SAndroid Build Coastguard Worker region.AddRect(rect);
272*d9f75844SAndroid Build Coastguard Worker IntersectWith(region);
273*d9f75844SAndroid Build Coastguard Worker }
274*d9f75844SAndroid Build Coastguard Worker
Subtract(const DesktopRegion & region)275*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Subtract(const DesktopRegion& region) {
276*d9f75844SAndroid Build Coastguard Worker if (region.rows_.empty())
277*d9f75844SAndroid Build Coastguard Worker return;
278*d9f75844SAndroid Build Coastguard Worker
279*d9f75844SAndroid Build Coastguard Worker // `row_b` refers to the current row being subtracted.
280*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator row_b = region.rows_.begin();
281*d9f75844SAndroid Build Coastguard Worker
282*d9f75844SAndroid Build Coastguard Worker // Current vertical position at which subtraction is happening.
283*d9f75844SAndroid Build Coastguard Worker int top = row_b->second->top;
284*d9f75844SAndroid Build Coastguard Worker
285*d9f75844SAndroid Build Coastguard Worker // `row_a` refers to the current row we are subtracting from. Skip all rows
286*d9f75844SAndroid Build Coastguard Worker // above `top`.
287*d9f75844SAndroid Build Coastguard Worker Rows::iterator row_a = rows_.upper_bound(top);
288*d9f75844SAndroid Build Coastguard Worker
289*d9f75844SAndroid Build Coastguard Worker // Step through rows of the both regions subtracting content of `row_b` from
290*d9f75844SAndroid Build Coastguard Worker // `row_a`.
291*d9f75844SAndroid Build Coastguard Worker while (row_a != rows_.end() && row_b != region.rows_.end()) {
292*d9f75844SAndroid Build Coastguard Worker // Skip `row_a` if it doesn't intersect with the `row_b`.
293*d9f75844SAndroid Build Coastguard Worker if (row_a->second->bottom <= top) {
294*d9f75844SAndroid Build Coastguard Worker // Each output row is merged with previously-processed rows before further
295*d9f75844SAndroid Build Coastguard Worker // rows are processed.
296*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(row_a);
297*d9f75844SAndroid Build Coastguard Worker ++row_a;
298*d9f75844SAndroid Build Coastguard Worker continue;
299*d9f75844SAndroid Build Coastguard Worker }
300*d9f75844SAndroid Build Coastguard Worker
301*d9f75844SAndroid Build Coastguard Worker if (top > row_a->second->top) {
302*d9f75844SAndroid Build Coastguard Worker // If `top` falls in the middle of `row_a` then split `row_a` into two, at
303*d9f75844SAndroid Build Coastguard Worker // `top`, and leave `row_a` referring to the lower of the two, ready to
304*d9f75844SAndroid Build Coastguard Worker // subtract spans from.
305*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_LE(top, row_a->second->bottom);
306*d9f75844SAndroid Build Coastguard Worker Rows::iterator new_row = rows_.insert(
307*d9f75844SAndroid Build Coastguard Worker row_a, Rows::value_type(top, new Row(row_a->second->top, top)));
308*d9f75844SAndroid Build Coastguard Worker row_a->second->top = top;
309*d9f75844SAndroid Build Coastguard Worker new_row->second->spans = row_a->second->spans;
310*d9f75844SAndroid Build Coastguard Worker } else if (top < row_a->second->top) {
311*d9f75844SAndroid Build Coastguard Worker // If the `top` is above `row_a` then skip the range between `top` and
312*d9f75844SAndroid Build Coastguard Worker // top of `row_a` because it's empty.
313*d9f75844SAndroid Build Coastguard Worker top = row_a->second->top;
314*d9f75844SAndroid Build Coastguard Worker if (top >= row_b->second->bottom) {
315*d9f75844SAndroid Build Coastguard Worker ++row_b;
316*d9f75844SAndroid Build Coastguard Worker if (row_b != region.rows_.end())
317*d9f75844SAndroid Build Coastguard Worker top = row_b->second->top;
318*d9f75844SAndroid Build Coastguard Worker continue;
319*d9f75844SAndroid Build Coastguard Worker }
320*d9f75844SAndroid Build Coastguard Worker }
321*d9f75844SAndroid Build Coastguard Worker
322*d9f75844SAndroid Build Coastguard Worker if (row_b->second->bottom < row_a->second->bottom) {
323*d9f75844SAndroid Build Coastguard Worker // If the bottom of `row_b` falls in the middle of the `row_a` split
324*d9f75844SAndroid Build Coastguard Worker // `row_a` into two, at `top`, and leave `row_a` referring to the upper of
325*d9f75844SAndroid Build Coastguard Worker // the two, ready to subtract spans from.
326*d9f75844SAndroid Build Coastguard Worker int bottom = row_b->second->bottom;
327*d9f75844SAndroid Build Coastguard Worker Rows::iterator new_row =
328*d9f75844SAndroid Build Coastguard Worker rows_.insert(row_a, Rows::value_type(bottom, new Row(top, bottom)));
329*d9f75844SAndroid Build Coastguard Worker row_a->second->top = bottom;
330*d9f75844SAndroid Build Coastguard Worker new_row->second->spans = row_a->second->spans;
331*d9f75844SAndroid Build Coastguard Worker row_a = new_row;
332*d9f75844SAndroid Build Coastguard Worker }
333*d9f75844SAndroid Build Coastguard Worker
334*d9f75844SAndroid Build Coastguard Worker // At this point the vertical range covered by `row_a` lays within the
335*d9f75844SAndroid Build Coastguard Worker // range covered by `row_b`. Subtract `row_b` spans from `row_a`.
336*d9f75844SAndroid Build Coastguard Worker RowSpanSet new_spans;
337*d9f75844SAndroid Build Coastguard Worker SubtractRows(row_a->second->spans, row_b->second->spans, &new_spans);
338*d9f75844SAndroid Build Coastguard Worker new_spans.swap(row_a->second->spans);
339*d9f75844SAndroid Build Coastguard Worker top = row_a->second->bottom;
340*d9f75844SAndroid Build Coastguard Worker
341*d9f75844SAndroid Build Coastguard Worker if (top >= row_b->second->bottom) {
342*d9f75844SAndroid Build Coastguard Worker ++row_b;
343*d9f75844SAndroid Build Coastguard Worker if (row_b != region.rows_.end())
344*d9f75844SAndroid Build Coastguard Worker top = row_b->second->top;
345*d9f75844SAndroid Build Coastguard Worker }
346*d9f75844SAndroid Build Coastguard Worker
347*d9f75844SAndroid Build Coastguard Worker // Check if the row is empty after subtraction and delete it. Otherwise move
348*d9f75844SAndroid Build Coastguard Worker // to the next one.
349*d9f75844SAndroid Build Coastguard Worker if (row_a->second->spans.empty()) {
350*d9f75844SAndroid Build Coastguard Worker Rows::iterator row_to_delete = row_a;
351*d9f75844SAndroid Build Coastguard Worker ++row_a;
352*d9f75844SAndroid Build Coastguard Worker delete row_to_delete->second;
353*d9f75844SAndroid Build Coastguard Worker rows_.erase(row_to_delete);
354*d9f75844SAndroid Build Coastguard Worker } else {
355*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(row_a);
356*d9f75844SAndroid Build Coastguard Worker ++row_a;
357*d9f75844SAndroid Build Coastguard Worker }
358*d9f75844SAndroid Build Coastguard Worker }
359*d9f75844SAndroid Build Coastguard Worker
360*d9f75844SAndroid Build Coastguard Worker if (row_a != rows_.end())
361*d9f75844SAndroid Build Coastguard Worker MergeWithPrecedingRow(row_a);
362*d9f75844SAndroid Build Coastguard Worker }
363*d9f75844SAndroid Build Coastguard Worker
Subtract(const DesktopRect & rect)364*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Subtract(const DesktopRect& rect) {
365*d9f75844SAndroid Build Coastguard Worker DesktopRegion region;
366*d9f75844SAndroid Build Coastguard Worker region.AddRect(rect);
367*d9f75844SAndroid Build Coastguard Worker Subtract(region);
368*d9f75844SAndroid Build Coastguard Worker }
369*d9f75844SAndroid Build Coastguard Worker
Translate(int32_t dx,int32_t dy)370*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Translate(int32_t dx, int32_t dy) {
371*d9f75844SAndroid Build Coastguard Worker Rows new_rows;
372*d9f75844SAndroid Build Coastguard Worker
373*d9f75844SAndroid Build Coastguard Worker for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
374*d9f75844SAndroid Build Coastguard Worker Row* row = it->second;
375*d9f75844SAndroid Build Coastguard Worker
376*d9f75844SAndroid Build Coastguard Worker row->top += dy;
377*d9f75844SAndroid Build Coastguard Worker row->bottom += dy;
378*d9f75844SAndroid Build Coastguard Worker
379*d9f75844SAndroid Build Coastguard Worker if (dx != 0) {
380*d9f75844SAndroid Build Coastguard Worker // Translate each span.
381*d9f75844SAndroid Build Coastguard Worker for (RowSpanSet::iterator span = row->spans.begin();
382*d9f75844SAndroid Build Coastguard Worker span != row->spans.end(); ++span) {
383*d9f75844SAndroid Build Coastguard Worker span->left += dx;
384*d9f75844SAndroid Build Coastguard Worker span->right += dx;
385*d9f75844SAndroid Build Coastguard Worker }
386*d9f75844SAndroid Build Coastguard Worker }
387*d9f75844SAndroid Build Coastguard Worker
388*d9f75844SAndroid Build Coastguard Worker if (dy != 0)
389*d9f75844SAndroid Build Coastguard Worker new_rows.insert(new_rows.end(), Rows::value_type(row->bottom, row));
390*d9f75844SAndroid Build Coastguard Worker }
391*d9f75844SAndroid Build Coastguard Worker
392*d9f75844SAndroid Build Coastguard Worker if (dy != 0)
393*d9f75844SAndroid Build Coastguard Worker new_rows.swap(rows_);
394*d9f75844SAndroid Build Coastguard Worker }
395*d9f75844SAndroid Build Coastguard Worker
Swap(DesktopRegion * region)396*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Swap(DesktopRegion* region) {
397*d9f75844SAndroid Build Coastguard Worker rows_.swap(region->rows_);
398*d9f75844SAndroid Build Coastguard Worker }
399*d9f75844SAndroid Build Coastguard Worker
400*d9f75844SAndroid Build Coastguard Worker // static
CompareSpanRight(const RowSpan & r,int32_t value)401*d9f75844SAndroid Build Coastguard Worker bool DesktopRegion::CompareSpanRight(const RowSpan& r, int32_t value) {
402*d9f75844SAndroid Build Coastguard Worker return r.right < value;
403*d9f75844SAndroid Build Coastguard Worker }
404*d9f75844SAndroid Build Coastguard Worker
405*d9f75844SAndroid Build Coastguard Worker // static
CompareSpanLeft(const RowSpan & r,int32_t value)406*d9f75844SAndroid Build Coastguard Worker bool DesktopRegion::CompareSpanLeft(const RowSpan& r, int32_t value) {
407*d9f75844SAndroid Build Coastguard Worker return r.left < value;
408*d9f75844SAndroid Build Coastguard Worker }
409*d9f75844SAndroid Build Coastguard Worker
410*d9f75844SAndroid Build Coastguard Worker // static
AddSpanToRow(Row * row,int left,int right)411*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::AddSpanToRow(Row* row, int left, int right) {
412*d9f75844SAndroid Build Coastguard Worker // First check if the new span is located to the right of all existing spans.
413*d9f75844SAndroid Build Coastguard Worker // This is an optimization to avoid binary search in the case when rectangles
414*d9f75844SAndroid Build Coastguard Worker // are inserted sequentially from left to right.
415*d9f75844SAndroid Build Coastguard Worker if (row->spans.empty() || left > row->spans.back().right) {
416*d9f75844SAndroid Build Coastguard Worker row->spans.push_back(RowSpan(left, right));
417*d9f75844SAndroid Build Coastguard Worker return;
418*d9f75844SAndroid Build Coastguard Worker }
419*d9f75844SAndroid Build Coastguard Worker
420*d9f75844SAndroid Build Coastguard Worker // Find the first span that ends at or after `left`.
421*d9f75844SAndroid Build Coastguard Worker RowSpanSet::iterator start = std::lower_bound(
422*d9f75844SAndroid Build Coastguard Worker row->spans.begin(), row->spans.end(), left, CompareSpanRight);
423*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(start < row->spans.end());
424*d9f75844SAndroid Build Coastguard Worker
425*d9f75844SAndroid Build Coastguard Worker // Find the first span that starts after `right`.
426*d9f75844SAndroid Build Coastguard Worker RowSpanSet::iterator end =
427*d9f75844SAndroid Build Coastguard Worker std::lower_bound(start, row->spans.end(), right + 1, CompareSpanLeft);
428*d9f75844SAndroid Build Coastguard Worker if (end == row->spans.begin()) {
429*d9f75844SAndroid Build Coastguard Worker // There are no overlaps. Just insert the new span at the beginning.
430*d9f75844SAndroid Build Coastguard Worker row->spans.insert(row->spans.begin(), RowSpan(left, right));
431*d9f75844SAndroid Build Coastguard Worker return;
432*d9f75844SAndroid Build Coastguard Worker }
433*d9f75844SAndroid Build Coastguard Worker
434*d9f75844SAndroid Build Coastguard Worker // Move end to the left, so that it points the last span that ends at or
435*d9f75844SAndroid Build Coastguard Worker // before `right`.
436*d9f75844SAndroid Build Coastguard Worker end--;
437*d9f75844SAndroid Build Coastguard Worker
438*d9f75844SAndroid Build Coastguard Worker // At this point [start, end] is the range of spans that intersect with the
439*d9f75844SAndroid Build Coastguard Worker // new one.
440*d9f75844SAndroid Build Coastguard Worker if (end < start) {
441*d9f75844SAndroid Build Coastguard Worker // There are no overlaps. Just insert the new span at the correct position.
442*d9f75844SAndroid Build Coastguard Worker row->spans.insert(start, RowSpan(left, right));
443*d9f75844SAndroid Build Coastguard Worker return;
444*d9f75844SAndroid Build Coastguard Worker }
445*d9f75844SAndroid Build Coastguard Worker
446*d9f75844SAndroid Build Coastguard Worker left = std::min(left, start->left);
447*d9f75844SAndroid Build Coastguard Worker right = std::max(right, end->right);
448*d9f75844SAndroid Build Coastguard Worker
449*d9f75844SAndroid Build Coastguard Worker // Replace range [start, end] with the new span.
450*d9f75844SAndroid Build Coastguard Worker *start = RowSpan(left, right);
451*d9f75844SAndroid Build Coastguard Worker ++start;
452*d9f75844SAndroid Build Coastguard Worker ++end;
453*d9f75844SAndroid Build Coastguard Worker if (start < end)
454*d9f75844SAndroid Build Coastguard Worker row->spans.erase(start, end);
455*d9f75844SAndroid Build Coastguard Worker }
456*d9f75844SAndroid Build Coastguard Worker
457*d9f75844SAndroid Build Coastguard Worker // static
IsSpanInRow(const Row & row,const RowSpan & span)458*d9f75844SAndroid Build Coastguard Worker bool DesktopRegion::IsSpanInRow(const Row& row, const RowSpan& span) {
459*d9f75844SAndroid Build Coastguard Worker // Find the first span that starts at or after `span.left` and then check if
460*d9f75844SAndroid Build Coastguard Worker // it's the same span.
461*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator it = std::lower_bound(
462*d9f75844SAndroid Build Coastguard Worker row.spans.begin(), row.spans.end(), span.left, CompareSpanLeft);
463*d9f75844SAndroid Build Coastguard Worker return it != row.spans.end() && *it == span;
464*d9f75844SAndroid Build Coastguard Worker }
465*d9f75844SAndroid Build Coastguard Worker
466*d9f75844SAndroid Build Coastguard Worker // static
SubtractRows(const RowSpanSet & set_a,const RowSpanSet & set_b,RowSpanSet * output)467*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::SubtractRows(const RowSpanSet& set_a,
468*d9f75844SAndroid Build Coastguard Worker const RowSpanSet& set_b,
469*d9f75844SAndroid Build Coastguard Worker RowSpanSet* output) {
470*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!set_a.empty() && !set_b.empty());
471*d9f75844SAndroid Build Coastguard Worker
472*d9f75844SAndroid Build Coastguard Worker RowSpanSet::const_iterator it_b = set_b.begin();
473*d9f75844SAndroid Build Coastguard Worker
474*d9f75844SAndroid Build Coastguard Worker // Iterate over all spans in `set_a` adding parts of it that do not intersect
475*d9f75844SAndroid Build Coastguard Worker // with `set_b` to the `output`.
476*d9f75844SAndroid Build Coastguard Worker for (RowSpanSet::const_iterator it_a = set_a.begin(); it_a != set_a.end();
477*d9f75844SAndroid Build Coastguard Worker ++it_a) {
478*d9f75844SAndroid Build Coastguard Worker // If there is no intersection then append the current span and continue.
479*d9f75844SAndroid Build Coastguard Worker if (it_b == set_b.end() || it_a->right < it_b->left) {
480*d9f75844SAndroid Build Coastguard Worker output->push_back(*it_a);
481*d9f75844SAndroid Build Coastguard Worker continue;
482*d9f75844SAndroid Build Coastguard Worker }
483*d9f75844SAndroid Build Coastguard Worker
484*d9f75844SAndroid Build Coastguard Worker // Iterate over `set_b` spans that may intersect with `it_a`.
485*d9f75844SAndroid Build Coastguard Worker int pos = it_a->left;
486*d9f75844SAndroid Build Coastguard Worker while (it_b != set_b.end() && it_b->left < it_a->right) {
487*d9f75844SAndroid Build Coastguard Worker if (it_b->left > pos)
488*d9f75844SAndroid Build Coastguard Worker output->push_back(RowSpan(pos, it_b->left));
489*d9f75844SAndroid Build Coastguard Worker if (it_b->right > pos) {
490*d9f75844SAndroid Build Coastguard Worker pos = it_b->right;
491*d9f75844SAndroid Build Coastguard Worker if (pos >= it_a->right)
492*d9f75844SAndroid Build Coastguard Worker break;
493*d9f75844SAndroid Build Coastguard Worker }
494*d9f75844SAndroid Build Coastguard Worker ++it_b;
495*d9f75844SAndroid Build Coastguard Worker }
496*d9f75844SAndroid Build Coastguard Worker if (pos < it_a->right)
497*d9f75844SAndroid Build Coastguard Worker output->push_back(RowSpan(pos, it_a->right));
498*d9f75844SAndroid Build Coastguard Worker }
499*d9f75844SAndroid Build Coastguard Worker }
500*d9f75844SAndroid Build Coastguard Worker
Iterator(const DesktopRegion & region)501*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Iterator::Iterator(const DesktopRegion& region)
502*d9f75844SAndroid Build Coastguard Worker : region_(region),
503*d9f75844SAndroid Build Coastguard Worker row_(region.rows_.begin()),
504*d9f75844SAndroid Build Coastguard Worker previous_row_(region.rows_.end()) {
505*d9f75844SAndroid Build Coastguard Worker if (!IsAtEnd()) {
506*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(row_->second->spans.size(), 0);
507*d9f75844SAndroid Build Coastguard Worker row_span_ = row_->second->spans.begin();
508*d9f75844SAndroid Build Coastguard Worker UpdateCurrentRect();
509*d9f75844SAndroid Build Coastguard Worker }
510*d9f75844SAndroid Build Coastguard Worker }
511*d9f75844SAndroid Build Coastguard Worker
~Iterator()512*d9f75844SAndroid Build Coastguard Worker DesktopRegion::Iterator::~Iterator() {}
513*d9f75844SAndroid Build Coastguard Worker
IsAtEnd() const514*d9f75844SAndroid Build Coastguard Worker bool DesktopRegion::Iterator::IsAtEnd() const {
515*d9f75844SAndroid Build Coastguard Worker return row_ == region_.rows_.end();
516*d9f75844SAndroid Build Coastguard Worker }
517*d9f75844SAndroid Build Coastguard Worker
Advance()518*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Iterator::Advance() {
519*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!IsAtEnd());
520*d9f75844SAndroid Build Coastguard Worker
521*d9f75844SAndroid Build Coastguard Worker while (true) {
522*d9f75844SAndroid Build Coastguard Worker ++row_span_;
523*d9f75844SAndroid Build Coastguard Worker if (row_span_ == row_->second->spans.end()) {
524*d9f75844SAndroid Build Coastguard Worker previous_row_ = row_;
525*d9f75844SAndroid Build Coastguard Worker ++row_;
526*d9f75844SAndroid Build Coastguard Worker if (row_ != region_.rows_.end()) {
527*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(row_->second->spans.size(), 0);
528*d9f75844SAndroid Build Coastguard Worker row_span_ = row_->second->spans.begin();
529*d9f75844SAndroid Build Coastguard Worker }
530*d9f75844SAndroid Build Coastguard Worker }
531*d9f75844SAndroid Build Coastguard Worker
532*d9f75844SAndroid Build Coastguard Worker if (IsAtEnd())
533*d9f75844SAndroid Build Coastguard Worker return;
534*d9f75844SAndroid Build Coastguard Worker
535*d9f75844SAndroid Build Coastguard Worker // If the same span exists on the previous row then skip it, as we've
536*d9f75844SAndroid Build Coastguard Worker // already returned this span merged into the previous one, via
537*d9f75844SAndroid Build Coastguard Worker // UpdateCurrentRect().
538*d9f75844SAndroid Build Coastguard Worker if (previous_row_ != region_.rows_.end() &&
539*d9f75844SAndroid Build Coastguard Worker previous_row_->second->bottom == row_->second->top &&
540*d9f75844SAndroid Build Coastguard Worker IsSpanInRow(*previous_row_->second, *row_span_)) {
541*d9f75844SAndroid Build Coastguard Worker continue;
542*d9f75844SAndroid Build Coastguard Worker }
543*d9f75844SAndroid Build Coastguard Worker
544*d9f75844SAndroid Build Coastguard Worker break;
545*d9f75844SAndroid Build Coastguard Worker }
546*d9f75844SAndroid Build Coastguard Worker
547*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!IsAtEnd());
548*d9f75844SAndroid Build Coastguard Worker UpdateCurrentRect();
549*d9f75844SAndroid Build Coastguard Worker }
550*d9f75844SAndroid Build Coastguard Worker
UpdateCurrentRect()551*d9f75844SAndroid Build Coastguard Worker void DesktopRegion::Iterator::UpdateCurrentRect() {
552*d9f75844SAndroid Build Coastguard Worker // Merge the current rectangle with the matching spans from later rows.
553*d9f75844SAndroid Build Coastguard Worker int bottom;
554*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator bottom_row = row_;
555*d9f75844SAndroid Build Coastguard Worker Rows::const_iterator previous;
556*d9f75844SAndroid Build Coastguard Worker do {
557*d9f75844SAndroid Build Coastguard Worker bottom = bottom_row->second->bottom;
558*d9f75844SAndroid Build Coastguard Worker previous = bottom_row;
559*d9f75844SAndroid Build Coastguard Worker ++bottom_row;
560*d9f75844SAndroid Build Coastguard Worker } while (bottom_row != region_.rows_.end() &&
561*d9f75844SAndroid Build Coastguard Worker previous->second->bottom == bottom_row->second->top &&
562*d9f75844SAndroid Build Coastguard Worker IsSpanInRow(*bottom_row->second, *row_span_));
563*d9f75844SAndroid Build Coastguard Worker rect_ = DesktopRect::MakeLTRB(row_span_->left, row_->second->top,
564*d9f75844SAndroid Build Coastguard Worker row_span_->right, bottom);
565*d9f75844SAndroid Build Coastguard Worker }
566*d9f75844SAndroid Build Coastguard Worker
567*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
568