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.

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

mercurial