tor-browser

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

nsRegion.cpp (32817B)


      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 #include "nsRegion.h"
      8 #include "nsTArray.h"
      9 #include "gfxUtils.h"
     10 #include "gfx2DGlue.h"
     11 #include "mozilla/ToString.h"
     12 
     13 void nsRegion::AssertStateInternal() const {
     14  bool failed = false;
     15  // Verify consistent state inside the region.
     16  int32_t lastY = INT32_MIN;
     17  int32_t lowestX = INT32_MAX;
     18  int32_t highestX = INT32_MIN;
     19  for (auto iter = mBands.begin(); iter != mBands.end(); iter++) {
     20    const Band& band = *iter;
     21    if (band.bottom <= band.top) {
     22      failed = true;
     23      break;
     24    }
     25    if (band.top < lastY) {
     26      failed = true;
     27      break;
     28    }
     29    lastY = band.bottom;
     30 
     31    lowestX = std::min(lowestX, band.mStrips.begin()->left);
     32    highestX = std::max(highestX, band.mStrips.LastElement().right);
     33 
     34    int32_t lastX = INT32_MIN;
     35    if (iter != mBands.begin()) {
     36      auto prev = iter;
     37      prev--;
     38 
     39      if (prev->bottom == iter->top) {
     40        if (band.EqualStrips(*prev)) {
     41          failed = true;
     42          break;
     43        }
     44      }
     45    }
     46    for (const Strip& strip : band.mStrips) {
     47      if (strip.right <= strip.left) {
     48        failed = true;
     49        break;
     50      }
     51      if (strip.left <= lastX) {
     52        failed = true;
     53        break;
     54      }
     55      lastX = strip.right;
     56    }
     57    if (failed) {
     58      break;
     59    }
     60  }
     61 
     62  if (!(mBounds.IsEqualEdges(CalculateBounds()))) {
     63    failed = true;
     64  }
     65 
     66  if (failed) {
     67 #ifdef DEBUG_REGIONS
     68    if (mCurrentOpGenerator) {
     69      mCurrentOpGenerator->OutputOp();
     70    }
     71 #endif
     72    MOZ_ASSERT(false);
     73  }
     74 }
     75 
     76 bool nsRegion::Contains(const nsRegion& aRgn) const {
     77  // XXX this could be made faster by iterating over
     78  // both regions at the same time some how
     79  for (auto iter = aRgn.RectIter(); !iter.Done(); iter.Next()) {
     80    if (!Contains(iter.Get())) {
     81      return false;
     82    }
     83  }
     84  return true;
     85 }
     86 
     87 bool nsRegion::Intersects(const nsRectAbsolute& aRect) const {
     88  if (mBands.IsEmpty()) {
     89    return mBounds.Intersects(aRect);
     90  }
     91 
     92  if (!mBounds.Intersects(aRect)) {
     93    return false;
     94  }
     95 
     96  Strip rectStrip(aRect.X(), aRect.XMost());
     97 
     98  auto iter = mBands.begin();
     99  while (iter != mBands.end()) {
    100    if (iter->top >= aRect.YMost()) {
    101      return false;
    102    }
    103 
    104    if (iter->bottom <= aRect.Y()) {
    105      // This band is entirely before aRect, move on.
    106      iter++;
    107      continue;
    108    }
    109 
    110    if (!iter->Intersects(rectStrip)) {
    111      // This band does not intersect aRect horizontally. Move on.
    112      iter++;
    113      continue;
    114    }
    115 
    116    // This band intersects with aRect.
    117    return true;
    118  }
    119 
    120  return false;
    121 }
    122 
    123 void nsRegion::Inflate(const nsMargin& aMargin) {
    124  nsRegion newRegion;
    125  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    126    nsRectAbsolute rect = iter.GetAbsolute();
    127    rect.Inflate(aMargin);
    128    newRegion.AddRect(rect);
    129  }
    130 
    131  *this = std::move(newRegion);
    132 }
    133 
    134 void nsRegion::SimplifyOutward(uint32_t aMaxRects) {
    135  MOZ_ASSERT(aMaxRects >= 1, "Invalid max rect count");
    136 
    137  if (GetNumRects() <= aMaxRects) {
    138    return;
    139  }
    140 
    141  // Try combining rects in horizontal bands into a single rect
    142  // The goal here is to try to keep groups of rectangles that are vertically
    143  // discontiguous as separate rectangles in the final region. This is
    144  // simple and fast to implement and page contents tend to vary more
    145  // vertically than horizontally (which is why our rectangles are stored
    146  // sorted by y-coordinate, too).
    147  //
    148  // Note: if boxes share y1 because of the canonical representation they
    149  // will share y2
    150 
    151  size_t idx = 0;
    152 
    153  while (idx < mBands.Length()) {
    154    size_t oldIdx = idx;
    155    mBands[idx].mStrips.begin()->right =
    156        mBands[idx].mStrips.LastElement().right;
    157    mBands[idx].mStrips.TruncateLength(1);
    158    idx++;
    159 
    160    // Merge any bands with the same bounds.
    161    while (idx < mBands.Length() &&
    162           mBands[idx].mStrips.begin()->left ==
    163               mBands[oldIdx].mStrips.begin()->left &&
    164           mBands[idx].mStrips.LastElement().right ==
    165               mBands[oldIdx].mStrips.begin()->right) {
    166      mBands[oldIdx].bottom = mBands[idx].bottom;
    167      mBands.RemoveElementAt(idx);
    168    }
    169  }
    170 
    171  AssertState();
    172 
    173  // mBands.size() is now equal to our rect count.
    174  if (mBands.Length() > aMaxRects) {
    175    *this = GetBounds();
    176  }
    177 }
    178 
    179 // compute the covered area difference between two rows.
    180 // by iterating over both rows simultaneously and adding up
    181 // the additional increase in area caused by extending each
    182 // of the rectangles to the combined height of both rows
    183 uint32_t nsRegion::ComputeMergedAreaIncrease(const Band& aTopBand,
    184                                             const Band& aBottomBand) {
    185  uint32_t totalArea = 0;
    186 
    187  uint32_t topHeight = aBottomBand.top - aTopBand.top;
    188  uint32_t bottomHeight = aBottomBand.bottom - aTopBand.bottom;
    189  uint32_t currentStripBottom = 0;
    190 
    191  // This could be done with slightly better worse case performance by merging
    192  // these two for-loops, but this makes the code a lot easier to understand.
    193  for (auto& strip : aTopBand.mStrips) {
    194    if (currentStripBottom == aBottomBand.mStrips.Length() ||
    195        strip.right < aBottomBand.mStrips[currentStripBottom].left) {
    196      totalArea += bottomHeight * strip.Size();
    197      continue;
    198    }
    199 
    200    int32_t currentX = strip.left;
    201    while (currentStripBottom != aBottomBand.mStrips.Length() &&
    202           aBottomBand.mStrips[currentStripBottom].left < strip.right) {
    203      if (currentX >= strip.right) {
    204        break;
    205      }
    206      if (currentX < aBottomBand.mStrips[currentStripBottom].left) {
    207        // Add the part that's not intersecting.
    208        totalArea += (aBottomBand.mStrips[currentStripBottom].left - currentX) *
    209                     bottomHeight;
    210      }
    211 
    212      currentX =
    213          std::max(aBottomBand.mStrips[currentStripBottom].right, currentX);
    214      currentStripBottom++;
    215    }
    216 
    217    // Add remainder of this strip.
    218    if (currentX < strip.right) {
    219      totalArea += (strip.right - currentX) * bottomHeight;
    220    }
    221    if (currentStripBottom) {
    222      currentStripBottom--;
    223    }
    224  }
    225  uint32_t currentStripTop = 0;
    226  for (auto& strip : aBottomBand.mStrips) {
    227    if (currentStripTop == aTopBand.mStrips.Length() ||
    228        strip.right < aTopBand.mStrips[currentStripTop].left) {
    229      totalArea += topHeight * strip.Size();
    230      continue;
    231    }
    232 
    233    int32_t currentX = strip.left;
    234    while (currentStripTop != aTopBand.mStrips.Length() &&
    235           aTopBand.mStrips[currentStripTop].left < strip.right) {
    236      if (currentX >= strip.right) {
    237        break;
    238      }
    239      if (currentX < aTopBand.mStrips[currentStripTop].left) {
    240        // Add the part that's not intersecting.
    241        totalArea +=
    242            (aTopBand.mStrips[currentStripTop].left - currentX) * topHeight;
    243      }
    244 
    245      currentX = std::max(aTopBand.mStrips[currentStripTop].right, currentX);
    246      currentStripTop++;
    247    }
    248 
    249    // Add remainder of this strip.
    250    if (currentX < strip.right) {
    251      totalArea += (strip.right - currentX) * topHeight;
    252    }
    253    if (currentStripTop) {
    254      currentStripTop--;
    255    }
    256  }
    257  return totalArea;
    258 }
    259 
    260 void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold) {
    261  if (mBands.Length() < 2) {
    262    // We have only one or no row and we're done.
    263    return;
    264  }
    265 
    266  uint32_t currentBand = 0;
    267  do {
    268    Band& band = mBands[currentBand];
    269 
    270    uint32_t totalArea =
    271        ComputeMergedAreaIncrease(band, mBands[currentBand + 1]);
    272 
    273    if (totalArea <= aThreshold) {
    274      for (Strip& strip : mBands[currentBand + 1].mStrips) {
    275        // This could use an optimized function to merge two bands.
    276        band.InsertStrip(strip);
    277      }
    278      band.bottom = mBands[currentBand + 1].bottom;
    279      mBands.RemoveElementAt(currentBand + 1);
    280    } else {
    281      currentBand++;
    282    }
    283  } while (currentBand + 1 < mBands.Length());
    284 
    285  EnsureSimplified();
    286  AssertState();
    287 }
    288 
    289 typedef void (*visit_fn)(void* closure, VisitSide side, int x1, int y1, int x2,
    290                         int y2);
    291 
    292 void nsRegion::VisitEdges(visit_fn visit, void* closure) const {
    293  if (mBands.IsEmpty()) {
    294    visit(closure, VisitSide::LEFT, mBounds.X(), mBounds.Y(), mBounds.X(),
    295          mBounds.YMost());
    296    visit(closure, VisitSide::RIGHT, mBounds.XMost(), mBounds.Y(),
    297          mBounds.XMost(), mBounds.YMost());
    298    visit(closure, VisitSide::TOP, mBounds.X() - 1, mBounds.Y(),
    299          mBounds.XMost() + 1, mBounds.Y());
    300    visit(closure, VisitSide::BOTTOM, mBounds.X() - 1, mBounds.YMost(),
    301          mBounds.XMost() + 1, mBounds.YMost());
    302    return;
    303  }
    304 
    305  auto band = std::begin(mBands);
    306  auto bandFinal = std::end(mBands);
    307  bandFinal--;
    308  for (const Strip& strip : band->mStrips) {
    309    visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left,
    310          band->bottom);
    311    visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right,
    312          band->bottom);
    313    visit(closure, VisitSide::TOP, strip.left - 1, band->top, strip.right + 1,
    314          band->top);
    315  }
    316 
    317  if (band != bandFinal) {
    318    do {
    319      const Band& topBand = *band;
    320      band++;
    321 
    322      for (const Strip& strip : band->mStrips) {
    323        visit(closure, VisitSide::LEFT, strip.left, band->top, strip.left,
    324              band->bottom);
    325        visit(closure, VisitSide::RIGHT, strip.right, band->top, strip.right,
    326              band->bottom);
    327      }
    328 
    329      if (band->top == topBand.bottom) {
    330        // Two bands touching each other vertically.
    331        const Band& bottomBand = *band;
    332        auto topStrip = std::begin(topBand.mStrips);
    333        auto bottomStrip = std::begin(bottomBand.mStrips);
    334 
    335        int y = topBand.bottom;
    336 
    337        // State from this point on along the vertical edge:
    338        // 0 - Empty
    339        // 1 - Touched by top rect
    340        // 2 - Touched by bottom rect
    341        // 3 - Touched on both sides
    342        int state;
    343        const int TouchedByNothing = 0;
    344        const int TouchedByTop = 1;
    345        const int TouchedByBottom = 2;
    346        // We always start with nothing.
    347        int oldState = TouchedByNothing;
    348        // Last state change, adjusted by -1 if the last state change was
    349        // a change away from 0.
    350        int lastX = std::min(topStrip->left, bottomStrip->left) - 1;
    351 
    352        // Current edge being considered for top and bottom,
    353        // 0 - left, 1 - right.
    354        bool topEdgeIsLeft = true;
    355        bool bottomEdgeIsLeft = true;
    356        while (topStrip != std::end(topBand.mStrips) &&
    357               bottomStrip != std::end(bottomBand.mStrips)) {
    358          int topPos;
    359          int bottomPos;
    360          if (topEdgeIsLeft) {
    361            topPos = topStrip->left;
    362          } else {
    363            topPos = topStrip->right;
    364          }
    365          if (bottomEdgeIsLeft) {
    366            bottomPos = bottomStrip->left;
    367          } else {
    368            bottomPos = bottomStrip->right;
    369          }
    370 
    371          int currentX = std::min(topPos, bottomPos);
    372          if (topPos < bottomPos) {
    373            if (topEdgeIsLeft) {
    374              state = oldState | TouchedByTop;
    375            } else {
    376              state = oldState ^ TouchedByTop;
    377              topStrip++;
    378            }
    379            topEdgeIsLeft = !topEdgeIsLeft;
    380          } else if (bottomPos < topPos) {
    381            if (bottomEdgeIsLeft) {
    382              state = oldState | TouchedByBottom;
    383            } else {
    384              state = oldState ^ TouchedByBottom;
    385              bottomStrip++;
    386            }
    387            bottomEdgeIsLeft = !bottomEdgeIsLeft;
    388          } else {
    389            // bottomPos == topPos
    390            state = TouchedByNothing;
    391            if (bottomEdgeIsLeft) {
    392              state = TouchedByBottom;
    393            } else {
    394              bottomStrip++;
    395            }
    396            if (topEdgeIsLeft) {
    397              state |= TouchedByTop;
    398            } else {
    399              topStrip++;
    400            }
    401            topEdgeIsLeft = !topEdgeIsLeft;
    402            bottomEdgeIsLeft = !bottomEdgeIsLeft;
    403          }
    404 
    405          MOZ_ASSERT(state != oldState);
    406          if (oldState == TouchedByNothing) {
    407            // We had nothing before, make sure the left edge will be padded.
    408            lastX = currentX - 1;
    409          } else if (oldState == TouchedByTop) {
    410            if (state == TouchedByNothing) {
    411              visit(closure, VisitSide::BOTTOM, lastX, y, currentX + 1, y);
    412            } else {
    413              visit(closure, VisitSide::BOTTOM, lastX, y, currentX, y);
    414              lastX = currentX;
    415            }
    416          } else if (oldState == TouchedByBottom) {
    417            if (state == TouchedByNothing) {
    418              visit(closure, VisitSide::TOP, lastX, y, currentX + 1, y);
    419            } else {
    420              visit(closure, VisitSide::TOP, lastX, y, currentX, y);
    421              lastX = currentX;
    422            }
    423          } else {
    424            lastX = currentX;
    425          }
    426          oldState = state;
    427        }
    428 
    429        MOZ_ASSERT(!state || (topEdgeIsLeft || bottomEdgeIsLeft));
    430        if (topStrip != std::end(topBand.mStrips)) {
    431          if (!topEdgeIsLeft) {
    432            visit(closure, VisitSide::BOTTOM, lastX, y, topStrip->right + 1, y);
    433            topStrip++;
    434          }
    435          while (topStrip != std::end(topBand.mStrips)) {
    436            visit(closure, VisitSide::BOTTOM, topStrip->left - 1, y,
    437                  topStrip->right + 1, y);
    438            topStrip++;
    439          }
    440        } else if (bottomStrip != std::end(bottomBand.mStrips)) {
    441          if (!bottomEdgeIsLeft) {
    442            visit(closure, VisitSide::TOP, lastX, y, bottomStrip->right + 1, y);
    443            bottomStrip++;
    444          }
    445          while (bottomStrip != std::end(bottomBand.mStrips)) {
    446            visit(closure, VisitSide::TOP, bottomStrip->left - 1, y,
    447                  bottomStrip->right + 1, y);
    448            bottomStrip++;
    449          }
    450        }
    451      } else {
    452        for (const Strip& strip : topBand.mStrips) {
    453          visit(closure, VisitSide::BOTTOM, strip.left - 1, topBand.bottom,
    454                strip.right + 1, topBand.bottom);
    455        }
    456        for (const Strip& strip : band->mStrips) {
    457          visit(closure, VisitSide::TOP, strip.left - 1, band->top,
    458                strip.right + 1, band->top);
    459        }
    460      }
    461    } while (band != bandFinal);
    462  }
    463 
    464  for (const Strip& strip : band->mStrips) {
    465    visit(closure, VisitSide::BOTTOM, strip.left - 1, band->bottom,
    466          strip.right + 1, band->bottom);
    467  }
    468 }
    469 
    470 void nsRegion::SimplifyInward(uint32_t aMaxRects) {
    471  NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
    472 
    473  if (GetNumRects() <= aMaxRects) return;
    474 
    475  SetEmpty();
    476 }
    477 
    478 uint64_t nsRegion::Area() const {
    479  if (mBands.IsEmpty()) {
    480    return mBounds.Area();
    481  }
    482 
    483  uint64_t area = 0;
    484  for (const Band& band : mBands) {
    485    uint32_t height = band.bottom - band.top;
    486    for (const Strip& strip : band.mStrips) {
    487      area += (strip.right - strip.left) * height;
    488    }
    489  }
    490 
    491  return area;
    492 }
    493 
    494 nsRegion& nsRegion::ScaleRoundOut(float aXScale, float aYScale) {
    495  if (mozilla::gfx::FuzzyEqual(aXScale, 1.0f) &&
    496      mozilla::gfx::FuzzyEqual(aYScale, 1.0f)) {
    497    return *this;
    498  }
    499 
    500  nsRegion newRegion;
    501  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    502    nsRectAbsolute rect = iter.GetAbsolute();
    503    rect.ScaleRoundOut(aXScale, aYScale);
    504    newRegion.AddRect(rect);
    505  }
    506 
    507  *this = std::move(newRegion);
    508  return *this;
    509 }
    510 
    511 nsRegion& nsRegion::ScaleInverseRoundOut(float aXScale, float aYScale) {
    512  nsRegion newRegion;
    513  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    514    nsRectAbsolute rect = iter.GetAbsolute();
    515    rect.ScaleInverseRoundOut(aXScale, aYScale);
    516    newRegion.AddRect(rect);
    517  }
    518 
    519  *this = std::move(newRegion);
    520  return *this;
    521 }
    522 
    523 static mozilla::gfx::IntRect TransformRect(
    524    const mozilla::gfx::IntRect& aRect,
    525    const mozilla::gfx::Matrix4x4& aTransform) {
    526  if (aRect.IsEmpty()) {
    527    return mozilla::gfx::IntRect();
    528  }
    529 
    530  mozilla::gfx::RectDouble rect(aRect.X(), aRect.Y(), aRect.Width(),
    531                                aRect.Height());
    532  rect = aTransform.TransformAndClipBounds(
    533      rect, mozilla::gfx::RectDouble::MaxIntRect());
    534  rect.RoundOut();
    535 
    536  mozilla::gfx::IntRect intRect;
    537  if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
    538    return mozilla::gfx::IntRect();
    539  }
    540 
    541  return intRect;
    542 }
    543 
    544 nsRegion& nsRegion::Transform(const mozilla::gfx::Matrix4x4& aTransform) {
    545  nsRegion newRegion;
    546  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    547    nsRect rect = nsIntRegion::ToRect(
    548        TransformRect(nsIntRegion::FromRect(iter.Get()), aTransform));
    549    newRegion.AddRect(nsRectAbsolute::FromRect(rect));
    550  }
    551 
    552  *this = std::move(newRegion);
    553  return *this;
    554 }
    555 
    556 nsRegion nsRegion::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP,
    557                                                int32_t aToAPP) const {
    558  if (aFromAPP == aToAPP) {
    559    return *this;
    560  }
    561  nsRegion newRegion;
    562  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    563    nsRect rect = iter.Get();
    564    rect = rect.ScaleToOtherAppUnitsRoundOut(aFromAPP, aToAPP);
    565    newRegion.AddRect(nsRectAbsolute::FromRect(rect));
    566  }
    567 
    568  return newRegion;
    569 }
    570 
    571 nsRegion nsRegion::ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
    572                                               int32_t aToAPP) const {
    573  if (aFromAPP == aToAPP) {
    574    return *this;
    575  }
    576 
    577  nsRegion newRegion;
    578  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    579    nsRect rect = iter.Get();
    580    rect = rect.ScaleToOtherAppUnitsRoundIn(aFromAPP, aToAPP);
    581    newRegion.AddRect(nsRectAbsolute::FromRect(rect));
    582  }
    583 
    584  return newRegion;
    585 }
    586 
    587 nsIntRegion nsRegion::ToPixels(nscoord aAppUnitsPerPixel,
    588                               bool aOutsidePixels) const {
    589  nsIntRegion intRegion;
    590  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    591    mozilla::gfx::IntRect deviceRect;
    592    nsRect rect = iter.Get();
    593    if (aOutsidePixels)
    594      deviceRect = rect.ToOutsidePixels(aAppUnitsPerPixel);
    595    else
    596      deviceRect = rect.ToNearestPixels(aAppUnitsPerPixel);
    597    intRegion.OrWith(deviceRect);
    598  }
    599 
    600  return intRegion;
    601 }
    602 
    603 nsIntRegion nsRegion::ToOutsidePixels(nscoord aAppUnitsPerPixel) const {
    604  return ToPixels(aAppUnitsPerPixel, true);
    605 }
    606 
    607 nsIntRegion nsRegion::ToNearestPixels(nscoord aAppUnitsPerPixel) const {
    608  return ToPixels(aAppUnitsPerPixel, false);
    609 }
    610 
    611 nsIntRegion nsRegion::ScaleToNearestPixels(float aScaleX, float aScaleY,
    612                                           nscoord aAppUnitsPerPixel) const {
    613  nsIntRegion result;
    614  for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
    615    mozilla::gfx::IntRect deviceRect =
    616        iter.Get().ScaleToNearestPixels(aScaleX, aScaleY, aAppUnitsPerPixel);
    617    result.Or(result, deviceRect);
    618  }
    619  return result;
    620 }
    621 
    622 nsIntRegion nsRegion::ScaleToOutsidePixels(float aScaleX, float aScaleY,
    623                                           nscoord aAppUnitsPerPixel) const {
    624  // make a copy of the region so that we can mutate it inplace
    625  nsIntRegion intRegion;
    626  for (RectIterator iter = RectIterator(*this); !iter.Done(); iter.Next()) {
    627    nsRect rect = iter.Get();
    628    intRegion.OrWith(
    629        rect.ScaleToOutsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel));
    630  }
    631  return intRegion;
    632 }
    633 
    634 nsIntRegion nsRegion::ScaleToInsidePixels(float aScaleX, float aScaleY,
    635                                          nscoord aAppUnitsPerPixel) const {
    636  /* When scaling a rect, walk forward through the rect list up until the y
    637   * value is greater than the current rect's YMost() value.
    638   *
    639   * For each rect found, check if the rects have a touching edge (in unscaled
    640   * coordinates), and if one edge is entirely contained within the other.
    641   *
    642   * If it is, then the contained edge can be moved (in scaled pixels) to ensure
    643   * that no gap exists.
    644   *
    645   * Since this could be potentially expensive - O(n^2), we only attempt this
    646   * algorithm for the first rect.
    647   */
    648 
    649  if (mBands.IsEmpty()) {
    650    nsIntRect rect = mBounds.ToNSRect().ScaleToInsidePixels(aScaleX, aScaleY,
    651                                                            aAppUnitsPerPixel);
    652    return nsIntRegion(rect);
    653  }
    654 
    655  nsIntRegion intRegion;
    656  RectIterator iter = RectIterator(*this);
    657 
    658  nsRect first = iter.Get();
    659 
    660  mozilla::gfx::IntRect firstDeviceRect =
    661      first.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
    662 
    663  for (iter.Next(); !iter.Done(); iter.Next()) {
    664    nsRect rect = iter.Get();
    665    mozilla::gfx::IntRect deviceRect =
    666        rect.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
    667 
    668    if (rect.Y() <= first.YMost()) {
    669      if (rect.XMost() == first.X() && rect.YMost() <= first.YMost()) {
    670        // rect is touching on the left edge of the first rect and contained
    671        // within the length of its left edge
    672        deviceRect.SetRightEdge(firstDeviceRect.X());
    673      } else if (rect.X() == first.XMost() && rect.YMost() <= first.YMost()) {
    674        // rect is touching on the right edge of the first rect and contained
    675        // within the length of its right edge
    676        deviceRect.SetLeftEdge(firstDeviceRect.XMost());
    677      } else if (rect.Y() == first.YMost()) {
    678        // The bottom of the first rect is on the same line as the top of rect,
    679        // but they aren't necessarily contained.
    680        if (rect.X() <= first.X() && rect.XMost() >= first.XMost()) {
    681          // The top of rect contains the bottom of the first rect
    682          firstDeviceRect.SetBottomEdge(deviceRect.Y());
    683        } else if (rect.X() >= first.X() && rect.XMost() <= first.XMost()) {
    684          // The bottom of the first contains the top of rect
    685          deviceRect.SetTopEdge(firstDeviceRect.YMost());
    686        }
    687      }
    688    }
    689 
    690    intRegion.OrWith(deviceRect);
    691  }
    692 
    693  intRegion.OrWith(firstDeviceRect);
    694  return intRegion;
    695 }
    696 
    697 // A cell's "value" is a pair consisting of
    698 // a) the area of the subrectangle it corresponds to, if it's in
    699 // aContainingRect and in the region, 0 otherwise
    700 // b) the area of the subrectangle it corresponds to, if it's in the region,
    701 // 0 otherwise
    702 // Addition, subtraction and identity are defined on these values in the
    703 // obvious way. Partial order is lexicographic.
    704 // A "large negative value" is defined with large negative numbers for both
    705 // fields of the pair. This negative value has the property that adding any
    706 // number of non-negative values to it always results in a negative value.
    707 //
    708 // The GetLargestRectangle algorithm works in three phases:
    709 //  1) Convert the region into a grid by adding vertical/horizontal lines for
    710 //     each edge of each rectangle in the region.
    711 //  2) For each rectangle in the region, for each cell it contains, set that
    712 //     cells's value as described above.
    713 //  3) Calculate the submatrix with the largest sum such that none of its cells
    714 //     contain any 0s (empty regions). The rectangle represented by the
    715 //     submatrix is the largest rectangle in the region.
    716 //
    717 // Let k be the number of rectangles in the region.
    718 // Let m be the height of the grid generated in step 1.
    719 // Let n be the width of the grid generated in step 1.
    720 //
    721 // Step 1 is O(k) in time and O(m+n) in space for the sparse grid.
    722 // Step 2 is O(mn) in time and O(mn) in additional space for the full grid.
    723 // Step 3 is O(m^2 n) in time and O(mn) in additional space
    724 //
    725 // The implementation of steps 1 and 2 are rather straightforward. However our
    726 // implementation of step 3 uses dynamic programming to achieve its efficiency.
    727 //
    728 // Psuedo code for step 3 is as follows where G is the grid from step 1 and A
    729 // is the array from step 2:
    730 // Phase3 = function (G, A, m, n) {
    731 //   let (t,b,l,r,_) = MaxSum2D(A,m,n)
    732 //   return rect(G[t],G[l],G[r],G[b]);
    733 // }
    734 // MaxSum2D = function (A, m, n) {
    735 //   S = array(m+1,n+1)
    736 //   S[0][i] = 0 for i in [0,n]
    737 //   S[j][0] = 0 for j in [0,m]
    738 //   S[j][i] = (if A[j-1][i-1] = 0 then some large negative value
    739 //                                 else A[j-1][i-1])
    740 //           + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
    741 //
    742 //   // top, bottom, left, right, area
    743 //   var maxRect = (-1, -1, -1, -1, 0);
    744 //
    745 //   for all (m',m'') in [0, m]^2 {
    746 //     let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
    747 //     let ((l,r),area) = MaxSum1D(B,n+1)
    748 //     if (area > maxRect.area) {
    749 //       maxRect := (m', m'', l, r, area)
    750 //     }
    751 //   }
    752 //
    753 //   return maxRect;
    754 // }
    755 //
    756 // Originally taken from Improved algorithms for the k-maximum subarray problem
    757 // for small k - SE Bae, T Takaoka but modified to show the explicit tracking
    758 // of indices and we already have the prefix sums from our one call site so
    759 // there's no need to construct them.
    760 // MaxSum1D = function (A,n) {
    761 //   var minIdx = 0;
    762 //   var min = 0;
    763 //   var maxIndices = (0,0);
    764 //   var max = 0;
    765 //   for i in range(n) {
    766 //     let cand = A[i] - min;
    767 //     if (cand > max) {
    768 //       max := cand;
    769 //       maxIndices := (minIdx, i)
    770 //     }
    771 //     if (min > A[i]) {
    772 //       min := A[i];
    773 //       minIdx := i;
    774 //     }
    775 //   }
    776 //   return (minIdx, maxIdx, max);
    777 // }
    778 
    779 namespace {
    780 // This class represents a partitioning of an axis delineated by coordinates.
    781 // It internally maintains a sorted array of coordinates.
    782 class AxisPartition {
    783 public:
    784  // Adds a new partition at the given coordinate to this partitioning. If
    785  // the coordinate is already present in the partitioning, this does nothing.
    786  void InsertCoord(nscoord c) {
    787    uint32_t i = mStops.IndexOfFirstElementGt(c);
    788    if (i == 0 || mStops[i - 1] != c) {
    789      mStops.InsertElementAt(i, c);
    790    }
    791  }
    792 
    793  // Returns the array index of the given partition point. The partition
    794  // point must already be present in the partitioning.
    795  int32_t IndexOf(nscoord p) const { return mStops.BinaryIndexOf(p); }
    796 
    797  // Returns the partition at the given index which must be non-zero and
    798  // less than the number of partitions in this partitioning.
    799  nscoord StopAt(int32_t index) const { return mStops[index]; }
    800 
    801  // Returns the size of the gap between the partition at the given index and
    802  // the next partition in this partitioning. If the index is the last index
    803  // in the partitioning, the result is undefined.
    804  nscoord StopSize(int32_t index) const {
    805    return mStops[index + 1] - mStops[index];
    806  }
    807 
    808  // Returns the number of partitions in this partitioning.
    809  int32_t GetNumStops() const { return mStops.Length(); }
    810 
    811 private:
    812  nsTArray<nscoord> mStops;
    813 };
    814 
    815 const int64_t kVeryLargeNegativeNumber = 0xffff000000000000ll;
    816 
    817 struct SizePair {
    818  int64_t mSizeContainingRect;
    819  int64_t mSize;
    820 
    821  SizePair() : mSizeContainingRect(0), mSize(0) {}
    822 
    823  static SizePair VeryLargeNegative() {
    824    SizePair result;
    825    result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
    826    return result;
    827  }
    828  bool operator<(const SizePair& aOther) const {
    829    if (mSizeContainingRect < aOther.mSizeContainingRect) return true;
    830    if (mSizeContainingRect > aOther.mSizeContainingRect) return false;
    831    return mSize < aOther.mSize;
    832  }
    833  bool operator>(const SizePair& aOther) const {
    834    return aOther.operator<(*this);
    835  }
    836  SizePair operator+(const SizePair& aOther) const {
    837    SizePair result = *this;
    838    result.mSizeContainingRect += aOther.mSizeContainingRect;
    839    result.mSize += aOther.mSize;
    840    return result;
    841  }
    842  SizePair operator-(const SizePair& aOther) const {
    843    SizePair result = *this;
    844    result.mSizeContainingRect -= aOther.mSizeContainingRect;
    845    result.mSize -= aOther.mSize;
    846    return result;
    847  }
    848 };
    849 
    850 // Returns the sum and indices of the subarray with the maximum sum of the
    851 // given array (A,n), assuming the array is already in prefix sum form.
    852 SizePair MaxSum1D(const nsTArray<SizePair>& A, int32_t n, int32_t* minIdx,
    853                  int32_t* maxIdx) {
    854  // The min/max indicies of the largest subarray found so far
    855  SizePair min, max;
    856  int32_t currentMinIdx = 0;
    857 
    858  *minIdx = 0;
    859  *maxIdx = 0;
    860 
    861  // Because we're given the array in prefix sum form, we know the first
    862  // element is 0
    863  for (int32_t i = 1; i < n; i++) {
    864    SizePair cand = A[i] - min;
    865    if (cand > max) {
    866      max = cand;
    867      *minIdx = currentMinIdx;
    868      *maxIdx = i;
    869    }
    870    if (min > A[i]) {
    871      min = A[i];
    872      currentMinIdx = i;
    873    }
    874  }
    875 
    876  return max;
    877 }
    878 }  // namespace
    879 
    880 nsRect nsRegion::GetLargestRectangle(const nsRect& aContainingRect) const {
    881  nsRect bestRect;
    882 
    883  if (GetNumRects() <= 1) {
    884    bestRect = GetBounds();
    885    return bestRect;
    886  }
    887 
    888  AxisPartition xaxis, yaxis;
    889 
    890  // Step 1: Calculate the grid lines
    891  for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
    892    const nsRect& rect = iter.Get();
    893    xaxis.InsertCoord(rect.X());
    894    xaxis.InsertCoord(rect.XMost());
    895    yaxis.InsertCoord(rect.Y());
    896    yaxis.InsertCoord(rect.YMost());
    897  }
    898  if (!aContainingRect.IsEmpty()) {
    899    xaxis.InsertCoord(aContainingRect.X());
    900    xaxis.InsertCoord(aContainingRect.XMost());
    901    yaxis.InsertCoord(aContainingRect.Y());
    902    yaxis.InsertCoord(aContainingRect.YMost());
    903  }
    904 
    905  // Step 2: Fill out the grid with the areas
    906  // Note: due to the ordering of rectangles in the region, it is not always
    907  // possible to combine steps 2 and 3 so we don't try to be clever.
    908  int32_t matrixHeight = yaxis.GetNumStops() - 1;
    909  int32_t matrixWidth = xaxis.GetNumStops() - 1;
    910  int32_t matrixSize = matrixHeight * matrixWidth;
    911  nsTArray<SizePair> areas(matrixSize);
    912  areas.SetLength(matrixSize);
    913 
    914  for (auto iter = RectIter(); !iter.Done(); iter.Next()) {
    915    const nsRect& rect = iter.Get();
    916    int32_t xstart = xaxis.IndexOf(rect.X());
    917    int32_t xend = xaxis.IndexOf(rect.XMost());
    918    int32_t y = yaxis.IndexOf(rect.Y());
    919    int32_t yend = yaxis.IndexOf(rect.YMost());
    920 
    921    for (; y < yend; y++) {
    922      nscoord height = yaxis.StopSize(y);
    923      for (int32_t x = xstart; x < xend; x++) {
    924        nscoord width = xaxis.StopSize(x);
    925        int64_t size = width * int64_t(height);
    926        if (rect.Intersects(aContainingRect)) {
    927          areas[y * matrixWidth + x].mSizeContainingRect = size;
    928        }
    929        areas[y * matrixWidth + x].mSize = size;
    930      }
    931    }
    932  }
    933 
    934  // Step 3: Find the maximum submatrix sum that does not contain a rectangle
    935  {
    936    // First get the prefix sum array
    937    int32_t m = matrixHeight + 1;
    938    int32_t n = matrixWidth + 1;
    939    nsTArray<SizePair> pareas(m * n);
    940    pareas.SetLength(m * n);
    941    for (int32_t y = 1; y < m; y++) {
    942      for (int32_t x = 1; x < n; x++) {
    943        SizePair area = areas[(y - 1) * matrixWidth + x - 1];
    944        if (!area.mSize) {
    945          area = SizePair::VeryLargeNegative();
    946        }
    947        area = area + pareas[y * n + x - 1] + pareas[(y - 1) * n + x] -
    948               pareas[(y - 1) * n + x - 1];
    949        pareas[y * n + x] = area;
    950      }
    951    }
    952 
    953    // No longer need the grid
    954    areas.SetLength(0);
    955 
    956    SizePair bestArea;
    957    struct {
    958      int32_t left, top, right, bottom;
    959    } bestRectIndices = {0, 0, 0, 0};
    960    for (int32_t m1 = 0; m1 < m; m1++) {
    961      for (int32_t m2 = m1 + 1; m2 < m; m2++) {
    962        nsTArray<SizePair> B;
    963        B.SetLength(n);
    964        for (int32_t i = 0; i < n; i++) {
    965          B[i] = pareas[m2 * n + i] - pareas[m1 * n + i];
    966        }
    967        int32_t minIdx, maxIdx;
    968        SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
    969        if (area > bestArea) {
    970          bestRectIndices.left = minIdx;
    971          bestRectIndices.top = m1;
    972          bestRectIndices.right = maxIdx;
    973          bestRectIndices.bottom = m2;
    974          bestArea = area;
    975        }
    976      }
    977    }
    978 
    979    bestRect.MoveTo(xaxis.StopAt(bestRectIndices.left),
    980                    yaxis.StopAt(bestRectIndices.top));
    981    bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.X(),
    982                    yaxis.StopAt(bestRectIndices.bottom) - bestRect.Y());
    983  }
    984 
    985  return bestRect;
    986 }
    987 
    988 std::ostream& operator<<(std::ostream& stream, const nsRegion& m) {
    989  stream << "[";
    990 
    991  bool first = true;
    992  for (auto iter = m.RectIter(); !iter.Done(); iter.Next()) {
    993    if (!first) {
    994      stream << "; ";
    995    } else {
    996      first = false;
    997    }
    998    const nsRect& rect = iter.Get();
    999    stream << rect.X() << "," << rect.Y() << "," << rect.XMost() << ","
   1000           << rect.YMost();
   1001  }
   1002 
   1003  stream << "]";
   1004  return stream;
   1005 }
   1006 
   1007 void nsRegion::OutputToStream(std::string aObjName,
   1008                              std::ostream& stream) const {
   1009  auto iter = RectIter();
   1010  nsRect r = iter.Get();
   1011  stream << "nsRegion " << aObjName << "(nsRect(" << r.X() << ", " << r.Y()
   1012         << ", " << r.Width() << ", " << r.Height() << "));\n";
   1013  iter.Next();
   1014 
   1015  for (; !iter.Done(); iter.Next()) {
   1016    nsRect r = iter.Get();
   1017    stream << aObjName << ".OrWith(nsRect(" << r.X() << ", " << r.Y() << ", "
   1018           << r.Width() << ", " << r.Height() << "));\n";
   1019  }
   1020 }
   1021 
   1022 nsCString nsRegion::ToString() const {
   1023  return nsCString(mozilla::ToString(*this).c_str());
   1024 }