tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

desktop_region.cc (17532B)


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