Tue, 06 Jan 2015 21:39:09 +0100
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(®ion, 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(®ion, 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(®ion, 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(®ion.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(®ion.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(®ion.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(®ion.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(®ion.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(®ion.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 }