tor-browser

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

nsRegion.h (73483B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef nsRegion_h__
      8 #define nsRegion_h__
      9 
     10 #include <stddef.h>  // for size_t
     11 #include <stdint.h>  // for uint32_t, uint64_t
     12 
     13 #include <ostream>  // for std::ostream
     14 #include <utility>  // for mozilla::Move
     15 
     16 #include "mozilla/ArrayView.h"      // for ArrayView
     17 #include "mozilla/gfx/MatrixFwd.h"  // for mozilla::gfx::Matrix4x4
     18 #include "nsCoord.h"                // for nscoord
     19 #include "nsMargin.h"               // for nsIntMargin
     20 #include "nsPoint.h"                // for nsIntPoint, nsPoint
     21 #include "nsRect.h"                 // for mozilla::gfx::IntRect, nsRect
     22 #include "nsRectAbsolute.h"
     23 #include "nsRegionFwd.h"  // for nsIntRegion
     24 #include "nsString.h"     // for nsCString
     25 #include "nsTArray.h"
     26 #include "pixman.h"
     27 
     28 // Uncomment this line to get additional integrity checking.
     29 // #define DEBUG_REGIONS
     30 #ifdef DEBUG_REGIONS
     31 #  include <sstream>
     32 #endif
     33 
     34 /* For information on the internal representation look at pixman-region.c
     35 *
     36 * This replaces an older homebrew implementation of nsRegion. The
     37 * representation used here may use more rectangles than nsRegion however, the
     38 * representation is canonical.  This means that there's no need for an
     39 * Optimize() method because for a paticular region there is only one
     40 * representation. This means that nsIntRegion will have more predictable
     41 * performance characteristics than the old nsRegion and should not become
     42 * degenerate.
     43 *
     44 * The pixman region code originates from X11 which has spread to a variety of
     45 * projects including Qt, Gtk, Wine. It should perform reasonably well.
     46 */
     47 
     48 enum class VisitSide { TOP, BOTTOM, LEFT, RIGHT };
     49 
     50 namespace regiondetails {
     51 struct Band;
     52 }
     53 
     54 template <>
     55 struct nsTArray_RelocationStrategy<regiondetails::Band> {
     56  typedef nsTArray_RelocateUsingMoveConstructor<regiondetails::Band> Type;
     57 };
     58 
     59 namespace regiondetails {
     60 
     61 template <typename T, typename E>
     62 class UncheckedArray : public T {
     63 public:
     64  using T::Elements;
     65  using T::Length;
     66 
     67  UncheckedArray() = default;
     68  MOZ_IMPLICIT UncheckedArray(T&& aSrc) : T(std::move(aSrc)) {}
     69 
     70  E& operator[](size_t aIndex) { return Elements()[aIndex]; }
     71  const E& operator[](size_t aIndex) const { return Elements()[aIndex]; }
     72  E& LastElement() { return Elements()[Length() - 1]; }
     73  const E& LastElement() const { return Elements()[Length() - 1]; }
     74 
     75  using iterator = E*;
     76  using const_iterator = const E*;
     77 
     78  iterator begin() { return iterator(Elements()); }
     79  const_iterator begin() const { return const_iterator(Elements()); }
     80  const_iterator cbegin() const { return begin(); }
     81  iterator end() { return iterator(Elements() + Length()); }
     82  const_iterator end() const { return const_iterator(Elements() + Length()); }
     83  const_iterator cend() const { return end(); }
     84 };
     85 
     86 struct Strip {
     87  // Default constructor should never be called, but is required for
     88  // vector::resize to compile.
     89  Strip() { MOZ_CRASH(); }
     90  Strip(int32_t aLeft, int32_t aRight) : left(aLeft), right(aRight) {}
     91 
     92  bool operator!=(const Strip& aOther) const {
     93    return left != aOther.left || right != aOther.right;
     94  }
     95 
     96  uint32_t Size() const { return right - left; }
     97 
     98  int32_t left;
     99  int32_t right;
    100 };
    101 
    102 struct Band {
    103  using Strip = regiondetails::Strip;
    104 #ifndef DEBUG
    105  using StripArray =
    106      regiondetails::UncheckedArray<CopyableAutoTArray<Strip, 2>, Strip>;
    107 #else
    108  using StripArray = CopyableAutoTArray<Strip, 2>;
    109 #endif
    110 
    111  MOZ_IMPLICIT Band(const nsRectAbsolute& aRect)
    112      : top(aRect.Y()), bottom(aRect.YMost()) {
    113    mStrips.AppendElement(Strip{aRect.X(), aRect.XMost()});
    114  }
    115 
    116  Band(const Band& aOther) = default;
    117  Band(Band&& aOther) = default;
    118 
    119  void InsertStrip(const Strip& aStrip) {
    120    for (size_t i = 0; i < mStrips.Length(); i++) {
    121      Strip& strip = mStrips[i];
    122      if (strip.left > aStrip.right) {
    123        // Current strip is beyond aStrip, insert aStrip before.
    124        mStrips.InsertElementAt(i, aStrip);
    125        return;
    126      }
    127 
    128      if (strip.right < aStrip.left) {
    129        // Current strip is before aStrip, try the next.
    130        continue;
    131      }
    132 
    133      // Current strip intersects with aStrip, extend to the lext.
    134      strip.left = std::min(strip.left, aStrip.left);
    135 
    136      if (strip.right >= aStrip.right) {
    137        // Current strip extends beyond aStrip, done.
    138        return;
    139      }
    140 
    141      size_t next = i;
    142      next++;
    143      // Consume any subsequent strips intersecting with aStrip.
    144      while (next < mStrips.Length() && mStrips[next].left <= aStrip.right) {
    145        strip.right = mStrips[next].right;
    146 
    147        mStrips.RemoveElementAt(next);
    148      }
    149 
    150      // Extend the strip in case the aStrip goes on beyond it.
    151      strip.right = std::max(strip.right, aStrip.right);
    152      return;
    153    }
    154    mStrips.AppendElement(aStrip);
    155  }
    156 
    157  void SubStrip(const Strip& aStrip) {
    158    for (size_t i = 0; i < mStrips.Length(); i++) {
    159      Strip& strip = mStrips[i];
    160      if (strip.left > aStrip.right) {
    161        // Strip is entirely to the right of aStrip. Done.
    162        return;
    163      }
    164 
    165      if (strip.right < aStrip.left) {
    166        // Strip is entirely to the left of aStrip. Move on.
    167        continue;
    168      }
    169 
    170      if (strip.left < aStrip.left) {
    171        if (strip.right <= aStrip.right) {
    172          strip.right = aStrip.left;
    173          // This strip lies to the left of the start of aStrip.
    174          continue;
    175        }
    176 
    177        // aStrip is completely contained by this strip.
    178        Strip newStrip(aStrip.right, strip.right);
    179        strip.right = aStrip.left;
    180        if (i < mStrips.Length()) {
    181          i++;
    182          mStrips.InsertElementAt(i, newStrip);
    183        } else {
    184          mStrips.AppendElement(newStrip);
    185        }
    186        return;
    187      }
    188 
    189      // This strip lies to the right of the start of aStrip.
    190      if (strip.right <= aStrip.right) {
    191        // aStrip completely contains this strip.
    192        mStrips.RemoveElementAt(i);
    193        // Make sure we evaluate the strip now at i. This loop will increment.
    194        i--;
    195        continue;
    196      }
    197      strip.left = aStrip.right;
    198      return;
    199    }
    200  }
    201 
    202  bool Intersects(const Strip& aStrip) const {
    203    for (const Strip& strip : mStrips) {
    204      if (strip.left >= aStrip.right) {
    205        return false;
    206      }
    207 
    208      if (strip.right <= aStrip.left) {
    209        continue;
    210      }
    211 
    212      return true;
    213    }
    214    return false;
    215  }
    216 
    217  bool IntersectStripBounds(Strip& aStrip) const {
    218    bool intersected = false;
    219 
    220    int32_t rightMost;
    221    for (const Strip& strip : mStrips) {
    222      if (strip.left > aStrip.right) {
    223        break;
    224      }
    225 
    226      if (strip.right <= aStrip.left) {
    227        continue;
    228      }
    229 
    230      if (!intersected) {
    231        // First intersection, this is where the left side begins.
    232        aStrip.left = std::max(aStrip.left, strip.left);
    233      }
    234 
    235      intersected = true;
    236      // Expand to the right for each intersecting strip found.
    237      rightMost = std::min(strip.right, aStrip.right);
    238    }
    239 
    240    if (intersected) {
    241      aStrip.right = rightMost;
    242    } else {
    243      aStrip.right = aStrip.left = 0;
    244    }
    245    return intersected;
    246  }
    247 
    248  bool ContainsStrip(const Strip& aStrip) const {
    249    for (const Strip& strip : mStrips) {
    250      if (strip.left > aStrip.left) {
    251        return false;
    252      }
    253 
    254      if (strip.right >= aStrip.right) {
    255        return true;
    256      }
    257    }
    258    return false;
    259  }
    260 
    261  bool EqualStrips(const Band& aBand) const {
    262    if (mStrips.Length() != aBand.mStrips.Length()) {
    263      return false;
    264    }
    265 
    266    for (auto iter1 = mStrips.begin(), iter2 = aBand.mStrips.begin();
    267         iter1 != mStrips.end(); iter1++, iter2++) {
    268      if (*iter1 != *iter2) {
    269        return false;
    270      }
    271    }
    272 
    273    return true;
    274  }
    275 
    276  void IntersectStrip(const Strip& aStrip) {
    277    size_t i = 0;
    278 
    279    while (i < mStrips.Length()) {
    280      Strip& strip = mStrips[i];
    281      if (strip.right <= aStrip.left) {
    282        mStrips.RemoveElementAt(i);
    283        continue;
    284      }
    285 
    286      if (strip.left >= aStrip.right) {
    287        mStrips.TruncateLength(i);
    288        return;
    289      }
    290 
    291      strip.left = std::max(aStrip.left, strip.left);
    292      strip.right = std::min(aStrip.right, strip.right);
    293      i++;
    294    }
    295  }
    296 
    297  void IntersectStrips(const Band& aOther) {
    298    auto iter = mStrips.begin();
    299    auto iterOther = aOther.mStrips.begin();
    300 
    301    StripArray newStrips;
    302 
    303    // This function finds the intersection between two sets of strips.
    304    while (true) {
    305      while (true) {
    306        while (iter != mStrips.end() && iter->right <= iterOther->left) {
    307          // Increment our current strip until it ends beyond aOther's current
    308          // strip.
    309          iter++;
    310        }
    311 
    312        if (iter == mStrips.end()) {
    313          // End of our strips. Done.
    314          break;
    315        }
    316 
    317        while (iterOther != aOther.mStrips.end() &&
    318               iterOther->right <= iter->left) {
    319          // Increment aOther's current strip until it lies beyond our current
    320          // strip.
    321          iterOther++;
    322        }
    323 
    324        if (iterOther == aOther.mStrips.end()) {
    325          // End of aOther's strips. Done.
    326          break;
    327        }
    328 
    329        if (iterOther->left < iter->right) {
    330          // Intersection!
    331          break;
    332        }
    333      }
    334 
    335      if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) {
    336        break;
    337      }
    338 
    339      newStrips.AppendElement(Strip(std::max(iter->left, iterOther->left),
    340                                    std::min(iterOther->right, iter->right)));
    341 
    342      if (iterOther->right < iter->right) {
    343        iterOther++;
    344        if (iterOther == aOther.mStrips.end()) {
    345          break;
    346        }
    347      } else {
    348        iter++;
    349      }
    350    }
    351 
    352    mStrips = std::move(newStrips);
    353  }
    354 
    355  bool Intersects(const Band& aOther) const {
    356    auto iter = mStrips.begin();
    357    auto iterOther = aOther.mStrips.begin();
    358 
    359    // This function finds the intersection between two sets of strips.
    360    while (true) {
    361      while (true) {
    362        while (iter != mStrips.end() && iter->right <= iterOther->left) {
    363          // Increment our current strip until it ends beyond aOther's current
    364          // strip.
    365          iter++;
    366        }
    367 
    368        if (iter == mStrips.end()) {
    369          // End of our strips. Done.
    370          break;
    371        }
    372 
    373        while (iterOther != aOther.mStrips.end() &&
    374               iterOther->right <= iter->left) {
    375          // Increment aOther's current strip until it lies beyond our current
    376          // strip.
    377          iterOther++;
    378        }
    379 
    380        if (iterOther == aOther.mStrips.end()) {
    381          // End of aOther's strips. Done.
    382          break;
    383        }
    384 
    385        if (iterOther->left < iter->right) {
    386          // Intersection!
    387          break;
    388        }
    389      }
    390 
    391      if (iter == mStrips.end() || iterOther == aOther.mStrips.end()) {
    392        break;
    393      }
    394 
    395      return true;
    396    }
    397    return false;
    398  }
    399 
    400  void SubStrips(const Band& aOther) {
    401    size_t idx = 0;
    402    auto iterOther = aOther.mStrips.begin();
    403 
    404    // This function finds the intersection between two sets of strips.
    405    while (true) {
    406      while (true) {
    407        while (idx < mStrips.Length() &&
    408               mStrips[idx].right <= iterOther->left) {
    409          // Increment our current strip until it ends beyond aOther's current
    410          // strip.
    411          idx++;
    412        }
    413 
    414        if (idx == mStrips.Length()) {
    415          // End of our strips. Done.
    416          break;
    417        }
    418 
    419        while (iterOther != aOther.mStrips.end() &&
    420               iterOther->right <= mStrips[idx].left) {
    421          // Increment aOther's current strip until it lies beyond our current
    422          // strip.
    423          iterOther++;
    424        }
    425 
    426        if (iterOther == aOther.mStrips.end()) {
    427          // End of aOther's strips. Done.
    428          break;
    429        }
    430 
    431        if (iterOther->left < mStrips[idx].right) {
    432          // Intersection!
    433          break;
    434        }
    435      }
    436 
    437      if (idx == mStrips.Length() || iterOther == aOther.mStrips.end()) {
    438        break;
    439      }
    440 
    441      if (mStrips[idx].left < iterOther->left) {
    442        size_t oldIdx = idx;
    443        // Our strip starts beyond other's
    444        if (mStrips[idx].right > iterOther->right) {
    445          // Our strip ends beyond other's as well.
    446          Strip newStrip(mStrips[idx]);
    447          newStrip.left = iterOther->right;
    448          mStrips.InsertElementAt(idx + 1, newStrip);
    449          idx++;
    450        }
    451        mStrips[oldIdx].right = iterOther->left;
    452        // Either idx was just incremented, or the current index no longer
    453        // intersects with iterOther.
    454        continue;
    455      } else if (mStrips[idx].right > iterOther->right) {
    456        mStrips[idx].left = iterOther->right;
    457        // Current strip no longer intersects, continue.
    458        iterOther++;
    459        if (iterOther == aOther.mStrips.end()) {
    460          break;
    461        }
    462        continue;
    463      }
    464 
    465      // Our current strip is completely contained by the other strip.
    466      mStrips.RemoveElementAt(idx);
    467    }
    468  }
    469 
    470  int32_t top;
    471  int32_t bottom;
    472  StripArray mStrips;
    473 };
    474 }  // namespace regiondetails
    475 
    476 class nsRegion {
    477 public:
    478  using Band = regiondetails::Band;
    479  using Strip = regiondetails::Strip;
    480 #ifndef DEBUG
    481  using BandArray = regiondetails::UncheckedArray<nsTArray<Band>, Band>;
    482  using StripArray = regiondetails::UncheckedArray<AutoTArray<Strip, 2>, Strip>;
    483 #else
    484  using BandArray = nsTArray<Band>;
    485  using StripArray = AutoTArray<Strip, 2>;
    486 #endif
    487 
    488  typedef nsRect RectType;
    489  typedef nsPoint PointType;
    490  typedef nsMargin MarginType;
    491 
    492  nsRegion() = default;
    493  MOZ_IMPLICIT nsRegion(const nsRect& aRect) {
    494    mBounds = nsRectAbsolute::FromRect(aRect);
    495  }
    496  MOZ_IMPLICIT nsRegion(const nsRectAbsolute& aRect) { mBounds = aRect; }
    497  explicit nsRegion(mozilla::gfx::ArrayView<pixman_box32_t> aRects) {
    498    for (uint32_t i = 0; i < aRects.Length(); i++) {
    499      AddRect(BoxToRect(aRects[i]));
    500    }
    501  }
    502 
    503  nsRegion(const nsRegion& aRegion) { Copy(aRegion); }
    504  nsRegion(nsRegion&& aRegion)
    505      : mBands(std::move(aRegion.mBands)), mBounds(aRegion.mBounds) {
    506    aRegion.SetEmpty();
    507  }
    508  nsRegion& operator=(nsRegion&& aRegion) {
    509    mBands = std::move(aRegion.mBands);
    510    mBounds = aRegion.mBounds;
    511    aRegion.SetEmpty();
    512    return *this;
    513  }
    514  nsRegion& operator=(const nsRect& aRect) {
    515    Copy(aRect);
    516    return *this;
    517  }
    518  nsRegion& operator=(const nsRegion& aRegion) {
    519    Copy(aRegion);
    520    return *this;
    521  }
    522  bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); }
    523  bool operator!=(const nsRegion& aRgn) const { return !(*this == aRgn); }
    524 
    525  friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m);
    526  void OutputToStream(std::string aObjName, std::ostream& stream) const;
    527 
    528 private:
    529 #ifdef DEBUG_REGIONS
    530  class OperationStringGenerator {
    531   public:
    532    virtual ~OperationStringGenerator() = default;
    533 
    534    virtual void OutputOp() = 0;
    535  };
    536 #endif
    537 public:
    538  void AssertStateInternal() const;
    539  void AssertState() const {
    540 #ifdef DEBUG_REGIONS
    541    AssertStateInternal();
    542 #endif
    543  }
    544 
    545 private:
    546  void And(BandArray& aOut, const BandArray& aIn1, const BandArray& aIn2) {
    547    size_t idx = 0;
    548    size_t idxOther = 0;
    549 
    550    // This algorithm essentially forms a new list of bands, by iterating over
    551    // both regions' lists of band simultaneously, and building a new band
    552    // wherever the two regions intersect.
    553    while (true) {
    554      while (true) {
    555        while (idx != aIn1.Length() && aIn1[idx].bottom <= aIn2[idxOther].top) {
    556          // Increment our current band until it ends beyond aOther's current
    557          // band.
    558          idx++;
    559        }
    560 
    561        if (idx == aIn1.Length()) {
    562          // This region is out of bands, the other region's future bands are
    563          // ignored.
    564          break;
    565        }
    566 
    567        while (idxOther != aIn2.Length() &&
    568               aIn2[idxOther].bottom <= aIn1[idx].top) {
    569          // Increment aOther's current band until it ends beyond our current
    570          // band.
    571          idxOther++;
    572        }
    573 
    574        if (idxOther == aIn2.Length()) {
    575          // The other region's bands are all processed, all our future bands
    576          // are ignored.
    577          break;
    578        }
    579 
    580        if (aIn2[idxOther].top < aIn1[idx].bottom) {
    581          // We know the other band's bottom lies beyond our band's top because
    582          // otherwise we would've incremented above. Intersecting bands found.
    583          break;
    584        }
    585      }
    586 
    587      if (idx == aIn1.Length() || idxOther == aIn2.Length()) {
    588        // The above loop executed a break because we're done.
    589        break;
    590      }
    591 
    592      Band newBand(aIn1[idx]);
    593      // The new band is the intersection of the two current bands from both
    594      // regions.
    595      newBand.top = std::max(aIn1[idx].top, aIn2[idxOther].top);
    596      newBand.bottom = std::min(aIn1[idx].bottom, aIn2[idxOther].bottom);
    597      newBand.IntersectStrips(aIn2[idxOther]);
    598 
    599      if (newBand.mStrips.Length()) {
    600        // The intersecting area of the bands had overlapping strips, if it is
    601        // identical to the band above it merge, otherwise append.
    602        if (aOut.Length() && aOut.LastElement().bottom == newBand.top &&
    603            aOut.LastElement().EqualStrips(newBand)) {
    604          aOut.LastElement().bottom = newBand.bottom;
    605        } else {
    606          aOut.AppendElement(std::move(newBand));
    607        }
    608      }
    609 
    610      if (aIn2[idxOther].bottom < aIn1[idx].bottom) {
    611        idxOther++;
    612        if (idxOther == aIn2.Length()) {
    613          // Since we will access idxOther the next iteration, check if we're
    614          // not done.
    615          break;
    616        }
    617      } else {
    618        // No need to check here since we do at the beginning of the next
    619        // iteration.
    620        idx++;
    621      }
    622    }
    623  }
    624 
    625 public:
    626  nsRegion& AndWith(const nsRegion& aRegion) {
    627 #ifdef DEBUG_REGIONS
    628    class OperationStringGeneratorAndWith : public OperationStringGenerator {
    629     public:
    630      OperationStringGeneratorAndWith(nsRegion& aRegion,
    631                                      const nsRegion& aOtherRegion)
    632          : mRegion(&aRegion),
    633            mRegionCopy(aRegion),
    634            mOtherRegion(aOtherRegion) {
    635        aRegion.mCurrentOpGenerator = this;
    636      }
    637      virtual ~OperationStringGeneratorAndWith() {
    638        mRegion->mCurrentOpGenerator = nullptr;
    639      }
    640 
    641      virtual void OutputOp() override {
    642        std::stringstream stream;
    643        mRegionCopy.OutputToStream("r1", stream);
    644        mOtherRegion.OutputToStream("r2", stream);
    645        stream << "r1.AndWith(r2);\n";
    646        gfxCriticalError() << stream.str();
    647      }
    648 
    649     private:
    650      nsRegion* mRegion;
    651      nsRegion mRegionCopy;
    652      nsRegion mOtherRegion;
    653    };
    654 
    655    OperationStringGeneratorAndWith opGenerator(*this, aRegion);
    656 #endif
    657    if (mBounds.IsEmpty()) {
    658      // Region is empty, stays empty.
    659      return *this;
    660    }
    661 
    662    if (aRegion.IsEmpty()) {
    663      SetEmpty();
    664      return *this;
    665    }
    666 
    667    if (aRegion.mBands.IsEmpty()) {
    668      // Other region is a rect.
    669      return AndWith(aRegion.mBounds);
    670    }
    671 
    672    if (mBands.IsEmpty()) {
    673      mBands.AppendElement(mBounds);
    674    }
    675 
    676    BandArray newBands;
    677 
    678    And(newBands, mBands, aRegion.mBands);
    679 
    680    mBands = std::move(newBands);
    681    if (!mBands.Length()) {
    682      mBounds = nsRectAbsolute();
    683    } else {
    684      mBounds = CalculateBounds();
    685    }
    686 
    687    EnsureSimplified();
    688    AssertState();
    689    return *this;
    690  }
    691 
    692  nsRegion& AndWith(const nsRectAbsolute& aRect) {
    693 #ifdef DEBUG_REGIONS
    694    class OperationStringGeneratorAndWith : public OperationStringGenerator {
    695     public:
    696      OperationStringGeneratorAndWith(nsRegion& aRegion,
    697                                      const nsRectAbsolute& aRect)
    698          : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) {
    699        aRegion.mCurrentOpGenerator = this;
    700      }
    701      virtual ~OperationStringGeneratorAndWith() {
    702        mRegion->mCurrentOpGenerator = nullptr;
    703      }
    704 
    705      virtual void OutputOp() override {
    706        std::stringstream stream;
    707        mRegionCopy.OutputToStream("r", stream);
    708        stream << "r.AndWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", "
    709               << mRect.Width() << ", " << mRect.Height() << "));\n";
    710        gfxCriticalError() << stream.str();
    711      }
    712 
    713     private:
    714      nsRegion* mRegion;
    715      nsRegion mRegionCopy;
    716      nsRectAbsolute mRect;
    717    };
    718 
    719    OperationStringGeneratorAndWith opGenerator(*this, aRect);
    720 #endif
    721    if (aRect.IsEmpty()) {
    722      SetEmpty();
    723      return *this;
    724    }
    725 
    726    if (mBands.IsEmpty()) {
    727      mBounds = mBounds.Intersect(aRect);
    728      return *this;
    729    }
    730 
    731    size_t idx = 0;
    732 
    733    size_t removeStart = 0;
    734 
    735    // This removes all bands that do not intersect with aRect, and intersects
    736    // the remaining ones with aRect.
    737 
    738    // Start by figuring out how much to remove from the start.
    739    while (idx != mBands.Length() && mBands[idx].bottom <= aRect.Y()) {
    740      idx++;
    741    }
    742 
    743    // We'll remove these later to avoid needless copying in the array.
    744    removeStart = idx;
    745 
    746    while (idx != mBands.Length()) {
    747      if (mBands[idx].top >= aRect.YMost()) {
    748        mBands.TruncateLength(idx);
    749        break;
    750      }
    751 
    752      mBands[idx].top = std::max(mBands[idx].top, aRect.Y());
    753      mBands[idx].bottom = std::min(mBands[idx].bottom, aRect.YMost());
    754 
    755      mBands[idx].IntersectStrip(Strip(aRect.X(), aRect.XMost()));
    756 
    757      if (!mBands[idx].mStrips.Length()) {
    758        mBands.RemoveElementAt(idx);
    759      } else {
    760        if (idx > removeStart) {
    761          CompressBefore(idx);
    762        }
    763        idx++;
    764      }
    765    }
    766 
    767    if (removeStart) {
    768      mBands.RemoveElementsAt(0, removeStart);
    769    }
    770 
    771    if (mBands.Length()) {
    772      mBounds = CalculateBounds();
    773    } else {
    774      mBounds.SetEmpty();
    775    }
    776    EnsureSimplified();
    777    AssertState();
    778    return *this;
    779  }
    780  nsRegion& AndWith(const nsRect& aRect) {
    781    return AndWith(nsRectAbsolute::FromRect(aRect));
    782  }
    783  nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) {
    784    if (&aRgn1 == this) {
    785      return AndWith(aRgn2);
    786    }
    787 #ifdef DEBUG_REGIONS
    788    class OperationStringGeneratorAnd : public OperationStringGenerator {
    789     public:
    790      OperationStringGeneratorAnd(nsRegion& aRegion, const nsRegion& aRegion1,
    791                                  const nsRegion& aRegion2)
    792          : mRegion(&aRegion), mRegion1(aRegion1), mRegion2(aRegion2) {
    793        aRegion.mCurrentOpGenerator = this;
    794      }
    795      virtual ~OperationStringGeneratorAnd() {
    796        mRegion->mCurrentOpGenerator = nullptr;
    797      }
    798 
    799      virtual void OutputOp() override {
    800        std::stringstream stream;
    801        mRegion1.OutputToStream("r1", stream);
    802        mRegion2.OutputToStream("r2", stream);
    803        stream << "nsRegion r3;\nr3.And(r1, r2);\n";
    804        gfxCriticalError() << stream.str();
    805      }
    806 
    807     private:
    808      nsRegion* mRegion;
    809      nsRegion mRegion1;
    810      nsRegion mRegion2;
    811    };
    812 
    813    OperationStringGeneratorAnd opGenerator(*this, aRgn1, aRgn2);
    814 #endif
    815    mBands.Clear();
    816 
    817    if (aRgn1.IsEmpty() || aRgn2.IsEmpty()) {
    818      mBounds.SetEmpty();
    819      return *this;
    820    }
    821 
    822    if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) {
    823      mBounds = aRgn1.mBounds.Intersect(aRgn2.mBounds);
    824      return *this;
    825    } else if (aRgn1.mBands.IsEmpty()) {
    826      return And(aRgn2, aRgn1.mBounds);
    827    } else if (aRgn2.mBands.IsEmpty()) {
    828      return And(aRgn1, aRgn2.mBounds);
    829    }
    830 
    831    And(mBands, aRgn1.mBands, aRgn2.mBands);
    832 
    833    if (!mBands.Length()) {
    834      mBounds = nsRectAbsolute();
    835    } else {
    836      mBounds = CalculateBounds();
    837    }
    838 
    839    EnsureSimplified();
    840    AssertState();
    841    return *this;
    842  }
    843  nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) {
    844    return And(aRegion, aRect);
    845  }
    846  nsRegion& And(const nsRegion& aRegion, const nsRectAbsolute& aRect) {
    847    if (&aRegion == this) {
    848      return AndWith(aRect);
    849    }
    850 #ifdef DEBUG_REGIONS
    851    class OperationStringGeneratorAnd : public OperationStringGenerator {
    852     public:
    853      OperationStringGeneratorAnd(nsRegion& aThisRegion,
    854                                  const nsRegion& aRegion,
    855                                  const nsRectAbsolute& aRect)
    856          : mThisRegion(&aThisRegion), mRegion(aRegion), mRect(aRect) {
    857        aThisRegion.mCurrentOpGenerator = this;
    858      }
    859      virtual ~OperationStringGeneratorAnd() {
    860        mThisRegion->mCurrentOpGenerator = nullptr;
    861      }
    862 
    863      virtual void OutputOp() override {
    864        std::stringstream stream;
    865        mRegion.OutputToStream("r", stream);
    866        stream << "nsRegion r2;\nr.And(r2, nsRect(" << mRect.X() << ", "
    867               << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height()
    868               << "));\n";
    869        gfxCriticalError() << stream.str();
    870      }
    871 
    872     private:
    873      nsRegion* mThisRegion;
    874      nsRegion mRegion;
    875      nsRectAbsolute mRect;
    876    };
    877 
    878    OperationStringGeneratorAnd opGenerator(*this, aRegion, aRect);
    879 #endif
    880    mBands.Clear();
    881 
    882    if (aRect.IsEmpty()) {
    883      mBounds.SetEmpty();
    884      return *this;
    885    }
    886 
    887    if (aRegion.mBands.IsEmpty()) {
    888      mBounds = aRegion.mBounds.Intersect(aRect);
    889      return *this;
    890    }
    891 
    892    size_t idx = 0;
    893    const BandArray& bands = aRegion.mBands;
    894 
    895    mBands.SetCapacity(bands.Length() + 3);
    896    while (idx != bands.Length()) {
    897      // Ignore anything before.
    898      if (bands[idx].bottom <= aRect.Y()) {
    899        idx++;
    900        continue;
    901      }
    902      // We're done once we've reached the bottom.
    903      if (bands[idx].top >= aRect.YMost()) {
    904        break;
    905      }
    906 
    907      // Now deal with bands actually intersecting the rectangle.
    908      Band newBand(bands[idx]);
    909      newBand.top = std::max(bands[idx].top, aRect.Y());
    910      newBand.bottom = std::min(bands[idx].bottom, aRect.YMost());
    911 
    912      newBand.IntersectStrip(Strip(aRect.X(), aRect.XMost()));
    913 
    914      if (newBand.mStrips.Length()) {
    915        if (!mBands.IsEmpty() && newBand.top == mBands.LastElement().bottom &&
    916            newBand.EqualStrips(mBands.LastElement())) {
    917          mBands.LastElement().bottom = newBand.bottom;
    918        } else {
    919          mBands.AppendElement(std::move(newBand));
    920        }
    921      }
    922      idx++;
    923    }
    924 
    925    if (mBands.Length()) {
    926      mBounds = CalculateBounds();
    927    } else {
    928      mBounds.SetEmpty();
    929    }
    930 
    931    EnsureSimplified();
    932    AssertState();
    933    return *this;
    934  }
    935  nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) {
    936    return And(aRegion, nsRectAbsolute::FromRect(aRect));
    937  }
    938  nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) {
    939    nsRect tmpRect;
    940 
    941    tmpRect.IntersectRect(aRect1, aRect2);
    942    return Copy(tmpRect);
    943  }
    944 
    945  nsRegion& OrWith(const nsRegion& aOther) {
    946    for (RectIterator idx(aOther); !idx.Done(); idx.Next()) {
    947      AddRect(idx.GetAbsolute());
    948    }
    949    return *this;
    950  }
    951  nsRegion& OrWith(const nsRect& aOther) {
    952    AddRect(nsRectAbsolute::FromRect(aOther));
    953    return *this;
    954  }
    955  nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) {
    956    if (&aRgn1 != this) {
    957      *this = aRgn1;
    958    }
    959    for (RectIterator idx(aRgn2); !idx.Done(); idx.Next()) {
    960      AddRect(idx.GetAbsolute());
    961    }
    962    return *this;
    963  }
    964  nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) {
    965    if (&aRegion != this) {
    966      *this = aRegion;
    967    }
    968    AddRect(nsRectAbsolute::FromRect(aRect));
    969    return *this;
    970  }
    971  nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) {
    972    return Or(aRegion, aRect);
    973  }
    974  nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) {
    975    Copy(aRect1);
    976    return Or(*this, aRect2);
    977  }
    978 
    979  nsRegion& XorWith(const nsRegion& aOther) { return Xor(*this, aOther); }
    980  nsRegion& XorWith(const nsRect& aOther) { return Xor(*this, aOther); }
    981  nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) {
    982    // this could be implemented better if pixman had direct
    983    // support for xoring regions.
    984    nsRegion p;
    985    p.Sub(aRgn1, aRgn2);
    986    nsRegion q;
    987    q.Sub(aRgn2, aRgn1);
    988    return Or(p, q);
    989  }
    990  nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect) {
    991    return Xor(aRegion, nsRegion(aRect));
    992  }
    993  nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion) {
    994    return Xor(nsRegion(aRect), aRegion);
    995  }
    996  nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2) {
    997    return Xor(nsRegion(aRect1), nsRegion(aRect2));
    998  }
    999 
   1000  nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const;
   1001 
   1002  nsRegion& SubWith(const nsRegion& aOther) {
   1003 #ifdef DEBUG_REGIONS
   1004    class OperationStringGeneratorSubWith : public OperationStringGenerator {
   1005     public:
   1006      OperationStringGeneratorSubWith(nsRegion& aRegion,
   1007                                      const nsRegion& aOtherRegion)
   1008          : mRegion(&aRegion),
   1009            mRegionCopy(aRegion),
   1010            mOtherRegion(aOtherRegion) {
   1011        aRegion.mCurrentOpGenerator = this;
   1012      }
   1013      virtual ~OperationStringGeneratorSubWith() {
   1014        mRegion->mCurrentOpGenerator = nullptr;
   1015      }
   1016 
   1017      virtual void OutputOp() override {
   1018        std::stringstream stream;
   1019        mRegionCopy.OutputToStream("r1", stream);
   1020        mOtherRegion.OutputToStream("r2", stream);
   1021        stream << "r1.SubWith(r2);\n";
   1022        gfxCriticalError() << stream.str();
   1023      }
   1024 
   1025     private:
   1026      nsRegion* mRegion;
   1027      nsRegion mRegionCopy;
   1028      nsRegion mOtherRegion;
   1029    };
   1030 
   1031    OperationStringGeneratorSubWith opGenerator(*this, aOther);
   1032 #endif
   1033 
   1034    if (mBounds.IsEmpty()) {
   1035      return *this;
   1036    }
   1037 
   1038    if (aOther.mBands.IsEmpty()) {
   1039      return SubWith(aOther.mBounds);
   1040    }
   1041 
   1042    if (mBands.IsEmpty()) {
   1043      mBands.AppendElement(Band(mBounds));
   1044    }
   1045 
   1046    size_t idx = 0;
   1047    size_t idxOther = 0;
   1048    while (idx < mBands.Length()) {
   1049      while (true) {
   1050        while (idx != mBands.Length() &&
   1051               mBands[idx].bottom <= aOther.mBands[idxOther].top) {
   1052          // Increment our current band until it ends beyond aOther's current
   1053          // band.
   1054          idx++;
   1055        }
   1056 
   1057        if (idx == mBands.Length()) {
   1058          // This region is out of bands, the other region's future bands are
   1059          // ignored.
   1060          break;
   1061        }
   1062 
   1063        while (idxOther != aOther.mBands.Length() &&
   1064               aOther.mBands[idxOther].bottom <= mBands[idx].top) {
   1065          // Increment aOther's current band until it ends beyond our current
   1066          // band.
   1067          idxOther++;
   1068        }
   1069 
   1070        if (idxOther == aOther.mBands.Length()) {
   1071          // The other region's bands are all processed, all our future bands
   1072          // are ignored.
   1073          break;
   1074        }
   1075 
   1076        if (aOther.mBands[idxOther].top < mBands[idx].bottom) {
   1077          // We know the other band's bottom lies beyond our band's top because
   1078          // otherwise we would've incremented above. Intersecting bands found.
   1079          break;
   1080        }
   1081      }
   1082 
   1083      if (idx == mBands.Length() || idxOther == aOther.mBands.Length()) {
   1084        // The above loop executed a break because we're done.
   1085        break;
   1086      }
   1087 
   1088      const Band& bandOther = aOther.mBands[idxOther];
   1089 
   1090      if (!mBands[idx].Intersects(bandOther)) {
   1091        if (mBands[idx].bottom < bandOther.bottom) {
   1092          idx++;
   1093        } else {
   1094          idxOther++;
   1095          if (idxOther == aOther.mBands.Length()) {
   1096            break;
   1097          }
   1098        }
   1099        continue;
   1100      }
   1101 
   1102      // These bands actually intersect.
   1103      if (mBands[idx].top < bandOther.top) {
   1104        mBands.InsertElementAt(idx + 1, Band(mBands[idx]));
   1105        mBands[idx].bottom = bandOther.top;
   1106        idx++;
   1107        mBands[idx].top = bandOther.top;
   1108      }
   1109 
   1110      // mBands[idx].top >= bandOther.top;
   1111      if (mBands[idx].bottom <= bandOther.bottom) {
   1112        mBands[idx].SubStrips(bandOther);
   1113        if (mBands[idx].mStrips.IsEmpty()) {
   1114          mBands.RemoveElementAt(idx);
   1115        } else {
   1116          CompressBefore(idx);
   1117          idx++;
   1118          // The band before us just changed, it may be identical now.
   1119          CompressBefore(idx);
   1120        }
   1121        continue;
   1122      }
   1123 
   1124      // mBands[idx].bottom > bandOther.bottom
   1125      Band newBand = mBands[idx];
   1126      newBand.SubStrips(bandOther);
   1127 
   1128      if (!newBand.mStrips.IsEmpty()) {
   1129        mBands.InsertElementAt(idx, newBand);
   1130        mBands[idx].bottom = bandOther.bottom;
   1131        CompressBefore(idx);
   1132        idx++;
   1133      }
   1134 
   1135      mBands[idx].top = bandOther.bottom;
   1136 
   1137      idxOther++;
   1138      if (idxOther == aOther.mBands.Length()) {
   1139        break;
   1140      }
   1141    }
   1142 
   1143    if (mBands.IsEmpty()) {
   1144      mBounds.SetEmpty();
   1145    } else {
   1146      mBounds = CalculateBounds();
   1147    }
   1148 
   1149    AssertState();
   1150    EnsureSimplified();
   1151    return *this;
   1152  }
   1153  nsRegion& SubOut(const nsRegion& aOther) { return SubWith(aOther); }
   1154  nsRegion& SubOut(const nsRect& aOther) { return SubWith(aOther); }
   1155 
   1156 private:
   1157  void AppendOrExtend(const Band& aNewBand) {
   1158    if (aNewBand.mStrips.IsEmpty()) {
   1159      return;
   1160    }
   1161    if (mBands.IsEmpty()) {
   1162      mBands.AppendElement(aNewBand);
   1163      return;
   1164    }
   1165 
   1166    if (mBands.LastElement().bottom == aNewBand.top &&
   1167        mBands.LastElement().EqualStrips(aNewBand)) {
   1168      mBands.LastElement().bottom = aNewBand.bottom;
   1169    } else {
   1170      mBands.AppendElement(aNewBand);
   1171    }
   1172  }
   1173  void AppendOrExtend(const Band&& aNewBand) {
   1174    if (aNewBand.mStrips.IsEmpty()) {
   1175      return;
   1176    }
   1177    if (mBands.IsEmpty()) {
   1178      mBands.AppendElement(std::move(aNewBand));
   1179      return;
   1180    }
   1181 
   1182    if (mBands.LastElement().bottom == aNewBand.top &&
   1183        mBands.LastElement().EqualStrips(aNewBand)) {
   1184      mBands.LastElement().bottom = aNewBand.bottom;
   1185    } else {
   1186      mBands.AppendElement(std::move(aNewBand));
   1187    }
   1188  }
   1189 
   1190 public:
   1191  nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) {
   1192    if (&aRgn1 == this) {
   1193      return SubWith(aRgn2);
   1194    }
   1195 #ifdef DEBUG_REGIONS
   1196    class OperationStringGeneratorSub : public OperationStringGenerator {
   1197     public:
   1198      OperationStringGeneratorSub(nsRegion& aRegion, const nsRegion& aRgn1,
   1199                                  const nsRegion& aRgn2)
   1200          : mRegion(&aRegion), mRegion1(aRgn1), mRegion2(aRgn2) {
   1201        aRegion.mCurrentOpGenerator = this;
   1202      }
   1203      virtual ~OperationStringGeneratorSub() {
   1204        mRegion->mCurrentOpGenerator = nullptr;
   1205      }
   1206 
   1207      virtual void OutputOp() override {
   1208        std::stringstream stream;
   1209        mRegion1.OutputToStream("r1", stream);
   1210        mRegion2.OutputToStream("r2", stream);
   1211        stream << "nsRegion r3;\nr3.Sub(r1, r2);\n";
   1212        gfxCriticalError() << stream.str();
   1213      }
   1214 
   1215     private:
   1216      nsRegion* mRegion;
   1217      nsRegion mRegion1;
   1218      nsRegion mRegion2;
   1219    };
   1220 
   1221    OperationStringGeneratorSub opGenerator(*this, aRgn1, aRgn2);
   1222 #endif
   1223 
   1224    mBands.Clear();
   1225 
   1226    if (aRgn1.mBounds.IsEmpty()) {
   1227      mBounds.SetEmpty();
   1228      return *this;
   1229    }
   1230 
   1231    if (aRgn2.mBounds.IsEmpty()) {
   1232      Copy(aRgn1);
   1233      return *this;
   1234    }
   1235 
   1236    if (aRgn1.mBands.IsEmpty() && aRgn2.mBands.IsEmpty()) {
   1237      return Sub(aRgn1.mBounds, aRgn2.mBounds);
   1238    } else if (aRgn1.mBands.IsEmpty()) {
   1239      return Sub(aRgn1.mBounds, aRgn2);
   1240    } else if (aRgn2.mBands.IsEmpty()) {
   1241      return Sub(aRgn1, aRgn2.mBounds);
   1242    }
   1243 
   1244    const BandArray& bands1 = aRgn1.mBands;
   1245    const BandArray& bands2 = aRgn2.mBands;
   1246 
   1247    size_t idx = 0;
   1248    size_t idxOther = 0;
   1249 
   1250    // We iterate the source region's bands, subtracting the other regions bands
   1251    // from them as we move them into ours.
   1252    while (idx < bands1.Length()) {
   1253      while (idxOther < bands2.Length() &&
   1254             bands2[idxOther].bottom <= bands1[idx].top) {
   1255        // These other bands are irrelevant as they don't intersect with the
   1256        // band we're currently processing.
   1257        idxOther++;
   1258      }
   1259      if (idxOther == bands2.Length()) {
   1260        break;
   1261      }
   1262 
   1263      const Band& other = bands2[idxOther];
   1264 
   1265      // bands2[idxOther].bottom >= bands1[idx].top
   1266      Band origBand(bands1[idx]);
   1267      if (other.top >= origBand.bottom) {
   1268        // No intersecting bands, append and continue.
   1269        AppendOrExtend(origBand);
   1270        idx++;
   1271        continue;
   1272      }
   1273 
   1274      // Push a band for an uncovered region above our band.
   1275      if (origBand.top < other.top) {
   1276        Band newBand(origBand);
   1277        newBand.bottom = other.top;
   1278        AppendOrExtend(std::move(newBand));
   1279      }
   1280 
   1281      int32_t lastBottom = std::max(other.top, origBand.top);
   1282      while (idxOther < bands2.Length() &&
   1283             bands2[idxOther].top < origBand.bottom) {
   1284        const Band& other = bands2[idxOther];
   1285        Band newBand(origBand);
   1286        newBand.top = std::max(origBand.top, other.top);
   1287        newBand.bottom = std::min(origBand.bottom, other.bottom);
   1288 
   1289        // If there was a gap, we need to add the original band there.
   1290        if (newBand.top > lastBottom) {
   1291          Band betweenBand(newBand);
   1292          betweenBand.top = lastBottom;
   1293          betweenBand.bottom = newBand.top;
   1294          AppendOrExtend(std::move(betweenBand));
   1295        }
   1296 
   1297        lastBottom = newBand.bottom;
   1298        newBand.SubStrips(other);
   1299        AppendOrExtend(std::move(newBand));
   1300        idxOther++;
   1301      }
   1302      // Decrement other here so we are back at the last band in region 2
   1303      // that intersected.
   1304      idxOther--;
   1305 
   1306      if (bands2[idxOther].bottom < origBand.bottom) {
   1307        // The last band in region2 that intersected ended before this one,
   1308        // we can copy the rest.
   1309        Band newBand(origBand);
   1310        newBand.top = bands2[idxOther].bottom;
   1311        AppendOrExtend(std::move(newBand));
   1312        idxOther++;
   1313      }
   1314      idx++;
   1315    }
   1316 
   1317    // Copy any remaining bands, the first one may have to be extended to fit
   1318    // the last one added before. The rest can be unconditionally appended.
   1319    if (idx < bands1.Length()) {
   1320      AppendOrExtend(bands1[idx]);
   1321      idx++;
   1322    }
   1323 
   1324    while (idx < bands1.Length()) {
   1325      mBands.AppendElement(bands1[idx]);
   1326      idx++;
   1327    }
   1328 
   1329    if (mBands.IsEmpty()) {
   1330      mBounds.SetEmpty();
   1331    } else {
   1332      mBounds = CalculateBounds();
   1333    }
   1334 
   1335    AssertState();
   1336    EnsureSimplified();
   1337    return *this;
   1338  }
   1339 
   1340 private:
   1341  // Internal helper for executing subtraction.
   1342  void RunSubtraction(const nsRectAbsolute& aRect) {
   1343    Strip rectStrip(aRect.X(), aRect.XMost());
   1344 
   1345    size_t idx = 0;
   1346 
   1347    while (idx < mBands.Length()) {
   1348      if (mBands[idx].top >= aRect.YMost()) {
   1349        return;
   1350      }
   1351 
   1352      if (mBands[idx].bottom <= aRect.Y()) {
   1353        // This band is entirely before aRect, move on.
   1354        idx++;
   1355        continue;
   1356      }
   1357 
   1358      if (!mBands[idx].Intersects(Strip(aRect.X(), aRect.XMost()))) {
   1359        // This band does not intersect aRect horizontally. Move on.
   1360        idx++;
   1361        continue;
   1362      }
   1363 
   1364      // This band intersects with aRect.
   1365 
   1366      if (mBands[idx].top < aRect.Y()) {
   1367        // This band starts above the start of aRect, split the band into two
   1368        // along the intersection, and continue to the next iteration to process
   1369        // the one that now intersects exactly.
   1370        auto above = mBands.InsertElementAt(idx, Band(mBands[idx]));
   1371        above->bottom = aRect.Y();
   1372        idx++;
   1373        mBands[idx].top = aRect.Y();
   1374        // Continue to run the loop for the next band.
   1375        continue;
   1376      }
   1377 
   1378      if (mBands[idx].bottom <= aRect.YMost()) {
   1379        // This band ends before the end of aRect.
   1380        mBands[idx].SubStrip(rectStrip);
   1381        if (mBands[idx].mStrips.Length()) {
   1382          CompressAdjacentBands(idx);
   1383        } else {
   1384          mBands.RemoveElementAt(idx);
   1385        }
   1386        continue;
   1387      }
   1388 
   1389      // This band extends beyond aRect.
   1390      Band newBand = mBands[idx];
   1391      newBand.SubStrip(rectStrip);
   1392      newBand.bottom = aRect.YMost();
   1393      mBands[idx].top = aRect.YMost();
   1394 
   1395      if (newBand.mStrips.Length()) {
   1396        if (idx && mBands[idx - 1].bottom == newBand.top &&
   1397            newBand.EqualStrips(mBands[idx - 1])) {
   1398          mBands[idx - 1].bottom = aRect.YMost();
   1399        } else {
   1400          mBands.InsertElementAt(idx, std::move(newBand));
   1401        }
   1402      }
   1403 
   1404      return;
   1405    }
   1406  }
   1407 
   1408 public:
   1409  nsRegion& SubWith(const nsRectAbsolute& aRect) {
   1410    if (!mBounds.Intersects(aRect)) {
   1411      return *this;
   1412    }
   1413 
   1414    if (aRect.Contains(mBounds)) {
   1415      SetEmpty();
   1416      return *this;
   1417    }
   1418 
   1419 #ifdef DEBUG_REGIONS
   1420    class OperationStringGeneratorSubWith : public OperationStringGenerator {
   1421     public:
   1422      OperationStringGeneratorSubWith(nsRegion& aRegion,
   1423                                      const nsRectAbsolute& aRect)
   1424          : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) {
   1425        aRegion.mCurrentOpGenerator = this;
   1426      }
   1427      virtual ~OperationStringGeneratorSubWith() {
   1428        mRegion->mCurrentOpGenerator = nullptr;
   1429      }
   1430 
   1431      virtual void OutputOp() override {
   1432        std::stringstream stream;
   1433        mRegionCopy.OutputToStream("r", stream);
   1434        stream << "r.SubWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", "
   1435               << mRect.Width() << ", " << mRect.Height() << "));\n";
   1436        gfxCriticalError() << stream.str();
   1437      }
   1438 
   1439     private:
   1440      nsRegion* mRegion;
   1441      nsRegion mRegionCopy;
   1442      nsRectAbsolute mRect;
   1443    };
   1444 
   1445    OperationStringGeneratorSubWith opGenerator(*this, aRect);
   1446 #endif
   1447 
   1448    if (mBands.IsEmpty()) {
   1449      mBands.AppendElement(Band(mBounds));
   1450    }
   1451 
   1452    RunSubtraction(aRect);
   1453 
   1454    if (aRect.X() <= mBounds.X() || aRect.Y() <= mBounds.Y() ||
   1455        aRect.XMost() >= mBounds.XMost() || aRect.YMost() >= mBounds.YMost()) {
   1456      mBounds = CalculateBounds();
   1457    }
   1458    EnsureSimplified();
   1459    AssertState();
   1460    return *this;
   1461  }
   1462  nsRegion& Sub(const nsRegion& aRegion, const nsRectAbsolute& aRect) {
   1463    if (aRect.Contains(aRegion.mBounds)) {
   1464      SetEmpty();
   1465      return *this;
   1466    }
   1467    if (&aRegion == this) {
   1468      return SubWith(aRect);
   1469    }
   1470 #ifdef DEBUG_REGIONS
   1471    class OperationStringGeneratorSub : public OperationStringGenerator {
   1472     public:
   1473      OperationStringGeneratorSub(nsRegion& aRegion,
   1474                                  const nsRegion& aRegionOther,
   1475                                  const nsRectAbsolute& aRect)
   1476          : mRegion(&aRegion), mRegionOther(aRegionOther), mRect(aRect) {
   1477        aRegion.mCurrentOpGenerator = this;
   1478      }
   1479      virtual ~OperationStringGeneratorSub() {
   1480        mRegion->mCurrentOpGenerator = nullptr;
   1481      }
   1482 
   1483      virtual void OutputOp() override {
   1484        std::stringstream stream;
   1485        mRegionOther.OutputToStream("r1", stream);
   1486        stream << "nsRegion r2;\nr2.Sub(r1, nsRect(" << mRect.X() << ", "
   1487               << mRect.Y() << ", " << mRect.Width() << ", " << mRect.Height()
   1488               << "));\n";
   1489        gfxCriticalError() << stream.str();
   1490      }
   1491 
   1492     private:
   1493      nsRegion* mRegion;
   1494      nsRegion mRegionOther;
   1495      nsRectAbsolute mRect;
   1496    };
   1497 
   1498    OperationStringGeneratorSub opGenerator(*this, aRegion, aRect);
   1499 #endif
   1500 
   1501    mBands.Clear();
   1502 
   1503    if (aRegion.mBounds.IsEmpty()) {
   1504      mBounds.SetEmpty();
   1505      return *this;
   1506    }
   1507 
   1508    if (aRect.IsEmpty()) {
   1509      Copy(aRegion);
   1510      return *this;
   1511    }
   1512 
   1513    if (aRegion.mBands.IsEmpty()) {
   1514      Copy(aRegion.mBounds);
   1515      return SubWith(aRect);
   1516    }
   1517 
   1518    const BandArray& bands = aRegion.mBands;
   1519 
   1520    size_t idx = 0;
   1521 
   1522    Strip strip(aRect.X(), aRect.XMost());
   1523 
   1524    mBands.SetCapacity(bands.Length() + 3);
   1525 
   1526    // Process all bands that lie before aRect.
   1527    while (idx < bands.Length() && bands[idx].bottom <= aRect.Y()) {
   1528      mBands.AppendElement(bands[idx]);
   1529      idx++;
   1530    }
   1531 
   1532    // This band's bottom lies beyond aRect.
   1533    if (idx < bands.Length() && bands[idx].top < aRect.Y()) {
   1534      Band newBand(bands[idx]);
   1535      if (bands[idx].Intersects(strip)) {
   1536        newBand.bottom = aRect.Y();
   1537      } else {
   1538        idx++;
   1539      }
   1540      mBands.AppendElement(std::move(newBand));
   1541    }
   1542 
   1543    // This tracks whether the band when we -exit- the next loop intersected the
   1544    // rectangle.
   1545    bool didIntersect = false;
   1546 
   1547    while (idx < bands.Length() && bands[idx].top < aRect.YMost()) {
   1548      // Process all bands intersecting with aRect.
   1549      if (!bands[idx].Intersects(strip)) {
   1550        AppendOrExtend(bands[idx]);
   1551        idx++;
   1552        didIntersect = false;
   1553        continue;
   1554      }
   1555 
   1556      didIntersect = true;
   1557      Band newBand(bands[idx]);
   1558      newBand.top = std::max(newBand.top, aRect.Y());
   1559      newBand.bottom = std::min(newBand.bottom, aRect.YMost());
   1560      newBand.SubStrip(strip);
   1561      AppendOrExtend(std::move(newBand));
   1562      idx++;
   1563    }
   1564 
   1565    if (didIntersect) {
   1566      if (aRect.YMost() < bands[idx - 1].bottom) {
   1567        // If this band does not intersect the loop above has already added the
   1568        // whole unmodified band.
   1569        Band newBand(bands[idx - 1]);
   1570        newBand.top = aRect.YMost();
   1571        AppendOrExtend(std::move(newBand));
   1572      }
   1573    }
   1574 
   1575    // Now process all bands beyond aRect.
   1576    if (idx < bands.Length()) {
   1577      AppendOrExtend(bands[idx]);
   1578      idx++;
   1579    }
   1580 
   1581    mBands.AppendElements(bands.Elements() + idx, bands.Length() - idx);
   1582 
   1583    if (mBands.IsEmpty()) {
   1584      mBounds.SetEmpty();
   1585    } else {
   1586      mBounds = CalculateBounds();
   1587    }
   1588 
   1589    AssertState();
   1590    EnsureSimplified();
   1591    return *this;
   1592  }
   1593  nsRegion& SubWith(const nsRect& aRect) {
   1594    return SubWith(nsRectAbsolute::FromRect(aRect));
   1595  }
   1596  nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) {
   1597    Copy(aRect);
   1598    return SubWith(aRegion);
   1599  }
   1600  nsRegion& Sub(const nsRectAbsolute& aRect, const nsRegion& aRegion) {
   1601    Copy(aRect);
   1602    return SubWith(aRegion);
   1603  }
   1604  nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) {
   1605    Copy(aRect1);
   1606    return SubWith(aRect2);
   1607  }
   1608  nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) {
   1609    return Sub(aRegion, nsRectAbsolute::FromRect(aRect));
   1610  }
   1611  nsRegion& Sub(const nsRectAbsolute& aRect1, const nsRectAbsolute& aRect2) {
   1612    Copy(aRect1);
   1613    return SubWith(aRect2);
   1614  }
   1615 
   1616  /**
   1617   * Returns true if the given point is inside the region. A region
   1618   * created from a rect (x=0, y=0, w=100, h=100) will NOT contain
   1619   * the point x=100, y=100.
   1620   */
   1621  bool Contains(int aX, int aY) const {
   1622    if (mBands.IsEmpty()) {
   1623      return mBounds.Contains(aX, aY);
   1624    }
   1625 
   1626    auto iter = mBands.begin();
   1627 
   1628    while (iter != mBands.end()) {
   1629      if (iter->bottom <= aY) {
   1630        iter++;
   1631        continue;
   1632      }
   1633 
   1634      if (iter->top > aY) {
   1635        return false;
   1636      }
   1637 
   1638      if (iter->ContainsStrip(Strip(aX, aX + 1))) {
   1639        return true;
   1640      }
   1641      return false;
   1642    }
   1643    return false;
   1644  }
   1645 
   1646  bool Contains(const nsPoint& aPoint) const {
   1647    return Contains(aPoint.x, aPoint.y);
   1648  }
   1649 
   1650  bool Contains(const nsRectAbsolute& aRect) const {
   1651    if (aRect.IsEmpty()) {
   1652      return false;
   1653    }
   1654 
   1655    if (mBands.IsEmpty()) {
   1656      return mBounds.Contains(aRect);
   1657    }
   1658 
   1659    auto iter = mBands.begin();
   1660 
   1661    while (iter != mBands.end()) {
   1662      if (iter->bottom <= aRect.Y()) {
   1663        iter++;
   1664        continue;
   1665      }
   1666 
   1667      if (iter->top > aRect.Y()) {
   1668        return false;
   1669      }
   1670 
   1671      // Now inside the rectangle.
   1672      if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) {
   1673        return false;
   1674      }
   1675 
   1676      if (iter->bottom >= aRect.YMost()) {
   1677        return true;
   1678      }
   1679 
   1680      int32_t lastY = iter->bottom;
   1681      iter++;
   1682      while (iter != mBands.end()) {
   1683        // Bands do not connect.
   1684        if (iter->top != lastY) {
   1685          return false;
   1686        }
   1687 
   1688        if (!iter->ContainsStrip(Strip(aRect.X(), aRect.XMost()))) {
   1689          return false;
   1690        }
   1691 
   1692        if (iter->bottom >= aRect.YMost()) {
   1693          return true;
   1694        }
   1695 
   1696        lastY = iter->bottom;
   1697        iter++;
   1698      }
   1699    }
   1700    return false;
   1701  }
   1702  bool Contains(const nsRect& aRect) const {
   1703    return Contains(nsRectAbsolute::FromRect(aRect));
   1704  }
   1705 
   1706  bool Contains(const nsRegion& aRgn) const;
   1707  bool Intersects(const nsRectAbsolute& aRect) const;
   1708  bool Intersects(const nsRect& aRect) const {
   1709    return Intersects(nsRectAbsolute::FromRect(aRect));
   1710  }
   1711 
   1712  void MoveBy(int32_t aXOffset, int32_t aYOffset) {
   1713    MoveBy(nsPoint(aXOffset, aYOffset));
   1714  }
   1715  void MoveBy(nsPoint aPt) {
   1716 #ifdef DEBUG_REGIONS
   1717    class OperationStringGeneratorMoveBy : public OperationStringGenerator {
   1718     public:
   1719      OperationStringGeneratorMoveBy(nsRegion& aRegion, const nsPoint& aPoint)
   1720          : mRegion(&aRegion), mRegionCopy(aRegion), mPoint(aPoint) {
   1721        aRegion.mCurrentOpGenerator = this;
   1722      }
   1723      virtual ~OperationStringGeneratorMoveBy() {
   1724        mRegion->mCurrentOpGenerator = nullptr;
   1725      }
   1726 
   1727      virtual void OutputOp() override {
   1728        std::stringstream stream;
   1729        mRegionCopy.OutputToStream("r", stream);
   1730        stream << "r.MoveBy(nsPoint(" << mPoint.x << ", " << mPoint.y
   1731               << "));\n";
   1732        gfxCriticalError() << stream.str();
   1733      }
   1734 
   1735     private:
   1736      nsRegion* mRegion;
   1737      nsRegion mRegionCopy;
   1738      nsPoint mPoint;
   1739    };
   1740 
   1741    OperationStringGeneratorMoveBy opGenerator(*this, aPt);
   1742 #endif
   1743 
   1744    mBounds.MoveBy(aPt);
   1745    for (Band& band : mBands) {
   1746      band.top += aPt.Y();
   1747      band.bottom += aPt.Y();
   1748      for (Strip& strip : band.mStrips) {
   1749        strip.left += aPt.X();
   1750        strip.right += aPt.X();
   1751      }
   1752    }
   1753    AssertState();
   1754  }
   1755  void SetEmpty() {
   1756    mBands.Clear();
   1757    mBounds.SetEmpty();
   1758  }
   1759 
   1760  nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const {
   1761    return MovedBy(nsPoint(aXOffset, aYOffset));
   1762  }
   1763  nsRegion MovedBy(const nsPoint& aPt) const {
   1764    nsRegion copy(*this);
   1765    copy.MoveBy(aPt);
   1766    return copy;
   1767  }
   1768 
   1769  nsRegion Intersect(const nsRegion& aOther) const {
   1770    nsRegion intersection;
   1771    intersection.And(*this, aOther);
   1772    return intersection;
   1773  }
   1774 
   1775  void Inflate(const nsMargin& aMargin);
   1776 
   1777  nsRegion Inflated(const nsMargin& aMargin) const {
   1778    nsRegion copy(*this);
   1779    copy.Inflate(aMargin);
   1780    return copy;
   1781  }
   1782 
   1783  bool IsEmpty() const { return mBounds.IsEmpty(); }
   1784  bool IsComplex() const { return GetNumRects() > 1; }
   1785  bool IsEqual(const nsRegion& aRegion) const {
   1786    if (!mBounds.IsEqualInterior(aRegion.mBounds)) {
   1787      return false;
   1788    }
   1789 
   1790    if (mBands.Length() != aRegion.mBands.Length()) {
   1791      return false;
   1792    }
   1793 
   1794    for (auto iter1 = mBands.begin(), iter2 = aRegion.mBands.begin();
   1795         iter1 != mBands.end(); iter1++, iter2++) {
   1796      if (iter1->top != iter2->top || iter1->bottom != iter2->bottom ||
   1797          !iter1->EqualStrips(*iter2)) {
   1798        return false;
   1799      }
   1800    }
   1801 
   1802    return true;
   1803  }
   1804 
   1805  uint32_t GetNumRects() const {
   1806    if (mBands.IsEmpty()) {
   1807      return mBounds.IsEmpty() ? 0 : 1;
   1808    }
   1809 
   1810    uint32_t rects = 0;
   1811 
   1812    for (const Band& band : mBands) {
   1813      rects += band.mStrips.Length();
   1814    }
   1815 
   1816    return rects;
   1817  }
   1818  const nsRect GetBounds() const { return mBounds.ToNSRect(); }
   1819  const nsRectAbsolute GetAbsoluteBounds() const { return mBounds; }
   1820  uint64_t Area() const;
   1821 
   1822  /**
   1823   * Return this region scaled to a different appunits per pixel (APP) ratio.
   1824   * This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the
   1825   * region.
   1826   * @param aFromAPP the APP to scale from
   1827   * @param aToAPP the APP to scale to
   1828   * @note this can turn an empty region into a non-empty region
   1829   */
   1830  [[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP,
   1831                                                      int32_t aToAPP) const;
   1832  [[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
   1833                                                     int32_t aToAPP) const;
   1834  nsRegion& ScaleRoundOut(float aXScale, float aYScale);
   1835  nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale);
   1836  nsRegion& Transform(const mozilla::gfx::Matrix4x4& aTransform);
   1837  nsIntRegion ScaleToOutsidePixels(float aXScale, float aYScale,
   1838                                   nscoord aAppUnitsPerPixel) const;
   1839  nsIntRegion ScaleToInsidePixels(float aXScale, float aYScale,
   1840                                  nscoord aAppUnitsPerPixel) const;
   1841  nsIntRegion ScaleToNearestPixels(float aXScale, float aYScale,
   1842                                   nscoord aAppUnitsPerPixel) const;
   1843  nsIntRegion ToOutsidePixels(nscoord aAppUnitsPerPixel) const;
   1844  nsIntRegion ToNearestPixels(nscoord aAppUnitsPerPixel) const;
   1845 
   1846  /**
   1847   * Gets the largest rectangle contained in the region.
   1848   * @param aContainingRect if non-empty, we choose a rectangle that
   1849   * maximizes the area intersecting with aContainingRect (and break ties by
   1850   * then choosing the largest rectangle overall)
   1851   */
   1852  nsRect GetLargestRectangle(const nsRect& aContainingRect = nsRect()) const;
   1853 
   1854  /**
   1855   * Make sure the region has at most aMaxRects by adding area to it
   1856   * if necessary. The simplified region will be a superset of the
   1857   * original region. The simplified region's bounding box will be
   1858   * the same as for the current region.
   1859   */
   1860  void SimplifyOutward(uint32_t aMaxRects);
   1861  /**
   1862   * Simplify the region by adding at most aThreshold area between spans of
   1863   * rects.  The simplified region will be a superset of the original region.
   1864   * The simplified region's bounding box will be the same as for the current
   1865   * region.
   1866   */
   1867  void SimplifyOutwardByArea(uint32_t aThreshold);
   1868  /**
   1869   * Make sure the region has at most aMaxRects by removing area from
   1870   * it if necessary. The simplified region will be a subset of the
   1871   * original region.
   1872   */
   1873  void SimplifyInward(uint32_t aMaxRects);
   1874 
   1875  /**
   1876   * VisitEdges is a weird kind of function that we use for padding
   1877   * out surfaces to prevent texture filtering artifacts.
   1878   * It calls the visitFn callback for each of the exterior edges of
   1879   * the regions. The top and bottom edges will be expanded 1 pixel
   1880   * to the left and right if there's an outside corner. The order
   1881   * the edges are visited is not guaranteed.
   1882   *
   1883   * visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT
   1884   * and specifies which kind of edge is being visited. x1, y1, x2, y2
   1885   * are the coordinates of the line. (x1 == x2) || (y1 == y2)
   1886   */
   1887  typedef void (*visitFn)(void* closure, VisitSide side, int x1, int y1, int x2,
   1888                          int y2);
   1889  void VisitEdges(visitFn, void* closure) const;
   1890 
   1891  nsCString ToString() const;
   1892 
   1893  static inline pixman_box32_t RectToBox(const nsRect& aRect) {
   1894    pixman_box32_t box = {aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()};
   1895    return box;
   1896  }
   1897 
   1898  static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect& aRect) {
   1899    pixman_box32_t box = {aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()};
   1900    return box;
   1901  }
   1902 
   1903 private:
   1904  nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const;
   1905 
   1906  nsRegion& Copy(const nsRegion& aRegion) {
   1907    mBounds = aRegion.mBounds;
   1908    mBands = aRegion.mBands.Clone();
   1909    return *this;
   1910  }
   1911 
   1912  nsRegion& Copy(const nsRect& aRect) {
   1913    mBands.Clear();
   1914    mBounds = nsRectAbsolute::FromRect(aRect);
   1915    return *this;
   1916  }
   1917 
   1918  nsRegion& Copy(const nsRectAbsolute& aRect) {
   1919    mBands.Clear();
   1920    mBounds = aRect;
   1921    return *this;
   1922  }
   1923 
   1924  void EnsureSimplified() {
   1925    if (mBands.Length() == 1 && mBands.begin()->mStrips.Length() == 1) {
   1926      mBands.Clear();
   1927    }
   1928  }
   1929 
   1930  static inline nsRectAbsolute BoxToRect(const pixman_box32_t& aBox) {
   1931    return nsRectAbsolute(aBox.x1, aBox.y1, aBox.x2, aBox.y2);
   1932  }
   1933 
   1934  void AddRect(const nsRectAbsolute& aRect) {
   1935 #ifdef DEBUG_REGIONS
   1936    class OperationStringGeneratorAddRect : public OperationStringGenerator {
   1937     public:
   1938      OperationStringGeneratorAddRect(nsRegion& aRegion,
   1939                                      const nsRectAbsolute& aRect)
   1940          : mRegion(&aRegion), mRegionCopy(aRegion), mRect(aRect) {
   1941        aRegion.mCurrentOpGenerator = this;
   1942      }
   1943      virtual ~OperationStringGeneratorAddRect() {
   1944        mRegion->mCurrentOpGenerator = nullptr;
   1945      }
   1946 
   1947      virtual void OutputOp() override {
   1948        std::stringstream stream;
   1949        mRegionCopy.OutputToStream("r", stream);
   1950        stream << "r.OrWith(nsRect(" << mRect.X() << ", " << mRect.Y() << ", "
   1951               << mRect.Width() << ", " << mRect.Height() << "));\n";
   1952        gfxCriticalError() << stream.str();
   1953      }
   1954 
   1955     private:
   1956      nsRegion* mRegion;
   1957      nsRegion mRegionCopy;
   1958      nsRectAbsolute mRect;
   1959    };
   1960 
   1961    OperationStringGeneratorAddRect opGenerator(*this, aRect);
   1962 #endif
   1963    if (aRect.IsEmpty()) {
   1964      return;
   1965    }
   1966 
   1967    if (mBands.IsEmpty()) {
   1968      if (mBounds.IsEmpty()) {
   1969        mBounds = aRect;
   1970        return;
   1971      } else if (mBounds.Contains(aRect)) {
   1972        return;
   1973      }
   1974 
   1975      mBands.AppendElement(Band(mBounds));
   1976    }
   1977 
   1978    mBounds = aRect.UnsafeUnion(mBounds);
   1979 
   1980    size_t idx = 0;
   1981 
   1982    Strip strip(aRect.X(), aRect.XMost());
   1983    Band remaining(aRect);
   1984 
   1985    while (idx != mBands.Length()) {
   1986      if (mBands[idx].bottom <= remaining.top) {
   1987        // This band lies wholly above aRect.
   1988        idx++;
   1989        continue;
   1990      }
   1991 
   1992      if (remaining.top >= remaining.bottom) {
   1993        AssertState();
   1994        EnsureSimplified();
   1995        return;
   1996      }
   1997 
   1998      if (mBands[idx].top >= remaining.bottom) {
   1999        // This band lies wholly below aRect.
   2000        break;
   2001      }
   2002 
   2003      if (mBands[idx].EqualStrips(remaining)) {
   2004        mBands[idx].top = std::min(mBands[idx].top, remaining.top);
   2005        // Nothing to do for this band. Just expand.
   2006        remaining.top = mBands[idx].bottom;
   2007        CompressBefore(idx);
   2008        idx++;
   2009        continue;
   2010      }
   2011 
   2012      if (mBands[idx].top > remaining.top) {
   2013        auto before = mBands.InsertElementAt(idx, remaining);
   2014        before->bottom = mBands[idx + 1].top;
   2015        remaining.top = before->bottom;
   2016        CompressBefore(idx);
   2017        idx++;
   2018        CompressBefore(idx);
   2019        continue;
   2020      }
   2021 
   2022      if (mBands[idx].ContainsStrip(strip)) {
   2023        remaining.top = mBands[idx].bottom;
   2024        idx++;
   2025        continue;
   2026      }
   2027 
   2028      // mBands[idx].top <= remaining.top.
   2029 
   2030      if (mBands[idx].top < remaining.top) {
   2031        auto before = mBands.InsertElementAt(idx, Band(mBands[idx]));
   2032        before->bottom = remaining.top;
   2033        idx++;
   2034        mBands[idx].top = remaining.top;
   2035        continue;
   2036      }
   2037 
   2038      // mBands[idx].top == remaining.top
   2039      if (mBands[idx].bottom > remaining.bottom) {
   2040        auto below = mBands.InsertElementAt(idx + 1, Band(mBands[idx]));
   2041        below->top = remaining.bottom;
   2042        mBands[idx].bottom = remaining.bottom;
   2043      }
   2044 
   2045      mBands[idx].InsertStrip(strip);
   2046      CompressBefore(idx);
   2047      remaining.top = mBands[idx].bottom;
   2048      idx++;
   2049      CompressBefore(idx);
   2050    }
   2051 
   2052    if (remaining.top < remaining.bottom) {
   2053      // We didn't find any bands that overlapped aRect.
   2054      if (idx) {
   2055        if (mBands[idx - 1].bottom == remaining.top &&
   2056            mBands[idx - 1].EqualStrips(remaining)) {
   2057          mBands[idx - 1].bottom = remaining.bottom;
   2058          CompressBefore(idx);
   2059          AssertState();
   2060          EnsureSimplified();
   2061          return;
   2062        }
   2063      }
   2064      mBands.InsertElementAt(idx, remaining);
   2065      idx++;
   2066      CompressBefore(idx);
   2067    } else {
   2068      CompressBefore(idx);
   2069    }
   2070 
   2071    AssertState();
   2072    EnsureSimplified();
   2073  }
   2074 
   2075  // Most callers could probably do this on the fly, if this ever shows up
   2076  // in profiles we could optimize this.
   2077  nsRectAbsolute CalculateBounds() const {
   2078    if (mBands.IsEmpty()) {
   2079      return mBounds;
   2080    }
   2081 
   2082    int32_t top = mBands.begin()->top;
   2083    int32_t bottom = mBands.LastElement().bottom;
   2084 
   2085    int32_t leftMost = mBands.begin()->mStrips.begin()->left;
   2086    int32_t rightMost = mBands.begin()->mStrips.LastElement().right;
   2087    for (const Band& band : mBands) {
   2088      leftMost = std::min(leftMost, band.mStrips.begin()->left);
   2089      rightMost = std::max(rightMost, band.mStrips.LastElement().right);
   2090    }
   2091 
   2092    return nsRectAbsolute(leftMost, top, rightMost, bottom);
   2093  }
   2094 
   2095  static uint32_t ComputeMergedAreaIncrease(const Band& aTopBand,
   2096                                            const Band& aBottomBand);
   2097 
   2098  // Returns true if idx is now referring to the 'next' band
   2099  bool CompressAdjacentBands(size_t& aIdx) {
   2100    if ((aIdx + 1) < mBands.Length()) {
   2101      if (mBands[aIdx + 1].top == mBands[aIdx].bottom &&
   2102          mBands[aIdx + 1].EqualStrips(mBands[aIdx])) {
   2103        mBands[aIdx].bottom = mBands[aIdx + 1].bottom;
   2104        mBands.RemoveElementAt(aIdx + 1);
   2105      }
   2106    }
   2107    if (aIdx) {
   2108      if (mBands[aIdx - 1].bottom == mBands[aIdx].top &&
   2109          mBands[aIdx].EqualStrips(mBands[aIdx - 1])) {
   2110        mBands[aIdx - 1].bottom = mBands[aIdx].bottom;
   2111        mBands.RemoveElementAt(aIdx);
   2112        return true;
   2113      }
   2114    }
   2115    return false;
   2116  }
   2117 
   2118  void CompressBefore(size_t& aIdx) {
   2119    if (aIdx && aIdx < mBands.Length()) {
   2120      if (mBands[aIdx - 1].bottom == mBands[aIdx].top &&
   2121          mBands[aIdx - 1].EqualStrips(mBands[aIdx])) {
   2122        mBands[aIdx].top = mBands[aIdx - 1].top;
   2123        mBands.RemoveElementAt(aIdx - 1);
   2124        aIdx--;
   2125      }
   2126    }
   2127  }
   2128 
   2129  BandArray mBands;
   2130  // Considering we only ever OR with nsRects, the bounds should fit in an
   2131  // nsRect as well.
   2132  nsRectAbsolute mBounds;
   2133 #ifdef DEBUG_REGIONS
   2134  friend class OperationStringGenerator;
   2135  OperationStringGenerator* mCurrentOpGenerator;
   2136 #endif
   2137 
   2138 public:
   2139  class RectIterator {
   2140    const nsRegion& mRegion;
   2141    typename BandArray::const_iterator mCurrentBand;
   2142    typename StripArray::const_iterator mCurrentStrip;
   2143 
   2144   public:
   2145    explicit RectIterator(const nsRegion& aRegion)
   2146        : mRegion(aRegion),
   2147          mCurrentBand(aRegion.mBands.begin())
   2148 #ifndef DEBUG
   2149          ,
   2150          mCurrentStrip(nullptr)
   2151 #endif
   2152    {
   2153      mIsDone = mRegion.mBounds.IsEmpty();
   2154      if (mCurrentBand != aRegion.mBands.end()) {
   2155        mCurrentStrip = mCurrentBand->mStrips.begin();
   2156      }
   2157    }
   2158 
   2159    bool Done() const { return mIsDone; }
   2160 
   2161    const nsRect Get() const {
   2162      if (mRegion.mBands.IsEmpty()) {
   2163        return mRegion.GetBounds();
   2164      }
   2165      return nsRect(mCurrentStrip->left, mCurrentBand->top,
   2166                    mCurrentStrip->right - mCurrentStrip->left,
   2167                    mCurrentBand->bottom - mCurrentBand->top);
   2168    }
   2169 
   2170    const nsRectAbsolute GetAbsolute() const {
   2171      if (mRegion.mBands.IsEmpty()) {
   2172        return mRegion.mBounds;
   2173      }
   2174      return nsRectAbsolute(mCurrentStrip->left, mCurrentBand->top,
   2175                            mCurrentStrip->right, mCurrentBand->bottom);
   2176    }
   2177 
   2178    void Next() {
   2179      if (mRegion.mBands.IsEmpty()) {
   2180        mIsDone = true;
   2181        return;
   2182      }
   2183 
   2184      mCurrentStrip++;
   2185      if (mCurrentStrip == mCurrentBand->mStrips.end()) {
   2186        mCurrentBand++;
   2187        if (mCurrentBand != mRegion.mBands.end()) {
   2188          mCurrentStrip = mCurrentBand->mStrips.begin();
   2189        } else {
   2190          mIsDone = true;
   2191        }
   2192      }
   2193    }
   2194 
   2195    bool mIsDone;
   2196  };
   2197 
   2198  RectIterator RectIter() const { return RectIterator(*this); }
   2199 };
   2200 
   2201 namespace mozilla {
   2202 namespace gfx {
   2203 
   2204 /**
   2205 * BaseIntRegions use int32_t coordinates.
   2206 */
   2207 template <typename Derived, typename Rect, typename Point, typename Margin>
   2208 class BaseIntRegion {
   2209  friend class ::nsRegion;
   2210 
   2211  // Give access to all specializations of IntRegionTyped, not just ones that
   2212  // derive from this specialization of BaseIntRegion.
   2213  template <typename units>
   2214  friend class IntRegionTyped;
   2215 
   2216 public:
   2217  typedef Rect RectType;
   2218  typedef Point PointType;
   2219  typedef Margin MarginType;
   2220 
   2221  BaseIntRegion() = default;
   2222  MOZ_IMPLICIT BaseIntRegion(const Rect& aRect) : mImpl(ToRect(aRect)) {}
   2223  explicit BaseIntRegion(mozilla::gfx::ArrayView<pixman_box32_t> aRects)
   2224      : mImpl(aRects) {}
   2225  BaseIntRegion(const BaseIntRegion& aRegion) : mImpl(aRegion.mImpl) {}
   2226  BaseIntRegion(BaseIntRegion&& aRegion) : mImpl(std::move(aRegion.mImpl)) {}
   2227  Derived& operator=(const Rect& aRect) {
   2228    mImpl = ToRect(aRect);
   2229    return This();
   2230  }
   2231  Derived& operator=(const Derived& aRegion) {
   2232    mImpl = aRegion.mImpl;
   2233    return This();
   2234  }
   2235  Derived& operator=(Derived&& aRegion) {
   2236    mImpl = std::move(aRegion.mImpl);
   2237    return This();
   2238  }
   2239 
   2240  bool operator==(const Derived& aRgn) const { return IsEqual(aRgn); }
   2241  bool operator!=(const Derived& aRgn) const { return !(*this == aRgn); }
   2242 
   2243  friend std::ostream& operator<<(std::ostream& stream, const Derived& m) {
   2244    return stream << m.mImpl;
   2245  }
   2246 
   2247  void AndWith(const Derived& aOther) { And(This(), aOther); }
   2248  void AndWith(const Rect& aOther) { And(This(), aOther); }
   2249  Derived& And(const Derived& aRgn1, const Derived& aRgn2) {
   2250    mImpl.And(aRgn1.mImpl, aRgn2.mImpl);
   2251    return This();
   2252  }
   2253  Derived& And(const Derived& aRegion, const Rect& aRect) {
   2254    mImpl.And(aRegion.mImpl, ToRect(aRect));
   2255    return This();
   2256  }
   2257  Derived& And(const Rect& aRect, const Derived& aRegion) {
   2258    return And(aRegion, aRect);
   2259  }
   2260  Derived& And(const Rect& aRect1, const Rect& aRect2) {
   2261    Rect TmpRect;
   2262 
   2263    TmpRect.IntersectRect(aRect1, aRect2);
   2264    mImpl = ToRect(TmpRect);
   2265    return This();
   2266  }
   2267 
   2268  Derived& OrWith(const Derived& aOther) { return Or(This(), aOther); }
   2269  Derived& OrWith(const Rect& aOther) { return Or(This(), aOther); }
   2270  Derived& Or(const Derived& aRgn1, const Derived& aRgn2) {
   2271    mImpl.Or(aRgn1.mImpl, aRgn2.mImpl);
   2272    return This();
   2273  }
   2274  Derived& Or(const Derived& aRegion, const Rect& aRect) {
   2275    mImpl.Or(aRegion.mImpl, ToRect(aRect));
   2276    return This();
   2277  }
   2278  Derived& Or(const Rect& aRect, const Derived& aRegion) {
   2279    return Or(aRegion, aRect);
   2280  }
   2281  Derived& Or(const Rect& aRect1, const Rect& aRect2) {
   2282    mImpl = ToRect(aRect1);
   2283    return Or(This(), aRect2);
   2284  }
   2285 
   2286  Derived& XorWith(const Derived& aOther) { return Xor(This(), aOther); }
   2287  Derived& XorWith(const Rect& aOther) { return Xor(This(), aOther); }
   2288  Derived& Xor(const Derived& aRgn1, const Derived& aRgn2) {
   2289    mImpl.Xor(aRgn1.mImpl, aRgn2.mImpl);
   2290    return This();
   2291  }
   2292  Derived& Xor(const Derived& aRegion, const Rect& aRect) {
   2293    mImpl.Xor(aRegion.mImpl, ToRect(aRect));
   2294    return This();
   2295  }
   2296  Derived& Xor(const Rect& aRect, const Derived& aRegion) {
   2297    return Xor(aRegion, aRect);
   2298  }
   2299  Derived& Xor(const Rect& aRect1, const Rect& aRect2) {
   2300    mImpl = ToRect(aRect1);
   2301    return Xor(This(), aRect2);
   2302  }
   2303 
   2304  Derived& SubOut(const Derived& aOther) { return Sub(This(), aOther); }
   2305  Derived& SubOut(const Rect& aOther) { return Sub(This(), aOther); }
   2306  Derived& Sub(const Derived& aRgn1, const Derived& aRgn2) {
   2307    mImpl.Sub(aRgn1.mImpl, aRgn2.mImpl);
   2308    return This();
   2309  }
   2310  Derived& Sub(const Derived& aRegion, const Rect& aRect) {
   2311    mImpl.Sub(aRegion.mImpl, ToRect(aRect));
   2312    return This();
   2313  }
   2314  Derived& Sub(const Rect& aRect, const Derived& aRegion) {
   2315    return Sub(Derived(aRect), aRegion);
   2316  }
   2317  Derived& Sub(const Rect& aRect1, const Rect& aRect2) {
   2318    mImpl = ToRect(aRect1);
   2319    return Sub(This(), aRect2);
   2320  }
   2321 
   2322  /**
   2323   * Returns true iff the given point is inside the region. A region
   2324   * created from a rect (x=0, y=0, w=100, h=100) will NOT contain
   2325   * the point x=100, y=100.
   2326   */
   2327  bool Contains(int aX, int aY) const { return mImpl.Contains(aX, aY); }
   2328  bool Contains(const Point& aPoint) const {
   2329    return mImpl.Contains(aPoint.x, aPoint.y);
   2330  }
   2331  bool Contains(const Rect& aRect) const {
   2332    return mImpl.Contains(ToRect(aRect));
   2333  }
   2334  bool Contains(const Derived& aRgn) const {
   2335    return mImpl.Contains(aRgn.mImpl);
   2336  }
   2337  bool Intersects(const Rect& aRect) const {
   2338    return mImpl.Intersects(ToRect(aRect));
   2339  }
   2340 
   2341  void MoveBy(int32_t aXOffset, int32_t aYOffset) {
   2342    MoveBy(Point(aXOffset, aYOffset));
   2343  }
   2344  void MoveBy(Point aPt) { mImpl.MoveBy(aPt.X(), aPt.Y()); }
   2345  Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const {
   2346    return MovedBy(Point(aXOffset, aYOffset));
   2347  }
   2348  Derived MovedBy(const Point& aPt) const {
   2349    Derived copy(This());
   2350    copy.MoveBy(aPt);
   2351    return copy;
   2352  }
   2353 
   2354  Derived Intersect(const Derived& aOther) const {
   2355    Derived intersection;
   2356    intersection.And(This(), aOther);
   2357    return intersection;
   2358  }
   2359 
   2360  void Inflate(const Margin& aMargin) {
   2361    mImpl.Inflate(
   2362        nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left));
   2363  }
   2364  Derived Inflated(const Margin& aMargin) const {
   2365    Derived copy(This());
   2366    copy.Inflate(aMargin);
   2367    return copy;
   2368  }
   2369 
   2370  void SetEmpty() { mImpl.SetEmpty(); }
   2371 
   2372  bool IsEmpty() const { return mImpl.IsEmpty(); }
   2373  bool IsComplex() const { return mImpl.IsComplex(); }
   2374  bool IsEqual(const Derived& aRegion) const {
   2375    return mImpl.IsEqual(aRegion.mImpl);
   2376  }
   2377  uint32_t GetNumRects() const { return mImpl.GetNumRects(); }
   2378  Rect GetBounds() const { return FromRect(mImpl.GetBounds()); }
   2379  uint64_t Area() const { return mImpl.Area(); }
   2380  nsRegion ToAppUnits(nscoord aAppUnitsPerPixel) const {
   2381    nsRegion result;
   2382    for (auto iter = RectIterator(*this); !iter.Done(); iter.Next()) {
   2383      nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel);
   2384      result.Or(result, appRect);
   2385    }
   2386    return result;
   2387  }
   2388  Rect GetLargestRectangle(const Rect& aContainingRect = Rect()) const {
   2389    return FromRect(mImpl.GetLargestRectangle(ToRect(aContainingRect)));
   2390  }
   2391 
   2392  Derived& ScaleRoundOut(float aXScale, float aYScale) {
   2393    mImpl.ScaleRoundOut(aXScale, aYScale);
   2394    return This();
   2395  }
   2396 
   2397  Derived& ScaleInverseRoundOut(float aXScale, float aYScale) {
   2398    mImpl.ScaleInverseRoundOut(aXScale, aYScale);
   2399    return This();
   2400  }
   2401 
   2402  // Prefer using TransformBy(matrix, region) from UnitTransforms.h,
   2403  // as applying the transform should typically change the unit system.
   2404  // TODO(botond): Move this to IntRegionTyped and disable it for
   2405  //               unit != UnknownUnits.
   2406  Derived& Transform(const mozilla::gfx::Matrix4x4& aTransform) {
   2407    mImpl.Transform(aTransform);
   2408    return This();
   2409  }
   2410 
   2411  /**
   2412   * Make sure the region has at most aMaxRects by adding area to it
   2413   * if necessary. The simplified region will be a superset of the
   2414   * original region. The simplified region's bounding box will be
   2415   * the same as for the current region.
   2416   */
   2417  void SimplifyOutward(uint32_t aMaxRects) { mImpl.SimplifyOutward(aMaxRects); }
   2418  void SimplifyOutwardByArea(uint32_t aThreshold) {
   2419    mImpl.SimplifyOutwardByArea(aThreshold);
   2420  }
   2421  /**
   2422   * Make sure the region has at most aMaxRects by removing area from
   2423   * it if necessary. The simplified region will be a subset of the
   2424   * original region.
   2425   */
   2426  void SimplifyInward(uint32_t aMaxRects) { mImpl.SimplifyInward(aMaxRects); }
   2427 
   2428  typedef void (*visitFn)(void* closure, VisitSide side, int x1, int y1, int x2,
   2429                          int y2);
   2430  void VisitEdges(visitFn visit, void* closure) const {
   2431    mImpl.VisitEdges(visit, closure);
   2432  }
   2433 
   2434  nsCString ToString() const { return mImpl.ToString(); }
   2435 
   2436  class RectIterator {
   2437    nsRegion::RectIterator mImpl;  // The underlying iterator.
   2438    mutable Rect mTmp;             // The most recently gotten rectangle.
   2439 
   2440   public:
   2441    explicit RectIterator(const BaseIntRegion& aRegion)
   2442        : mImpl(aRegion.mImpl) {}
   2443 
   2444    bool Done() const { return mImpl.Done(); }
   2445 
   2446    const Rect& Get() const {
   2447      mTmp = FromRect(mImpl.Get());
   2448      return mTmp;
   2449    }
   2450 
   2451    void Next() { mImpl.Next(); }
   2452  };
   2453 
   2454  RectIterator RectIter() const { return RectIterator(*this); }
   2455 
   2456 protected:
   2457  // Expose enough to derived classes from them to define conversions
   2458  // between different types of BaseIntRegions.
   2459  explicit BaseIntRegion(const nsRegion& aImpl) : mImpl(aImpl) {}
   2460  const nsRegion& Impl() const { return mImpl; }
   2461 
   2462 private:
   2463  nsRegion mImpl;
   2464 
   2465  static nsRect ToRect(const Rect& aRect) {
   2466    return nsRect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
   2467  }
   2468  static Rect FromRect(const nsRect& aRect) {
   2469    return Rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
   2470  }
   2471 
   2472  Derived& This() { return *static_cast<Derived*>(this); }
   2473  const Derived& This() const { return *static_cast<const Derived*>(this); }
   2474 };
   2475 
   2476 template <class units>
   2477 class IntRegionTyped
   2478    : public BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>,
   2479                           IntPointTyped<units>, IntMarginTyped<units>> {
   2480  typedef BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>,
   2481                        IntPointTyped<units>, IntMarginTyped<units>>
   2482      Super;
   2483 
   2484  // Make other specializations of IntRegionTyped friends.
   2485  template <typename OtherUnits>
   2486  friend class IntRegionTyped;
   2487 
   2488  static_assert(IsPixel<units>::value,
   2489                "'units' must be a coordinate system tag");
   2490 
   2491 public:
   2492  typedef IntRectTyped<units> RectType;
   2493  typedef IntPointTyped<units> PointType;
   2494  typedef IntMarginTyped<units> MarginType;
   2495 
   2496  // Forward constructors.
   2497  IntRegionTyped() = default;
   2498  MOZ_IMPLICIT IntRegionTyped(const IntRectTyped<units>& aRect)
   2499      : Super(aRect) {}
   2500  IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {}
   2501  explicit IntRegionTyped(mozilla::gfx::ArrayView<pixman_box32_t> aRects)
   2502      : Super(aRects) {}
   2503  IntRegionTyped(IntRegionTyped&& aRegion) : Super(std::move(aRegion)) {}
   2504 
   2505  // Assignment operators need to be forwarded as well, otherwise the compiler
   2506  // will declare deleted ones.
   2507  IntRegionTyped& operator=(const IntRegionTyped& aRegion) {
   2508    return Super::operator=(aRegion);
   2509  }
   2510  IntRegionTyped& operator=(IntRegionTyped&& aRegion) {
   2511    return Super::operator=(std::move(aRegion));
   2512  }
   2513 
   2514  static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion) {
   2515    return IntRegionTyped(aRegion.Impl());
   2516  }
   2517  IntRegion ToUnknownRegion() const {
   2518    // Need |this->| because Impl() is defined in a dependent base class.
   2519    return IntRegion(this->Impl());
   2520  }
   2521 
   2522 private:
   2523  // This is deliberately private, so calling code uses FromUnknownRegion().
   2524  explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {}
   2525 };
   2526 
   2527 }  // namespace gfx
   2528 }  // namespace mozilla
   2529 
   2530 typedef mozilla::gfx::IntRegion nsIntRegion;
   2531 
   2532 #endif