gfx/src/nsRegion.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsRegion.h"
     7 #include "nsPrintfCString.h"
     8 #include "nsTArray.h"
    11 bool nsRegion::Contains(const nsRegion& aRgn) const
    12 {
    13   // XXX this could be made faster
    14   nsRegionRectIterator iter(aRgn);
    15   while (const nsRect* r = iter.Next()) {
    16     if (!Contains (*r)) {
    17       return false;
    18     }
    19   }
    20   return true;
    21 }
    23 bool nsRegion::Intersects(const nsRect& aRect) const
    24 {
    25   // XXX this could be made faster
    26   nsRegionRectIterator iter(*this);
    27   while (const nsRect* r = iter.Next()) {
    28     if (r->Intersects(aRect)) {
    29       return true;
    30     }
    31   }
    32   return false;
    33 }
    35 void nsRegion::Inflate(const nsMargin& aMargin)
    36 {
    37   int n;
    38   pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
    39   for (int i=0; i<n; i++) {
    40     nsRect rect = BoxToRect(boxes[i]);
    41     rect.Inflate(aMargin);
    42     boxes[i] = RectToBox(rect);
    43   }
    45   pixman_region32_t region;
    46   // This will union all of the rectangles and runs in about O(n lg(n))
    47   pixman_region32_init_rects(&region, boxes, n);
    49   pixman_region32_fini(&mImpl);
    50   mImpl = region;
    51 }
    53 void nsRegion::SimplifyOutward (uint32_t aMaxRects)
    54 {
    55   MOZ_ASSERT(aMaxRects >= 1, "Invalid max rect count");
    57   if (GetNumRects() <= aMaxRects)
    58     return;
    60   pixman_box32_t *boxes;
    61   int n;
    62   boxes = pixman_region32_rectangles(&mImpl, &n);
    64   // Try combining rects in horizontal bands into a single rect
    65   int dest = 0;
    66   for (int src = 1; src < n; src++)
    67   {
    68     // The goal here is to try to keep groups of rectangles that are vertically
    69     // discontiguous as separate rectangles in the final region. This is
    70     // simple and fast to implement and page contents tend to vary more
    71     // vertically than horizontally (which is why our rectangles are stored
    72     // sorted by y-coordinate, too).
    73     //
    74     // Note: if boxes share y1 because of the canonical representation they
    75     // will share y2
    76     while ((src < (n)) && boxes[dest].y1 == boxes[src].y1) {
    77       // merge box[i] and box[i+1]
    78       boxes[dest].x2 = boxes[src].x2;
    79       src++;
    80     }
    81     if (src < n) {
    82       dest++;
    83       boxes[dest] = boxes[src];
    84     }
    85   }
    87   uint32_t reducedCount = dest+1;
    88   // pixman has a special representation for
    89   // regions of 1 rectangle. So just use the
    90   // bounds in that case
    91   if (reducedCount > 1 && reducedCount <= aMaxRects) {
    92     // reach into pixman and lower the number
    93     // of rects stored in data.
    94     mImpl.data->numRects = reducedCount;
    95   } else {
    96     *this = GetBounds();
    97   }
    98 }
   100 // compute the covered area difference between two rows.
   101 // by iterating over both rows simultaneously and adding up
   102 // the additional increase in area caused by extending each
   103 // of the rectangles to the combined height of both rows
   104 static uint32_t ComputeMergedAreaIncrease(pixman_box32_t *topRects,
   105 		                     pixman_box32_t *topRectsEnd,
   106 		                     pixman_box32_t *bottomRects,
   107 		                     pixman_box32_t *bottomRectsEnd)
   108 {
   109   uint32_t totalArea = 0;
   110   struct pt {
   111     int32_t x, y;
   112   };
   115   pt *i = (pt*)topRects;
   116   pt *end_i = (pt*)topRectsEnd;
   117   pt *j = (pt*)bottomRects;
   118   pt *end_j = (pt*)bottomRectsEnd;
   119   bool top = false;
   120   bool bottom = false;
   122   int cur_x = i->x;
   123   bool top_next = top;
   124   bool bottom_next = bottom;
   125   //XXX: we could probably simplify this condition and perhaps move it into the loop below
   126   if (j->x < cur_x) {
   127     cur_x = j->x;
   128     j++;
   129     bottom_next = !bottom;
   130   } else if (j->x == cur_x) {
   131     i++;
   132     top_next = !top;
   133     bottom_next = !bottom;
   134     j++;
   135   } else {
   136     top_next = !top;
   137     i++;
   138   }
   140   int topRectsHeight = topRects->y2 - topRects->y1;
   141   int bottomRectsHeight = bottomRects->y2 - bottomRects->y1;
   142   int inbetweenHeight = bottomRects->y1 - topRects->y2;
   143   int width = cur_x;
   144   // top and bottom are the in-status to the left of cur_x
   145   do {
   146     if (top && !bottom) {
   147       totalArea += (inbetweenHeight+bottomRectsHeight)*width;
   148     } else if (bottom && !top) {
   149       totalArea += (inbetweenHeight+topRectsHeight)*width;
   150     } else if (bottom && top) {
   151       totalArea += (inbetweenHeight)*width;
   152     }
   153     top = top_next;
   154     bottom = bottom_next;
   155     // find the next edge
   156     if (i->x < j->x) {
   157       top_next = !top;
   158       width = i->x - cur_x;
   159       cur_x = i->x;
   160       i++;
   161     } else if (j->x < i->x) {
   162       bottom_next = !bottom;
   163       width = j->x - cur_x;
   164       cur_x = j->x;
   165       j++;
   166     } else { // i->x == j->x
   167       top_next = !top;
   168       bottom_next = !bottom;
   169       width = i->x - cur_x;
   170       cur_x = i->x;
   171       i++;
   172       j++;
   173     }
   174   } while (i < end_i && j < end_j);
   176   // handle any remaining rects
   177   while (i < end_i) {
   178     width = i->x - cur_x;
   179     cur_x = i->x;
   180     i++;
   181     if (top)
   182       totalArea += (inbetweenHeight+bottomRectsHeight)*width;
   183     top = !top;
   184   }
   186   while (j < end_j) {
   187     width = j->x - cur_x;
   188     cur_x = j->x;
   189     j++;
   190     if (bottom)
   191       totalArea += (inbetweenHeight+topRectsHeight)*width;
   192     bottom = !bottom;
   193   }
   194   return totalArea;
   195 }
   197 static pixman_box32_t *
   198 CopyRow(pixman_box32_t *dest_it, pixman_box32_t *src_start, pixman_box32_t *src_end)
   199 {
   200     // XXX: std::copy
   201     pixman_box32_t *src_it = src_start;
   202     while (src_it < src_end) {
   203         *dest_it++ = *src_it++;
   204     }
   205     return dest_it;
   206 }
   208 static pixman_box32_t *
   209 MergeRects(pixman_box32_t *topRects, pixman_box32_t *topRectsEnd,
   210            pixman_box32_t *bottomRects, pixman_box32_t *bottomRectsEnd,
   211            pixman_box32_t *tmpRect)
   212 {
   213     struct pt {
   214         int32_t x, y;
   215     };
   217     pixman_box32_t *rect;
   218       // merge the two spans of rects
   219       pt *i = (pt*)topRects;
   220       pt *end_i = (pt*)topRectsEnd;
   221       pt *j = (pt*)bottomRects;
   222       pt *end_j = (pt*)bottomRectsEnd;
   223       bool top;
   224       bool bottom;
   226       int cur_x = i->x;
   227       int32_t y1 = topRects->y1;
   228       int32_t y2 = bottomRects->y2;
   229       if (j->x < cur_x) {
   230         top = false;
   231         bottom = true;
   232         cur_x = j->x;
   233         j++;
   234       } else if (j->x == cur_x) {
   235         top = true;
   236         bottom = true;
   237         i++;
   238         j++;
   239       } else {
   240         top = true;
   241         bottom = false;
   242         i++;
   243       }
   245       rect = tmpRect;
   246       bool started = false;
   247       do {
   248         if (started && !top && !bottom) {
   249           rect->x2 = cur_x;
   250           rect->y2 = y2;
   251           rect++;
   252           started = false;
   253         } else if (!started) {
   254           rect->x1 = cur_x;
   255           rect->y1 = y1;
   256           started = true;
   257         }
   259         if (i >= end_i || j >= end_j)
   260           break;
   262         if (i->x < j->x) {
   263           top = !top;
   264           cur_x = i->x;
   265           i++;
   266         } else if (j->x < i->x) {
   267           bottom = !bottom;
   268           cur_x = j->x;
   269           j++;
   270         } else { // i->x == j->x
   271           top = !top;
   272           bottom = !bottom;
   273           cur_x = i->x;
   274           i++;
   275           j++;
   276         }
   277       } while (true);
   279       // handle any remaining rects
   280       while (i < end_i) {
   281         top = !top;
   282         cur_x = i->x;
   283         i++;
   284         if (!top) {
   285           rect->x2 = cur_x;
   286           rect->y2 = y2;
   287           rect++;
   288         } else {
   289           rect->x1 = cur_x;
   290           rect->y1 = y1;
   291         }
   292       }
   294       while (j < end_j) {
   295         bottom = !bottom;
   296         cur_x = j->x;
   297         j++;
   298         if (!bottom) {
   299           rect->x2 = cur_x;
   300           rect->y2 = y2;
   301           rect++;
   302         } else {
   303           rect->x1 = cur_x;
   304           rect->y1 = y1;
   305         }
   306       }
   307       return rect;
   308 }
   310 void nsRegion::SimplifyOutwardByArea(uint32_t aThreshold)
   311 {
   313   pixman_box32_t *boxes;
   314   int n;
   315   boxes = pixman_region32_rectangles(&mImpl, &n);
   317   // if we have no rectangles then we're done
   318   if (!n)
   319     return;
   321   pixman_box32_t *end = boxes + n;
   322   pixman_box32_t *topRectsEnd = boxes+1;
   323   pixman_box32_t *topRects = boxes;
   325   // we need some temporary storage for merging both rows of rectangles
   326   nsAutoTArray<pixman_box32_t, 10> tmpStorage;
   327   tmpStorage.SetCapacity(n);
   328   pixman_box32_t *tmpRect = tmpStorage.Elements();
   330   pixman_box32_t *destRect = boxes;
   331   pixman_box32_t *rect = tmpRect;
   332   // find the end of the first span of rectangles
   333   while (topRectsEnd < end && topRectsEnd->y1 == topRects->y1) {
   334     topRectsEnd++;
   335   }
   337   // if we only have one row we are done
   338   if (topRectsEnd == end)
   339     return;
   341   pixman_box32_t *bottomRects = topRectsEnd;
   342   pixman_box32_t *bottomRectsEnd = bottomRects+1;
   343   do {
   344     // find the end of the bottom span of rectangles
   345     while (bottomRectsEnd < end && bottomRectsEnd->y1 == bottomRects->y1) {
   346       bottomRectsEnd++;
   347     }
   348     uint32_t totalArea = ComputeMergedAreaIncrease(topRects, topRectsEnd,
   349                                                    bottomRects, bottomRectsEnd);
   351     if (totalArea <= aThreshold) {
   352       // merge the rects into tmpRect
   353       rect = MergeRects(topRects, topRectsEnd, bottomRects, bottomRectsEnd, tmpRect);
   355       // copy the merged rects back into the destination
   356       topRectsEnd = CopyRow(destRect, tmpRect, rect);
   357       topRects = destRect;
   358       bottomRects = bottomRectsEnd;
   359       destRect = topRects;
   360     } else {
   361       // copy the unmerged rects
   362       destRect = CopyRow(destRect, topRects, topRectsEnd);
   364       topRects = bottomRects;
   365       topRectsEnd = bottomRectsEnd;
   366       bottomRects = bottomRectsEnd;
   367       if (bottomRectsEnd == end) {
   368         // copy the last row when we are done
   369         topRectsEnd = CopyRow(destRect, topRects, topRectsEnd);
   370       }
   371     }
   372   } while (bottomRectsEnd != end);
   375   uint32_t reducedCount = topRectsEnd - pixman_region32_rectangles(&this->mImpl, &n);
   376   // pixman has a special representation for
   377   // regions of 1 rectangle. So just use the
   378   // bounds in that case
   379   if (reducedCount > 1) {
   380     // reach into pixman and lower the number
   381     // of rects stored in data.
   382     this->mImpl.data->numRects = reducedCount;
   383   } else {
   384     *this = GetBounds();
   385   }
   386 }
   389 void nsRegion::SimplifyInward (uint32_t aMaxRects)
   390 {
   391   NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
   393   if (GetNumRects() <= aMaxRects)
   394     return;
   396   SetEmpty();
   397 }
   399 uint64_t nsRegion::Area () const
   400 {
   401   uint64_t area = 0;
   402   nsRegionRectIterator iter(*this);
   403   const nsRect* r;
   404   while ((r = iter.Next()) != nullptr) {
   405     area += uint64_t(r->width)*r->height;
   406   }
   407   return area;
   408 }
   410 nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale)
   411 {
   412   int n;
   413   pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
   414   for (int i=0; i<n; i++) {
   415     nsRect rect = BoxToRect(boxes[i]);
   416     rect.ScaleRoundOut(aXScale, aYScale);
   417     boxes[i] = RectToBox(rect);
   418   }
   420   pixman_region32_t region;
   421   // This will union all of the rectangles and runs in about O(n lg(n))
   422   pixman_region32_init_rects(&region, boxes, n);
   424   pixman_region32_fini(&mImpl);
   425   mImpl = region;
   426   return *this;
   427 }
   429 nsRegion& nsRegion::ScaleInverseRoundOut (float aXScale, float aYScale)
   430 {
   431   int n;
   432   pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
   433   for (int i=0; i<n; i++) {
   434     nsRect rect = BoxToRect(boxes[i]);
   435     rect.ScaleInverseRoundOut(aXScale, aYScale);
   436     boxes[i] = RectToBox(rect);
   437   }
   439   pixman_region32_t region;
   440   // This will union all of the rectangles and runs in about O(n lg(n))
   441   pixman_region32_init_rects(&region, boxes, n);
   443   pixman_region32_fini(&mImpl);
   444   mImpl = region;
   445   return *this;
   446 }
   448 nsRegion nsRegion::ConvertAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const
   449 {
   450   if (aFromAPP == aToAPP) {
   451     return *this;
   452   }
   454   nsRegion region = *this;
   455   int n;
   456   pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
   457   for (int i=0; i<n; i++) {
   458     nsRect rect = BoxToRect(boxes[i]);
   459     rect = rect.ConvertAppUnitsRoundOut(aFromAPP, aToAPP);
   460     boxes[i] = RectToBox(rect);
   461   }
   463   pixman_region32_t pixmanRegion;
   464   // This will union all of the rectangles and runs in about O(n lg(n))
   465   pixman_region32_init_rects(&pixmanRegion, boxes, n);
   467   pixman_region32_fini(&region.mImpl);
   468   region.mImpl = pixmanRegion;
   469   return region;
   470 }
   472 nsRegion nsRegion::ConvertAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const
   473 {
   474   if (aFromAPP == aToAPP) {
   475     return *this;
   476   }
   478   nsRegion region = *this;
   479   int n;
   480   pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
   481   for (int i=0; i<n; i++) {
   482     nsRect rect = BoxToRect(boxes[i]);
   483     rect = rect.ConvertAppUnitsRoundIn(aFromAPP, aToAPP);
   484     boxes[i] = RectToBox(rect);
   485   }
   487   pixman_region32_t pixmanRegion;
   488   // This will union all of the rectangles and runs in about O(n lg(n))
   489   pixman_region32_init_rects(&pixmanRegion, boxes, n);
   491   pixman_region32_fini(&region.mImpl);
   492   region.mImpl = pixmanRegion;
   493   return region;
   494 }
   496 nsIntRegion nsRegion::ToPixels (nscoord aAppUnitsPerPixel, bool aOutsidePixels) const
   497 {
   498   nsRegion region = *this;
   499   int n;
   500   pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
   501   for (int i=0; i<n; i++) {
   502     nsRect rect = BoxToRect(boxes[i]);
   503     nsIntRect deviceRect;
   504     if (aOutsidePixels)
   505       deviceRect = rect.ToOutsidePixels(aAppUnitsPerPixel);
   506     else
   507       deviceRect = rect.ToNearestPixels(aAppUnitsPerPixel);
   509     boxes[i] = RectToBox(deviceRect);
   510   }
   512   nsIntRegion intRegion;
   513   pixman_region32_fini(&intRegion.mImpl.mImpl);
   514   // This will union all of the rectangles and runs in about O(n lg(n))
   515   pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
   517   return intRegion;
   518 }
   520 nsIntRegion nsRegion::ToOutsidePixels (nscoord aAppUnitsPerPixel) const
   521 {
   522   return ToPixels(aAppUnitsPerPixel, true);
   523 }
   525 nsIntRegion nsRegion::ToNearestPixels (nscoord aAppUnitsPerPixel) const
   526 {
   527   return ToPixels(aAppUnitsPerPixel, false);
   528 }
   530 nsIntRegion nsRegion::ScaleToNearestPixels (float aScaleX, float aScaleY,
   531                                             nscoord aAppUnitsPerPixel) const
   532 {
   533   nsIntRegion result;
   534   nsRegionRectIterator rgnIter(*this);
   535   const nsRect* currentRect;
   536   while ((currentRect = rgnIter.Next())) {
   537     nsIntRect deviceRect =
   538       currentRect->ScaleToNearestPixels(aScaleX, aScaleY, aAppUnitsPerPixel);
   539     result.Or(result, deviceRect);
   540   }
   541   return result;
   542 }
   544 nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
   545                                             nscoord aAppUnitsPerPixel) const
   546 {
   547   nsIntRegion result;
   548   nsRegionRectIterator rgnIter(*this);
   549   const nsRect* currentRect;
   550   while ((currentRect = rgnIter.Next())) {
   551     nsIntRect deviceRect =
   552       currentRect->ScaleToOutsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
   553     result.Or(result, deviceRect);
   554   }
   555   return result;
   556 }
   558 nsIntRegion nsRegion::ScaleToInsidePixels (float aScaleX, float aScaleY,
   559                                            nscoord aAppUnitsPerPixel) const
   560 {
   561   /* When scaling a rect, walk forward through the rect list up until the y value is greater
   562    * than the current rect's YMost() value.
   563    *
   564    * For each rect found, check if the rects have a touching edge (in unscaled coordinates),
   565    * and if one edge is entirely contained within the other.
   566    *
   567    * If it is, then the contained edge can be moved (in scaled pixels) to ensure that no
   568    * gap exists.
   569    *
   570    * Since this could be potentially expensive - O(n^2), we only attempt this algorithm
   571    * for the first rect.
   572    */
   574   // make a copy of this region so that we can mutate it in place
   575   nsRegion region = *this;
   576   int n;
   577   pixman_box32_t *boxes = pixman_region32_rectangles(&region.mImpl, &n);
   579   nsIntRegion intRegion;
   580   if (n) {
   581     nsRect first = BoxToRect(boxes[0]);
   582     nsIntRect firstDeviceRect =
   583       first.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
   585     for (int i=1; i<n; i++) {
   586       nsRect rect = nsRect(boxes[i].x1, boxes[i].y1,
   587 	  boxes[i].x2 - boxes[i].x1,
   588 	  boxes[i].y2 - boxes[i].y1);
   589       nsIntRect deviceRect =
   590 	rect.ScaleToInsidePixels(aScaleX, aScaleY, aAppUnitsPerPixel);
   592       if (rect.y <= first.YMost()) {
   593 	if (rect.XMost() == first.x && rect.YMost() <= first.YMost()) {
   594 	  // rect is touching on the left edge of the first rect and contained within
   595 	  // the length of its left edge
   596 	  deviceRect.SetRightEdge(firstDeviceRect.x);
   597 	} else if (rect.x == first.XMost() && rect.YMost() <= first.YMost()) {
   598 	  // rect is touching on the right edge of the first rect and contained within
   599 	  // the length of its right edge
   600 	  deviceRect.SetLeftEdge(firstDeviceRect.XMost());
   601 	} else if (rect.y == first.YMost()) {
   602 	  // The bottom of the first rect is on the same line as the top of rect, but
   603 	  // they aren't necessarily contained.
   604 	  if (rect.x <= first.x && rect.XMost() >= first.XMost()) {
   605 	    // The top of rect contains the bottom of the first rect
   606 	    firstDeviceRect.SetBottomEdge(deviceRect.y);
   607 	  } else if (rect.x >= first.x && rect.XMost() <= first.XMost()) {
   608 	    // The bottom of the first contains the top of rect
   609 	    deviceRect.SetTopEdge(firstDeviceRect.YMost());
   610 	  }
   611 	}
   612       }
   614       boxes[i] = RectToBox(deviceRect);
   615     }
   617     boxes[0] = RectToBox(firstDeviceRect);
   619     pixman_region32_fini(&intRegion.mImpl.mImpl);
   620     // This will union all of the rectangles and runs in about O(n lg(n))
   621     pixman_region32_init_rects(&intRegion.mImpl.mImpl, boxes, n);
   622   }
   623   return intRegion;
   625 }
   627 // A cell's "value" is a pair consisting of
   628 // a) the area of the subrectangle it corresponds to, if it's in
   629 // aContainingRect and in the region, 0 otherwise
   630 // b) the area of the subrectangle it corresponds to, if it's in the region,
   631 // 0 otherwise
   632 // Addition, subtraction and identity are defined on these values in the
   633 // obvious way. Partial order is lexicographic.
   634 // A "large negative value" is defined with large negative numbers for both
   635 // fields of the pair. This negative value has the property that adding any
   636 // number of non-negative values to it always results in a negative value.
   637 //
   638 // The GetLargestRectangle algorithm works in three phases:
   639 //  1) Convert the region into a grid by adding vertical/horizontal lines for
   640 //     each edge of each rectangle in the region.
   641 //  2) For each rectangle in the region, for each cell it contains, set that
   642 //     cells's value as described above.
   643 //  3) Calculate the submatrix with the largest sum such that none of its cells
   644 //     contain any 0s (empty regions). The rectangle represented by the
   645 //     submatrix is the largest rectangle in the region.
   646 //
   647 // Let k be the number of rectangles in the region.
   648 // Let m be the height of the grid generated in step 1.
   649 // Let n be the width of the grid generated in step 1.
   650 //
   651 // Step 1 is O(k) in time and O(m+n) in space for the sparse grid.
   652 // Step 2 is O(mn) in time and O(mn) in additional space for the full grid.
   653 // Step 3 is O(m^2 n) in time and O(mn) in additional space
   654 //
   655 // The implementation of steps 1 and 2 are rather straightforward. However our
   656 // implementation of step 3 uses dynamic programming to achieve its efficiency.
   657 //
   658 // Psuedo code for step 3 is as follows where G is the grid from step 1 and A
   659 // is the array from step 2:
   660 // Phase3 = function (G, A, m, n) {
   661 //   let (t,b,l,r,_) = MaxSum2D(A,m,n)
   662 //   return rect(G[t],G[l],G[r],G[b]);
   663 // }
   664 // MaxSum2D = function (A, m, n) {
   665 //   S = array(m+1,n+1)
   666 //   S[0][i] = 0 for i in [0,n]
   667 //   S[j][0] = 0 for j in [0,m]
   668 //   S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
   669 //           + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
   670 //
   671 //   // top, bottom, left, right, area
   672 //   var maxRect = (-1, -1, -1, -1, 0);
   673 //
   674 //   for all (m',m'') in [0, m]^2 {
   675 //     let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
   676 //     let ((l,r),area) = MaxSum1D(B,n+1)
   677 //     if (area > maxRect.area) {
   678 //       maxRect := (m', m'', l, r, area)
   679 //     }
   680 //   }
   681 //
   682 //   return maxRect;
   683 // }
   684 //
   685 // Originally taken from Improved algorithms for the k-maximum subarray problem
   686 // for small k - SE Bae, T Takaoka but modified to show the explicit tracking
   687 // of indices and we already have the prefix sums from our one call site so
   688 // there's no need to construct them.
   689 // MaxSum1D = function (A,n) {
   690 //   var minIdx = 0;
   691 //   var min = 0;
   692 //   var maxIndices = (0,0);
   693 //   var max = 0;
   694 //   for i in range(n) {
   695 //     let cand = A[i] - min;
   696 //     if (cand > max) {
   697 //       max := cand;
   698 //       maxIndices := (minIdx, i)
   699 //     }
   700 //     if (min > A[i]) {
   701 //       min := A[i];
   702 //       minIdx := i;
   703 //     }
   704 //   }
   705 //   return (minIdx, maxIdx, max);
   706 // }
   708 namespace {
   709   // This class represents a partitioning of an axis delineated by coordinates.
   710   // It internally maintains a sorted array of coordinates.
   711   class AxisPartition {
   712   public:
   713     // Adds a new partition at the given coordinate to this partitioning. If
   714     // the coordinate is already present in the partitioning, this does nothing.
   715     void InsertCoord(nscoord c) {
   716       uint32_t i = mStops.IndexOfFirstElementGt(c);
   717       if (i == 0 || mStops[i-1] != c) {
   718         mStops.InsertElementAt(i, c);
   719       }
   720     }
   722     // Returns the array index of the given partition point. The partition
   723     // point must already be present in the partitioning.
   724     int32_t IndexOf(nscoord p) const {
   725       return mStops.BinaryIndexOf(p);
   726     }
   728     // Returns the partition at the given index which must be non-zero and
   729     // less than the number of partitions in this partitioning.
   730     nscoord StopAt(int32_t index) const {
   731       return mStops[index];
   732     }
   734     // Returns the size of the gap between the partition at the given index and
   735     // the next partition in this partitioning. If the index is the last index
   736     // in the partitioning, the result is undefined.
   737     nscoord StopSize(int32_t index) const {
   738       return mStops[index+1] - mStops[index];
   739     }
   741     // Returns the number of partitions in this partitioning.
   742     int32_t GetNumStops() const { return mStops.Length(); }
   744   private:
   745     nsTArray<nscoord> mStops;
   746   };
   748   const int64_t kVeryLargeNegativeNumber = 0xffff000000000000ll;
   750   struct SizePair {
   751     int64_t mSizeContainingRect;
   752     int64_t mSize;
   754     SizePair() : mSizeContainingRect(0), mSize(0) {}
   756     static SizePair VeryLargeNegative() {
   757       SizePair result;
   758       result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
   759       return result;
   760     }
   761     SizePair& operator=(const SizePair& aOther) {
   762       mSizeContainingRect = aOther.mSizeContainingRect;
   763       mSize = aOther.mSize;
   764       return *this;
   765     }
   766     bool operator<(const SizePair& aOther) const {
   767       if (mSizeContainingRect < aOther.mSizeContainingRect)
   768         return true;
   769       if (mSizeContainingRect > aOther.mSizeContainingRect)
   770         return false;
   771       return mSize < aOther.mSize;
   772     }
   773     bool operator>(const SizePair& aOther) const {
   774       return aOther.operator<(*this);
   775     }
   776     SizePair operator+(const SizePair& aOther) const {
   777       SizePair result = *this;
   778       result.mSizeContainingRect += aOther.mSizeContainingRect;
   779       result.mSize += aOther.mSize;
   780       return result;
   781     }
   782     SizePair operator-(const SizePair& aOther) const {
   783       SizePair result = *this;
   784       result.mSizeContainingRect -= aOther.mSizeContainingRect;
   785       result.mSize -= aOther.mSize;
   786       return result;
   787     }
   788   };
   790   // Returns the sum and indices of the subarray with the maximum sum of the
   791   // given array (A,n), assuming the array is already in prefix sum form.
   792   SizePair MaxSum1D(const nsTArray<SizePair> &A, int32_t n,
   793                     int32_t *minIdx, int32_t *maxIdx) {
   794     // The min/max indicies of the largest subarray found so far
   795     SizePair min, max;
   796     int32_t currentMinIdx = 0;
   798     *minIdx = 0;
   799     *maxIdx = 0;
   801     // Because we're given the array in prefix sum form, we know the first
   802     // element is 0
   803     for(int32_t i = 1; i < n; i++) {
   804       SizePair cand = A[i] - min;
   805       if (cand > max) {
   806         max = cand;
   807         *minIdx = currentMinIdx;
   808         *maxIdx = i;
   809       }
   810       if (min > A[i]) {
   811         min = A[i];
   812         currentMinIdx = i;
   813       }
   814     }
   816     return max;
   817   }
   818 }
   820 nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
   821   nsRect bestRect;
   823   if (GetNumRects() <= 1) {
   824     bestRect = GetBounds();
   825     return bestRect;
   826   }
   828   AxisPartition xaxis, yaxis;
   830   // Step 1: Calculate the grid lines
   831   nsRegionRectIterator iter(*this);
   832   const nsRect *currentRect;
   833   while ((currentRect = iter.Next())) {
   834     xaxis.InsertCoord(currentRect->x);
   835     xaxis.InsertCoord(currentRect->XMost());
   836     yaxis.InsertCoord(currentRect->y);
   837     yaxis.InsertCoord(currentRect->YMost());
   838   }
   839   if (!aContainingRect.IsEmpty()) {
   840     xaxis.InsertCoord(aContainingRect.x);
   841     xaxis.InsertCoord(aContainingRect.XMost());
   842     yaxis.InsertCoord(aContainingRect.y);
   843     yaxis.InsertCoord(aContainingRect.YMost());
   844   }
   846   // Step 2: Fill out the grid with the areas
   847   // Note: due to the ordering of rectangles in the region, it is not always
   848   // possible to combine steps 2 and 3 so we don't try to be clever.
   849   int32_t matrixHeight = yaxis.GetNumStops() - 1;
   850   int32_t matrixWidth = xaxis.GetNumStops() - 1;
   851   int32_t matrixSize = matrixHeight * matrixWidth;
   852   nsTArray<SizePair> areas(matrixSize);
   853   areas.SetLength(matrixSize);
   855   iter.Reset();
   856   while ((currentRect = iter.Next())) {
   857     int32_t xstart = xaxis.IndexOf(currentRect->x);
   858     int32_t xend = xaxis.IndexOf(currentRect->XMost());
   859     int32_t y = yaxis.IndexOf(currentRect->y);
   860     int32_t yend = yaxis.IndexOf(currentRect->YMost());
   862     for (; y < yend; y++) {
   863       nscoord height = yaxis.StopSize(y);
   864       for (int32_t x = xstart; x < xend; x++) {
   865         nscoord width = xaxis.StopSize(x);
   866         int64_t size = width*int64_t(height);
   867         if (currentRect->Intersects(aContainingRect)) {
   868           areas[y*matrixWidth+x].mSizeContainingRect = size;
   869         }
   870         areas[y*matrixWidth+x].mSize = size;
   871       }
   872     }
   873   }
   875   // Step 3: Find the maximum submatrix sum that does not contain a rectangle
   876   {
   877     // First get the prefix sum array
   878     int32_t m = matrixHeight + 1;
   879     int32_t n = matrixWidth + 1;
   880     nsTArray<SizePair> pareas(m*n);
   881     pareas.SetLength(m*n);
   882     for (int32_t y = 1; y < m; y++) {
   883       for (int32_t x = 1; x < n; x++) {
   884         SizePair area = areas[(y-1)*matrixWidth+x-1];
   885         if (!area.mSize) {
   886           area = SizePair::VeryLargeNegative();
   887         }
   888         area = area + pareas[    y*n+x-1]
   889                     + pareas[(y-1)*n+x  ]
   890                     - pareas[(y-1)*n+x-1];
   891         pareas[y*n+x] = area;
   892       }
   893     }
   895     // No longer need the grid
   896     areas.SetLength(0);
   898     SizePair bestArea;
   899     struct {
   900       int32_t left, top, right, bottom;
   901     } bestRectIndices = { 0, 0, 0, 0 };
   902     for (int32_t m1 = 0; m1 < m; m1++) {
   903       for (int32_t m2 = m1+1; m2 < m; m2++) {
   904         nsTArray<SizePair> B;
   905         B.SetLength(n);
   906         for (int32_t i = 0; i < n; i++) {
   907           B[i] = pareas[m2*n+i] - pareas[m1*n+i];
   908         }
   909         int32_t minIdx, maxIdx;
   910         SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
   911         if (area > bestArea) {
   912           bestRectIndices.left = minIdx;
   913           bestRectIndices.top = m1;
   914           bestRectIndices.right = maxIdx;
   915           bestRectIndices.bottom = m2;
   916           bestArea = area;
   917         }
   918       }
   919     }
   921     bestRect.MoveTo(xaxis.StopAt(bestRectIndices.left),
   922                     yaxis.StopAt(bestRectIndices.top));
   923     bestRect.SizeTo(xaxis.StopAt(bestRectIndices.right) - bestRect.x,
   924                     yaxis.StopAt(bestRectIndices.bottom) - bestRect.y);
   925   }
   927   return bestRect;
   928 }
   930 nsCString
   931 nsRegion::ToString() const {
   932     nsCString result;
   933     result.AppendLiteral("[");
   935     int n;
   936     pixman_box32_t *boxes = pixman_region32_rectangles(const_cast<pixman_region32_t*>(&mImpl), &n);
   937     for (int i=0; i<n; i++) {
   938         if (i != 0) {
   939             result.AppendLiteral("; ");
   940         }
   941         result.Append(nsPrintfCString("%d,%d,%d,%d", boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2));
   943     }
   944     result.AppendLiteral("]");
   946     return result;
   947 }
   950 nsRegion nsIntRegion::ToAppUnits (nscoord aAppUnitsPerPixel) const
   951 {
   952   nsRegion result;
   953   nsIntRegionRectIterator rgnIter(*this);
   954   const nsIntRect* currentRect;
   955   while ((currentRect = rgnIter.Next())) {
   956     nsRect appRect = currentRect->ToAppUnits(aAppUnitsPerPixel);
   957     result.Or(result, appRect);
   958   }
   959   return result;
   960 }

mercurial