gfx/skia/trunk/src/core/SkRegion.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "SkRegionPriv.h"
michael@0 11 #include "SkTemplates.h"
michael@0 12 #include "SkThread.h"
michael@0 13 #include "SkUtils.h"
michael@0 14
michael@0 15 /* Region Layout
michael@0 16 *
michael@0 17 * TOP
michael@0 18 *
michael@0 19 * [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
michael@0 20 * ...
michael@0 21 *
michael@0 22 * Y-Sentinel
michael@0 23 */
michael@0 24
michael@0 25 SkDEBUGCODE(int32_t gRgnAllocCounter;)
michael@0 26
michael@0 27 /////////////////////////////////////////////////////////////////////////////////////////////////
michael@0 28
michael@0 29 /* Pass in the beginning with the intervals.
michael@0 30 * We back up 1 to read the interval-count.
michael@0 31 * Return the beginning of the next scanline (i.e. the next Y-value)
michael@0 32 */
michael@0 33 static SkRegion::RunType* skip_intervals(const SkRegion::RunType runs[]) {
michael@0 34 int intervals = runs[-1];
michael@0 35 #ifdef SK_DEBUG
michael@0 36 if (intervals > 0) {
michael@0 37 SkASSERT(runs[0] < runs[1]);
michael@0 38 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
michael@0 39 } else {
michael@0 40 SkASSERT(0 == intervals);
michael@0 41 SkASSERT(SkRegion::kRunTypeSentinel == runs[0]);
michael@0 42 }
michael@0 43 #endif
michael@0 44 runs += intervals * 2 + 1;
michael@0 45 return const_cast<SkRegion::RunType*>(runs);
michael@0 46 }
michael@0 47
michael@0 48 bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
michael@0 49 SkIRect* bounds) {
michael@0 50 assert_sentinel(runs[0], false); // top
michael@0 51 SkASSERT(count >= kRectRegionRuns);
michael@0 52
michael@0 53 if (count == kRectRegionRuns) {
michael@0 54 assert_sentinel(runs[1], false); // bottom
michael@0 55 SkASSERT(1 == runs[2]);
michael@0 56 assert_sentinel(runs[3], false); // left
michael@0 57 assert_sentinel(runs[4], false); // right
michael@0 58 assert_sentinel(runs[5], true);
michael@0 59 assert_sentinel(runs[6], true);
michael@0 60
michael@0 61 SkASSERT(runs[0] < runs[1]); // valid height
michael@0 62 SkASSERT(runs[3] < runs[4]); // valid width
michael@0 63
michael@0 64 bounds->set(runs[3], runs[0], runs[4], runs[1]);
michael@0 65 return true;
michael@0 66 }
michael@0 67 return false;
michael@0 68 }
michael@0 69
michael@0 70 //////////////////////////////////////////////////////////////////////////
michael@0 71
michael@0 72 SkRegion::SkRegion() {
michael@0 73 fBounds.set(0, 0, 0, 0);
michael@0 74 fRunHead = SkRegion_gEmptyRunHeadPtr;
michael@0 75 }
michael@0 76
michael@0 77 SkRegion::SkRegion(const SkRegion& src) {
michael@0 78 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
michael@0 79 this->setRegion(src);
michael@0 80 }
michael@0 81
michael@0 82 SkRegion::SkRegion(const SkIRect& rect) {
michael@0 83 fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
michael@0 84 this->setRect(rect);
michael@0 85 }
michael@0 86
michael@0 87 SkRegion::~SkRegion() {
michael@0 88 this->freeRuns();
michael@0 89 }
michael@0 90
michael@0 91 void SkRegion::freeRuns() {
michael@0 92 if (this->isComplex()) {
michael@0 93 SkASSERT(fRunHead->fRefCnt >= 1);
michael@0 94 if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
michael@0 95 //SkASSERT(gRgnAllocCounter > 0);
michael@0 96 //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
michael@0 97 //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
michael@0 98 sk_free(fRunHead);
michael@0 99 }
michael@0 100 }
michael@0 101 }
michael@0 102
michael@0 103 void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
michael@0 104 fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
michael@0 105 }
michael@0 106
michael@0 107 void SkRegion::allocateRuns(int count) {
michael@0 108 fRunHead = RunHead::Alloc(count);
michael@0 109 }
michael@0 110
michael@0 111 void SkRegion::allocateRuns(const RunHead& head) {
michael@0 112 fRunHead = RunHead::Alloc(head.fRunCount,
michael@0 113 head.getYSpanCount(),
michael@0 114 head.getIntervalCount());
michael@0 115 }
michael@0 116
michael@0 117 SkRegion& SkRegion::operator=(const SkRegion& src) {
michael@0 118 (void)this->setRegion(src);
michael@0 119 return *this;
michael@0 120 }
michael@0 121
michael@0 122 void SkRegion::swap(SkRegion& other) {
michael@0 123 SkTSwap<SkIRect>(fBounds, other.fBounds);
michael@0 124 SkTSwap<RunHead*>(fRunHead, other.fRunHead);
michael@0 125 }
michael@0 126
michael@0 127 int SkRegion::computeRegionComplexity() const {
michael@0 128 if (this->isEmpty()) {
michael@0 129 return 0;
michael@0 130 } else if (this->isRect()) {
michael@0 131 return 1;
michael@0 132 }
michael@0 133 return fRunHead->getIntervalCount();
michael@0 134 }
michael@0 135
michael@0 136 bool SkRegion::setEmpty() {
michael@0 137 this->freeRuns();
michael@0 138 fBounds.set(0, 0, 0, 0);
michael@0 139 fRunHead = SkRegion_gEmptyRunHeadPtr;
michael@0 140 return false;
michael@0 141 }
michael@0 142
michael@0 143 bool SkRegion::setRect(int32_t left, int32_t top,
michael@0 144 int32_t right, int32_t bottom) {
michael@0 145 if (left >= right || top >= bottom) {
michael@0 146 return this->setEmpty();
michael@0 147 }
michael@0 148 this->freeRuns();
michael@0 149 fBounds.set(left, top, right, bottom);
michael@0 150 fRunHead = SkRegion_gRectRunHeadPtr;
michael@0 151 return true;
michael@0 152 }
michael@0 153
michael@0 154 bool SkRegion::setRect(const SkIRect& r) {
michael@0 155 return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
michael@0 156 }
michael@0 157
michael@0 158 bool SkRegion::setRegion(const SkRegion& src) {
michael@0 159 if (this != &src) {
michael@0 160 this->freeRuns();
michael@0 161
michael@0 162 fBounds = src.fBounds;
michael@0 163 fRunHead = src.fRunHead;
michael@0 164 if (this->isComplex()) {
michael@0 165 sk_atomic_inc(&fRunHead->fRefCnt);
michael@0 166 }
michael@0 167 }
michael@0 168 return fRunHead != SkRegion_gEmptyRunHeadPtr;
michael@0 169 }
michael@0 170
michael@0 171 bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op) {
michael@0 172 SkRegion tmp(rect);
michael@0 173
michael@0 174 return this->op(tmp, rgn, op);
michael@0 175 }
michael@0 176
michael@0 177 bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op) {
michael@0 178 SkRegion tmp(rect);
michael@0 179
michael@0 180 return this->op(rgn, tmp, op);
michael@0 181 }
michael@0 182
michael@0 183 ///////////////////////////////////////////////////////////////////////////////
michael@0 184
michael@0 185 #ifdef SK_BUILD_FOR_ANDROID
michael@0 186 #include <stdio.h>
michael@0 187 char* SkRegion::toString() {
michael@0 188 Iterator iter(*this);
michael@0 189 int count = 0;
michael@0 190 while (!iter.done()) {
michael@0 191 count++;
michael@0 192 iter.next();
michael@0 193 }
michael@0 194 // 4 ints, up to 10 digits each plus sign, 3 commas, '(', ')', SkRegion() and '\0'
michael@0 195 const int max = (count*((11*4)+5))+11+1;
michael@0 196 char* result = (char*)malloc(max);
michael@0 197 if (result == NULL) {
michael@0 198 return NULL;
michael@0 199 }
michael@0 200 count = sprintf(result, "SkRegion(");
michael@0 201 iter.reset(*this);
michael@0 202 while (!iter.done()) {
michael@0 203 const SkIRect& r = iter.rect();
michael@0 204 count += sprintf(result+count, "(%d,%d,%d,%d)", r.fLeft, r.fTop, r.fRight, r.fBottom);
michael@0 205 iter.next();
michael@0 206 }
michael@0 207 count += sprintf(result+count, ")");
michael@0 208 return result;
michael@0 209 }
michael@0 210 #endif
michael@0 211
michael@0 212 ///////////////////////////////////////////////////////////////////////////////
michael@0 213
michael@0 214 int SkRegion::count_runtype_values(int* itop, int* ibot) const {
michael@0 215 if (this == NULL) {
michael@0 216 *itop = SK_MinS32;
michael@0 217 *ibot = SK_MaxS32;
michael@0 218 return 0;
michael@0 219 }
michael@0 220
michael@0 221 int maxT;
michael@0 222
michael@0 223 if (this->isRect()) {
michael@0 224 maxT = 2;
michael@0 225 } else {
michael@0 226 SkASSERT(this->isComplex());
michael@0 227 maxT = fRunHead->getIntervalCount() * 2;
michael@0 228 }
michael@0 229 *itop = fBounds.fTop;
michael@0 230 *ibot = fBounds.fBottom;
michael@0 231 return maxT;
michael@0 232 }
michael@0 233
michael@0 234 static bool isRunCountEmpty(int count) {
michael@0 235 return count <= 2;
michael@0 236 }
michael@0 237
michael@0 238 bool SkRegion::setRuns(RunType runs[], int count) {
michael@0 239 SkDEBUGCODE(this->validate();)
michael@0 240 SkASSERT(count > 0);
michael@0 241
michael@0 242 if (isRunCountEmpty(count)) {
michael@0 243 // SkDEBUGF(("setRuns: empty\n"));
michael@0 244 assert_sentinel(runs[count-1], true);
michael@0 245 return this->setEmpty();
michael@0 246 }
michael@0 247
michael@0 248 // trim off any empty spans from the top and bottom
michael@0 249 // weird I should need this, perhaps op() could be smarter...
michael@0 250 if (count > kRectRegionRuns) {
michael@0 251 RunType* stop = runs + count;
michael@0 252 assert_sentinel(runs[0], false); // top
michael@0 253 assert_sentinel(runs[1], false); // bottom
michael@0 254 // runs[2] is uncomputed intervalCount
michael@0 255
michael@0 256 if (runs[3] == SkRegion::kRunTypeSentinel) { // should be first left...
michael@0 257 runs += 3; // skip empty initial span
michael@0 258 runs[0] = runs[-2]; // set new top to prev bottom
michael@0 259 assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
michael@0 260 assert_sentinel(runs[2], false); // intervalcount
michael@0 261 assert_sentinel(runs[3], false); // left
michael@0 262 assert_sentinel(runs[4], false); // right
michael@0 263 }
michael@0 264
michael@0 265 assert_sentinel(stop[-1], true);
michael@0 266 assert_sentinel(stop[-2], true);
michael@0 267
michael@0 268 // now check for a trailing empty span
michael@0 269 if (stop[-5] == SkRegion::kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
michael@0 270 stop[-4] = SkRegion::kRunTypeSentinel; // kill empty last span
michael@0 271 stop -= 3;
michael@0 272 assert_sentinel(stop[-1], true); // last y-sentinel
michael@0 273 assert_sentinel(stop[-2], true); // last x-sentinel
michael@0 274 assert_sentinel(stop[-3], false); // last right
michael@0 275 assert_sentinel(stop[-4], false); // last left
michael@0 276 assert_sentinel(stop[-5], false); // last interval-count
michael@0 277 assert_sentinel(stop[-6], false); // last bottom
michael@0 278 }
michael@0 279 count = (int)(stop - runs);
michael@0 280 }
michael@0 281
michael@0 282 SkASSERT(count >= kRectRegionRuns);
michael@0 283
michael@0 284 if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
michael@0 285 return this->setRect(fBounds);
michael@0 286 }
michael@0 287
michael@0 288 // if we get here, we need to become a complex region
michael@0 289
michael@0 290 if (!this->isComplex() || fRunHead->fRunCount != count) {
michael@0 291 this->freeRuns();
michael@0 292 this->allocateRuns(count);
michael@0 293 }
michael@0 294
michael@0 295 // must call this before we can write directly into runs()
michael@0 296 // in case we are sharing the buffer with another region (copy on write)
michael@0 297 fRunHead = fRunHead->ensureWritable();
michael@0 298 memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
michael@0 299 fRunHead->computeRunBounds(&fBounds);
michael@0 300
michael@0 301 SkDEBUGCODE(this->validate();)
michael@0 302
michael@0 303 return true;
michael@0 304 }
michael@0 305
michael@0 306 void SkRegion::BuildRectRuns(const SkIRect& bounds,
michael@0 307 RunType runs[kRectRegionRuns]) {
michael@0 308 runs[0] = bounds.fTop;
michael@0 309 runs[1] = bounds.fBottom;
michael@0 310 runs[2] = 1; // 1 interval for this scanline
michael@0 311 runs[3] = bounds.fLeft;
michael@0 312 runs[4] = bounds.fRight;
michael@0 313 runs[5] = kRunTypeSentinel;
michael@0 314 runs[6] = kRunTypeSentinel;
michael@0 315 }
michael@0 316
michael@0 317 bool SkRegion::contains(int32_t x, int32_t y) const {
michael@0 318 SkDEBUGCODE(this->validate();)
michael@0 319
michael@0 320 if (!fBounds.contains(x, y)) {
michael@0 321 return false;
michael@0 322 }
michael@0 323 if (this->isRect()) {
michael@0 324 return true;
michael@0 325 }
michael@0 326 SkASSERT(this->isComplex());
michael@0 327
michael@0 328 const RunType* runs = fRunHead->findScanline(y);
michael@0 329
michael@0 330 // Skip the Bottom and IntervalCount
michael@0 331 runs += 2;
michael@0 332
michael@0 333 // Just walk this scanline, checking each interval. The X-sentinel will
michael@0 334 // appear as a left-inteval (runs[0]) and should abort the search.
michael@0 335 //
michael@0 336 // We could do a bsearch, using interval-count (runs[1]), but need to time
michael@0 337 // when that would be worthwhile.
michael@0 338 //
michael@0 339 for (;;) {
michael@0 340 if (x < runs[0]) {
michael@0 341 break;
michael@0 342 }
michael@0 343 if (x < runs[1]) {
michael@0 344 return true;
michael@0 345 }
michael@0 346 runs += 2;
michael@0 347 }
michael@0 348 return false;
michael@0 349 }
michael@0 350
michael@0 351 static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
michael@0 352 return runs[0];
michael@0 353 }
michael@0 354
michael@0 355 static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
michael@0 356 // skip [B N [L R]... S]
michael@0 357 return runs + 2 + runs[1] * 2 + 1;
michael@0 358 }
michael@0 359
michael@0 360 static bool scanline_contains(const SkRegion::RunType runs[],
michael@0 361 SkRegion::RunType L, SkRegion::RunType R) {
michael@0 362 runs += 2; // skip Bottom and IntervalCount
michael@0 363 for (;;) {
michael@0 364 if (L < runs[0]) {
michael@0 365 break;
michael@0 366 }
michael@0 367 if (R <= runs[1]) {
michael@0 368 return true;
michael@0 369 }
michael@0 370 runs += 2;
michael@0 371 }
michael@0 372 return false;
michael@0 373 }
michael@0 374
michael@0 375 bool SkRegion::contains(const SkIRect& r) const {
michael@0 376 SkDEBUGCODE(this->validate();)
michael@0 377
michael@0 378 if (!fBounds.contains(r)) {
michael@0 379 return false;
michael@0 380 }
michael@0 381 if (this->isRect()) {
michael@0 382 return true;
michael@0 383 }
michael@0 384 SkASSERT(this->isComplex());
michael@0 385
michael@0 386 const RunType* scanline = fRunHead->findScanline(r.fTop);
michael@0 387 for (;;) {
michael@0 388 if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
michael@0 389 return false;
michael@0 390 }
michael@0 391 if (r.fBottom <= scanline_bottom(scanline)) {
michael@0 392 break;
michael@0 393 }
michael@0 394 scanline = scanline_next(scanline);
michael@0 395 }
michael@0 396 return true;
michael@0 397 }
michael@0 398
michael@0 399 bool SkRegion::contains(const SkRegion& rgn) const {
michael@0 400 SkDEBUGCODE(this->validate();)
michael@0 401 SkDEBUGCODE(rgn.validate();)
michael@0 402
michael@0 403 if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
michael@0 404 return false;
michael@0 405 }
michael@0 406 if (this->isRect()) {
michael@0 407 return true;
michael@0 408 }
michael@0 409 if (rgn.isRect()) {
michael@0 410 return this->contains(rgn.getBounds());
michael@0 411 }
michael@0 412
michael@0 413 /*
michael@0 414 * A contains B is equivalent to
michael@0 415 * B - A == 0
michael@0 416 */
michael@0 417 return !Oper(rgn, *this, kDifference_Op, NULL);
michael@0 418 }
michael@0 419
michael@0 420 const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
michael@0 421 int* intervals) const {
michael@0 422 SkASSERT(tmpStorage && intervals);
michael@0 423 const RunType* runs = tmpStorage;
michael@0 424
michael@0 425 if (this->isEmpty()) {
michael@0 426 tmpStorage[0] = kRunTypeSentinel;
michael@0 427 *intervals = 0;
michael@0 428 } else if (this->isRect()) {
michael@0 429 BuildRectRuns(fBounds, tmpStorage);
michael@0 430 *intervals = 1;
michael@0 431 } else {
michael@0 432 runs = fRunHead->readonly_runs();
michael@0 433 *intervals = fRunHead->getIntervalCount();
michael@0 434 }
michael@0 435 return runs;
michael@0 436 }
michael@0 437
michael@0 438 ///////////////////////////////////////////////////////////////////////////////
michael@0 439
michael@0 440 static bool scanline_intersects(const SkRegion::RunType runs[],
michael@0 441 SkRegion::RunType L, SkRegion::RunType R) {
michael@0 442 runs += 2; // skip Bottom and IntervalCount
michael@0 443 for (;;) {
michael@0 444 if (R <= runs[0]) {
michael@0 445 break;
michael@0 446 }
michael@0 447 if (L < runs[1]) {
michael@0 448 return true;
michael@0 449 }
michael@0 450 runs += 2;
michael@0 451 }
michael@0 452 return false;
michael@0 453 }
michael@0 454
michael@0 455 bool SkRegion::intersects(const SkIRect& r) const {
michael@0 456 SkDEBUGCODE(this->validate();)
michael@0 457
michael@0 458 if (this->isEmpty() || r.isEmpty()) {
michael@0 459 return false;
michael@0 460 }
michael@0 461
michael@0 462 SkIRect sect;
michael@0 463 if (!sect.intersect(fBounds, r)) {
michael@0 464 return false;
michael@0 465 }
michael@0 466 if (this->isRect()) {
michael@0 467 return true;
michael@0 468 }
michael@0 469 SkASSERT(this->isComplex());
michael@0 470
michael@0 471 const RunType* scanline = fRunHead->findScanline(sect.fTop);
michael@0 472 for (;;) {
michael@0 473 if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
michael@0 474 return true;
michael@0 475 }
michael@0 476 if (sect.fBottom <= scanline_bottom(scanline)) {
michael@0 477 break;
michael@0 478 }
michael@0 479 scanline = scanline_next(scanline);
michael@0 480 }
michael@0 481 return false;
michael@0 482 }
michael@0 483
michael@0 484 bool SkRegion::intersects(const SkRegion& rgn) const {
michael@0 485 if (this->isEmpty() || rgn.isEmpty()) {
michael@0 486 return false;
michael@0 487 }
michael@0 488
michael@0 489 if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
michael@0 490 return false;
michael@0 491 }
michael@0 492
michael@0 493 bool weAreARect = this->isRect();
michael@0 494 bool theyAreARect = rgn.isRect();
michael@0 495
michael@0 496 if (weAreARect && theyAreARect) {
michael@0 497 return true;
michael@0 498 }
michael@0 499 if (weAreARect) {
michael@0 500 return rgn.intersects(this->getBounds());
michael@0 501 }
michael@0 502 if (theyAreARect) {
michael@0 503 return this->intersects(rgn.getBounds());
michael@0 504 }
michael@0 505
michael@0 506 // both of us are complex
michael@0 507 return Oper(*this, rgn, kIntersect_Op, NULL);
michael@0 508 }
michael@0 509
michael@0 510 ///////////////////////////////////////////////////////////////////////////////
michael@0 511
michael@0 512 bool SkRegion::operator==(const SkRegion& b) const {
michael@0 513 SkDEBUGCODE(validate();)
michael@0 514 SkDEBUGCODE(b.validate();)
michael@0 515
michael@0 516 if (this == &b) {
michael@0 517 return true;
michael@0 518 }
michael@0 519 if (fBounds != b.fBounds) {
michael@0 520 return false;
michael@0 521 }
michael@0 522
michael@0 523 const SkRegion::RunHead* ah = fRunHead;
michael@0 524 const SkRegion::RunHead* bh = b.fRunHead;
michael@0 525
michael@0 526 // this catches empties and rects being equal
michael@0 527 if (ah == bh) {
michael@0 528 return true;
michael@0 529 }
michael@0 530 // now we insist that both are complex (but different ptrs)
michael@0 531 if (!this->isComplex() || !b.isComplex()) {
michael@0 532 return false;
michael@0 533 }
michael@0 534 return ah->fRunCount == bh->fRunCount &&
michael@0 535 !memcmp(ah->readonly_runs(), bh->readonly_runs(),
michael@0 536 ah->fRunCount * sizeof(SkRegion::RunType));
michael@0 537 }
michael@0 538
michael@0 539 void SkRegion::translate(int dx, int dy, SkRegion* dst) const {
michael@0 540 SkDEBUGCODE(this->validate();)
michael@0 541
michael@0 542 if (NULL == dst) {
michael@0 543 return;
michael@0 544 }
michael@0 545 if (this->isEmpty()) {
michael@0 546 dst->setEmpty();
michael@0 547 } else if (this->isRect()) {
michael@0 548 dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
michael@0 549 fBounds.fRight + dx, fBounds.fBottom + dy);
michael@0 550 } else {
michael@0 551 if (this == dst) {
michael@0 552 dst->fRunHead = dst->fRunHead->ensureWritable();
michael@0 553 } else {
michael@0 554 SkRegion tmp;
michael@0 555 tmp.allocateRuns(*fRunHead);
michael@0 556 tmp.fBounds = fBounds;
michael@0 557 dst->swap(tmp);
michael@0 558 }
michael@0 559
michael@0 560 dst->fBounds.offset(dx, dy);
michael@0 561
michael@0 562 const RunType* sruns = fRunHead->readonly_runs();
michael@0 563 RunType* druns = dst->fRunHead->writable_runs();
michael@0 564
michael@0 565 *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
michael@0 566 for (;;) {
michael@0 567 int bottom = *sruns++;
michael@0 568 if (bottom == kRunTypeSentinel) {
michael@0 569 break;
michael@0 570 }
michael@0 571 *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
michael@0 572 *druns++ = *sruns++; // copy intervalCount;
michael@0 573 for (;;) {
michael@0 574 int x = *sruns++;
michael@0 575 if (x == kRunTypeSentinel) {
michael@0 576 break;
michael@0 577 }
michael@0 578 *druns++ = (SkRegion::RunType)(x + dx);
michael@0 579 *druns++ = (SkRegion::RunType)(*sruns++ + dx);
michael@0 580 }
michael@0 581 *druns++ = kRunTypeSentinel; // x sentinel
michael@0 582 }
michael@0 583 *druns++ = kRunTypeSentinel; // y sentinel
michael@0 584
michael@0 585 SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
michael@0 586 SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
michael@0 587 }
michael@0 588
michael@0 589 SkDEBUGCODE(this->validate();)
michael@0 590 }
michael@0 591
michael@0 592 ///////////////////////////////////////////////////////////////////////////////
michael@0 593
michael@0 594 bool SkRegion::setRects(const SkIRect rects[], int count) {
michael@0 595 if (0 == count) {
michael@0 596 this->setEmpty();
michael@0 597 } else {
michael@0 598 this->setRect(rects[0]);
michael@0 599 for (int i = 1; i < count; i++) {
michael@0 600 this->op(rects[i], kUnion_Op);
michael@0 601 }
michael@0 602 }
michael@0 603 return !this->isEmpty();
michael@0 604 }
michael@0 605
michael@0 606 ///////////////////////////////////////////////////////////////////////////////
michael@0 607
michael@0 608 #if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized
michael@0 609 #pragma warning ( push )
michael@0 610 #pragma warning ( disable : 4701 )
michael@0 611 #endif
michael@0 612
michael@0 613 #ifdef SK_DEBUG
michael@0 614 static void assert_valid_pair(int left, int rite)
michael@0 615 {
michael@0 616 SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
michael@0 617 }
michael@0 618 #else
michael@0 619 #define assert_valid_pair(left, rite)
michael@0 620 #endif
michael@0 621
michael@0 622 struct spanRec {
michael@0 623 const SkRegion::RunType* fA_runs;
michael@0 624 const SkRegion::RunType* fB_runs;
michael@0 625 int fA_left, fA_rite, fB_left, fB_rite;
michael@0 626 int fLeft, fRite, fInside;
michael@0 627
michael@0 628 void init(const SkRegion::RunType a_runs[],
michael@0 629 const SkRegion::RunType b_runs[]) {
michael@0 630 fA_left = *a_runs++;
michael@0 631 fA_rite = *a_runs++;
michael@0 632 fB_left = *b_runs++;
michael@0 633 fB_rite = *b_runs++;
michael@0 634
michael@0 635 fA_runs = a_runs;
michael@0 636 fB_runs = b_runs;
michael@0 637 }
michael@0 638
michael@0 639 bool done() const {
michael@0 640 SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
michael@0 641 SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
michael@0 642 return fA_left == SkRegion::kRunTypeSentinel &&
michael@0 643 fB_left == SkRegion::kRunTypeSentinel;
michael@0 644 }
michael@0 645
michael@0 646 void next() {
michael@0 647 assert_valid_pair(fA_left, fA_rite);
michael@0 648 assert_valid_pair(fB_left, fB_rite);
michael@0 649
michael@0 650 int inside, left, rite SK_INIT_TO_AVOID_WARNING;
michael@0 651 bool a_flush = false;
michael@0 652 bool b_flush = false;
michael@0 653
michael@0 654 int a_left = fA_left;
michael@0 655 int a_rite = fA_rite;
michael@0 656 int b_left = fB_left;
michael@0 657 int b_rite = fB_rite;
michael@0 658
michael@0 659 if (a_left < b_left) {
michael@0 660 inside = 1;
michael@0 661 left = a_left;
michael@0 662 if (a_rite <= b_left) { // [...] <...>
michael@0 663 rite = a_rite;
michael@0 664 a_flush = true;
michael@0 665 } else { // [...<..]...> or [...<...>...]
michael@0 666 rite = a_left = b_left;
michael@0 667 }
michael@0 668 } else if (b_left < a_left) {
michael@0 669 inside = 2;
michael@0 670 left = b_left;
michael@0 671 if (b_rite <= a_left) { // [...] <...>
michael@0 672 rite = b_rite;
michael@0 673 b_flush = true;
michael@0 674 } else { // [...<..]...> or [...<...>...]
michael@0 675 rite = b_left = a_left;
michael@0 676 }
michael@0 677 } else { // a_left == b_left
michael@0 678 inside = 3;
michael@0 679 left = a_left; // or b_left
michael@0 680 if (a_rite <= b_rite) {
michael@0 681 rite = b_left = a_rite;
michael@0 682 a_flush = true;
michael@0 683 }
michael@0 684 if (b_rite <= a_rite) {
michael@0 685 rite = a_left = b_rite;
michael@0 686 b_flush = true;
michael@0 687 }
michael@0 688 }
michael@0 689
michael@0 690 if (a_flush) {
michael@0 691 a_left = *fA_runs++;
michael@0 692 a_rite = *fA_runs++;
michael@0 693 }
michael@0 694 if (b_flush) {
michael@0 695 b_left = *fB_runs++;
michael@0 696 b_rite = *fB_runs++;
michael@0 697 }
michael@0 698
michael@0 699 SkASSERT(left <= rite);
michael@0 700
michael@0 701 // now update our state
michael@0 702 fA_left = a_left;
michael@0 703 fA_rite = a_rite;
michael@0 704 fB_left = b_left;
michael@0 705 fB_rite = b_rite;
michael@0 706
michael@0 707 fLeft = left;
michael@0 708 fRite = rite;
michael@0 709 fInside = inside;
michael@0 710 }
michael@0 711 };
michael@0 712
michael@0 713 static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
michael@0 714 const SkRegion::RunType b_runs[],
michael@0 715 SkRegion::RunType dst[],
michael@0 716 int min, int max) {
michael@0 717 spanRec rec;
michael@0 718 bool firstInterval = true;
michael@0 719
michael@0 720 rec.init(a_runs, b_runs);
michael@0 721
michael@0 722 while (!rec.done()) {
michael@0 723 rec.next();
michael@0 724
michael@0 725 int left = rec.fLeft;
michael@0 726 int rite = rec.fRite;
michael@0 727
michael@0 728 // add left,rite to our dst buffer (checking for coincidence
michael@0 729 if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
michael@0 730 left < rite) { // skip if equal
michael@0 731 if (firstInterval || dst[-1] < left) {
michael@0 732 *dst++ = (SkRegion::RunType)(left);
michael@0 733 *dst++ = (SkRegion::RunType)(rite);
michael@0 734 firstInterval = false;
michael@0 735 } else {
michael@0 736 // update the right edge
michael@0 737 dst[-1] = (SkRegion::RunType)(rite);
michael@0 738 }
michael@0 739 }
michael@0 740 }
michael@0 741
michael@0 742 *dst++ = SkRegion::kRunTypeSentinel;
michael@0 743 return dst;
michael@0 744 }
michael@0 745
michael@0 746 #if defined _WIN32 && _MSC_VER >= 1300
michael@0 747 #pragma warning ( pop )
michael@0 748 #endif
michael@0 749
michael@0 750 static const struct {
michael@0 751 uint8_t fMin;
michael@0 752 uint8_t fMax;
michael@0 753 } gOpMinMax[] = {
michael@0 754 { 1, 1 }, // Difference
michael@0 755 { 3, 3 }, // Intersection
michael@0 756 { 1, 3 }, // Union
michael@0 757 { 1, 2 } // XOR
michael@0 758 };
michael@0 759
michael@0 760 class RgnOper {
michael@0 761 public:
michael@0 762 RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
michael@0 763 // need to ensure that the op enum lines up with our minmax array
michael@0 764 SkASSERT(SkRegion::kDifference_Op == 0);
michael@0 765 SkASSERT(SkRegion::kIntersect_Op == 1);
michael@0 766 SkASSERT(SkRegion::kUnion_Op == 2);
michael@0 767 SkASSERT(SkRegion::kXOR_Op == 3);
michael@0 768 SkASSERT((unsigned)op <= 3);
michael@0 769
michael@0 770 fStartDst = dst;
michael@0 771 fPrevDst = dst + 1;
michael@0 772 fPrevLen = 0; // will never match a length from operate_on_span
michael@0 773 fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
michael@0 774
michael@0 775 fMin = gOpMinMax[op].fMin;
michael@0 776 fMax = gOpMinMax[op].fMax;
michael@0 777 }
michael@0 778
michael@0 779 void addSpan(int bottom, const SkRegion::RunType a_runs[],
michael@0 780 const SkRegion::RunType b_runs[]) {
michael@0 781 // skip X values and slots for the next Y+intervalCount
michael@0 782 SkRegion::RunType* start = fPrevDst + fPrevLen + 2;
michael@0 783 // start points to beginning of dst interval
michael@0 784 SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
michael@0 785 size_t len = stop - start;
michael@0 786 SkASSERT(len >= 1 && (len & 1) == 1);
michael@0 787 SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
michael@0 788
michael@0 789 if (fPrevLen == len &&
michael@0 790 (1 == len || !memcmp(fPrevDst, start,
michael@0 791 (len - 1) * sizeof(SkRegion::RunType)))) {
michael@0 792 // update Y value
michael@0 793 fPrevDst[-2] = (SkRegion::RunType)(bottom);
michael@0 794 } else { // accept the new span
michael@0 795 if (len == 1 && fPrevLen == 0) {
michael@0 796 fTop = (SkRegion::RunType)(bottom); // just update our bottom
michael@0 797 } else {
michael@0 798 start[-2] = (SkRegion::RunType)(bottom);
michael@0 799 start[-1] = len >> 1;
michael@0 800 fPrevDst = start;
michael@0 801 fPrevLen = len;
michael@0 802 }
michael@0 803 }
michael@0 804 }
michael@0 805
michael@0 806 int flush() {
michael@0 807 fStartDst[0] = fTop;
michael@0 808 fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
michael@0 809 return (int)(fPrevDst - fStartDst + fPrevLen + 1);
michael@0 810 }
michael@0 811
michael@0 812 bool isEmpty() const { return 0 == fPrevLen; }
michael@0 813
michael@0 814 uint8_t fMin, fMax;
michael@0 815
michael@0 816 private:
michael@0 817 SkRegion::RunType* fStartDst;
michael@0 818 SkRegion::RunType* fPrevDst;
michael@0 819 size_t fPrevLen;
michael@0 820 SkRegion::RunType fTop;
michael@0 821 };
michael@0 822
michael@0 823 // want a unique value to signal that we exited due to quickExit
michael@0 824 #define QUICK_EXIT_TRUE_COUNT (-1)
michael@0 825
michael@0 826 static int operate(const SkRegion::RunType a_runs[],
michael@0 827 const SkRegion::RunType b_runs[],
michael@0 828 SkRegion::RunType dst[],
michael@0 829 SkRegion::Op op,
michael@0 830 bool quickExit) {
michael@0 831 const SkRegion::RunType gEmptyScanline[] = {
michael@0 832 0, // dummy bottom value
michael@0 833 0, // zero intervals
michael@0 834 SkRegion::kRunTypeSentinel,
michael@0 835 // just need a 2nd value, since spanRec.init() reads 2 values, even
michael@0 836 // though if the first value is the sentinel, it ignores the 2nd value.
michael@0 837 // w/o the 2nd value here, we might read uninitialized memory.
michael@0 838 // This happens when we are using gSentinel, which is pointing at
michael@0 839 // our sentinel value.
michael@0 840 0
michael@0 841 };
michael@0 842 const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
michael@0 843
michael@0 844 int a_top = *a_runs++;
michael@0 845 int a_bot = *a_runs++;
michael@0 846 int b_top = *b_runs++;
michael@0 847 int b_bot = *b_runs++;
michael@0 848
michael@0 849 a_runs += 1; // skip the intervalCount;
michael@0 850 b_runs += 1; // skip the intervalCount;
michael@0 851
michael@0 852 // Now a_runs and b_runs to their intervals (or sentinel)
michael@0 853
michael@0 854 assert_sentinel(a_top, false);
michael@0 855 assert_sentinel(a_bot, false);
michael@0 856 assert_sentinel(b_top, false);
michael@0 857 assert_sentinel(b_bot, false);
michael@0 858
michael@0 859 RgnOper oper(SkMin32(a_top, b_top), dst, op);
michael@0 860
michael@0 861 int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
michael@0 862
michael@0 863 while (a_bot < SkRegion::kRunTypeSentinel ||
michael@0 864 b_bot < SkRegion::kRunTypeSentinel) {
michael@0 865 int top, bot SK_INIT_TO_AVOID_WARNING;
michael@0 866 const SkRegion::RunType* run0 = gSentinel;
michael@0 867 const SkRegion::RunType* run1 = gSentinel;
michael@0 868 bool a_flush = false;
michael@0 869 bool b_flush = false;
michael@0 870
michael@0 871 if (a_top < b_top) {
michael@0 872 top = a_top;
michael@0 873 run0 = a_runs;
michael@0 874 if (a_bot <= b_top) { // [...] <...>
michael@0 875 bot = a_bot;
michael@0 876 a_flush = true;
michael@0 877 } else { // [...<..]...> or [...<...>...]
michael@0 878 bot = a_top = b_top;
michael@0 879 }
michael@0 880 } else if (b_top < a_top) {
michael@0 881 top = b_top;
michael@0 882 run1 = b_runs;
michael@0 883 if (b_bot <= a_top) { // [...] <...>
michael@0 884 bot = b_bot;
michael@0 885 b_flush = true;
michael@0 886 } else { // [...<..]...> or [...<...>...]
michael@0 887 bot = b_top = a_top;
michael@0 888 }
michael@0 889 } else { // a_top == b_top
michael@0 890 top = a_top; // or b_top
michael@0 891 run0 = a_runs;
michael@0 892 run1 = b_runs;
michael@0 893 if (a_bot <= b_bot) {
michael@0 894 bot = b_top = a_bot;
michael@0 895 a_flush = true;
michael@0 896 }
michael@0 897 if (b_bot <= a_bot) {
michael@0 898 bot = a_top = b_bot;
michael@0 899 b_flush = true;
michael@0 900 }
michael@0 901 }
michael@0 902
michael@0 903 if (top > prevBot) {
michael@0 904 oper.addSpan(top, gSentinel, gSentinel);
michael@0 905 }
michael@0 906 oper.addSpan(bot, run0, run1);
michael@0 907
michael@0 908 if (quickExit && !oper.isEmpty()) {
michael@0 909 return QUICK_EXIT_TRUE_COUNT;
michael@0 910 }
michael@0 911
michael@0 912 if (a_flush) {
michael@0 913 a_runs = skip_intervals(a_runs);
michael@0 914 a_top = a_bot;
michael@0 915 a_bot = *a_runs++;
michael@0 916 a_runs += 1; // skip uninitialized intervalCount
michael@0 917 if (a_bot == SkRegion::kRunTypeSentinel) {
michael@0 918 a_top = a_bot;
michael@0 919 }
michael@0 920 }
michael@0 921 if (b_flush) {
michael@0 922 b_runs = skip_intervals(b_runs);
michael@0 923 b_top = b_bot;
michael@0 924 b_bot = *b_runs++;
michael@0 925 b_runs += 1; // skip uninitialized intervalCount
michael@0 926 if (b_bot == SkRegion::kRunTypeSentinel) {
michael@0 927 b_top = b_bot;
michael@0 928 }
michael@0 929 }
michael@0 930
michael@0 931 prevBot = bot;
michael@0 932 }
michael@0 933 return oper.flush();
michael@0 934 }
michael@0 935
michael@0 936 ///////////////////////////////////////////////////////////////////////////////
michael@0 937
michael@0 938 /* Given count RunTypes in a complex region, return the worst case number of
michael@0 939 logical intervals that represents (i.e. number of rects that would be
michael@0 940 returned from the iterator).
michael@0 941
michael@0 942 We could just return count/2, since there must be at least 2 values per
michael@0 943 interval, but we can first trim off the const overhead of the initial TOP
michael@0 944 value, plus the final BOTTOM + 2 sentinels.
michael@0 945 */
michael@0 946 #if 0 // UNUSED
michael@0 947 static int count_to_intervals(int count) {
michael@0 948 SkASSERT(count >= 6); // a single rect is 6 values
michael@0 949 return (count - 4) >> 1;
michael@0 950 }
michael@0 951 #endif
michael@0 952
michael@0 953 /* Given a number of intervals, what is the worst case representation of that
michael@0 954 many intervals?
michael@0 955
michael@0 956 Worst case (from a storage perspective), is a vertical stack of single
michael@0 957 intervals: TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
michael@0 958 */
michael@0 959 static int intervals_to_count(int intervals) {
michael@0 960 return 1 + intervals * 5 + 1;
michael@0 961 }
michael@0 962
michael@0 963 /* Given the intervalCounts of RunTypes in two regions, return the worst-case number
michael@0 964 of RunTypes need to store the result after a region-op.
michael@0 965 */
michael@0 966 static int compute_worst_case_count(int a_intervals, int b_intervals) {
michael@0 967 // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
michael@0 968 int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
michael@0 969 // convert back to number of RunType values
michael@0 970 return intervals_to_count(intervals);
michael@0 971 }
michael@0 972
michael@0 973 static bool setEmptyCheck(SkRegion* result) {
michael@0 974 return result ? result->setEmpty() : false;
michael@0 975 }
michael@0 976
michael@0 977 static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
michael@0 978 return result ? result->setRect(rect) : !rect.isEmpty();
michael@0 979 }
michael@0 980
michael@0 981 static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
michael@0 982 return result ? result->setRegion(rgn) : !rgn.isEmpty();
michael@0 983 }
michael@0 984
michael@0 985 bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
michael@0 986 SkRegion* result) {
michael@0 987 SkASSERT((unsigned)op < kOpCount);
michael@0 988
michael@0 989 if (kReplace_Op == op) {
michael@0 990 return setRegionCheck(result, rgnbOrig);
michael@0 991 }
michael@0 992
michael@0 993 // swith to using pointers, so we can swap them as needed
michael@0 994 const SkRegion* rgna = &rgnaOrig;
michael@0 995 const SkRegion* rgnb = &rgnbOrig;
michael@0 996 // after this point, do not refer to rgnaOrig or rgnbOrig!!!
michael@0 997
michael@0 998 // collaps difference and reverse-difference into just difference
michael@0 999 if (kReverseDifference_Op == op) {
michael@0 1000 SkTSwap<const SkRegion*>(rgna, rgnb);
michael@0 1001 op = kDifference_Op;
michael@0 1002 }
michael@0 1003
michael@0 1004 SkIRect bounds;
michael@0 1005 bool a_empty = rgna->isEmpty();
michael@0 1006 bool b_empty = rgnb->isEmpty();
michael@0 1007 bool a_rect = rgna->isRect();
michael@0 1008 bool b_rect = rgnb->isRect();
michael@0 1009
michael@0 1010 switch (op) {
michael@0 1011 case kDifference_Op:
michael@0 1012 if (a_empty) {
michael@0 1013 return setEmptyCheck(result);
michael@0 1014 }
michael@0 1015 if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
michael@0 1016 rgnb->fBounds)) {
michael@0 1017 return setRegionCheck(result, *rgna);
michael@0 1018 }
michael@0 1019 if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
michael@0 1020 return setEmptyCheck(result);
michael@0 1021 }
michael@0 1022 break;
michael@0 1023
michael@0 1024 case kIntersect_Op:
michael@0 1025 if ((a_empty | b_empty)
michael@0 1026 || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
michael@0 1027 return setEmptyCheck(result);
michael@0 1028 }
michael@0 1029 if (a_rect & b_rect) {
michael@0 1030 return setRectCheck(result, bounds);
michael@0 1031 }
michael@0 1032 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
michael@0 1033 return setRegionCheck(result, *rgnb);
michael@0 1034 }
michael@0 1035 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
michael@0 1036 return setRegionCheck(result, *rgna);
michael@0 1037 }
michael@0 1038 break;
michael@0 1039
michael@0 1040 case kUnion_Op:
michael@0 1041 if (a_empty) {
michael@0 1042 return setRegionCheck(result, *rgnb);
michael@0 1043 }
michael@0 1044 if (b_empty) {
michael@0 1045 return setRegionCheck(result, *rgna);
michael@0 1046 }
michael@0 1047 if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
michael@0 1048 return setRegionCheck(result, *rgna);
michael@0 1049 }
michael@0 1050 if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
michael@0 1051 return setRegionCheck(result, *rgnb);
michael@0 1052 }
michael@0 1053 break;
michael@0 1054
michael@0 1055 case kXOR_Op:
michael@0 1056 if (a_empty) {
michael@0 1057 return setRegionCheck(result, *rgnb);
michael@0 1058 }
michael@0 1059 if (b_empty) {
michael@0 1060 return setRegionCheck(result, *rgna);
michael@0 1061 }
michael@0 1062 break;
michael@0 1063 default:
michael@0 1064 SkDEBUGFAIL("unknown region op");
michael@0 1065 return false;
michael@0 1066 }
michael@0 1067
michael@0 1068 RunType tmpA[kRectRegionRuns];
michael@0 1069 RunType tmpB[kRectRegionRuns];
michael@0 1070
michael@0 1071 int a_intervals, b_intervals;
michael@0 1072 const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
michael@0 1073 const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
michael@0 1074
michael@0 1075 int dstCount = compute_worst_case_count(a_intervals, b_intervals);
michael@0 1076 SkAutoSTMalloc<256, RunType> array(dstCount);
michael@0 1077
michael@0 1078 #ifdef SK_DEBUG
michael@0 1079 // Sometimes helpful to seed everything with a known value when debugging
michael@0 1080 // sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
michael@0 1081 #endif
michael@0 1082
michael@0 1083 int count = operate(a_runs, b_runs, array.get(), op, NULL == result);
michael@0 1084 SkASSERT(count <= dstCount);
michael@0 1085
michael@0 1086 if (result) {
michael@0 1087 SkASSERT(count >= 0);
michael@0 1088 return result->setRuns(array.get(), count);
michael@0 1089 } else {
michael@0 1090 return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
michael@0 1091 }
michael@0 1092 }
michael@0 1093
michael@0 1094 bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
michael@0 1095 SkDEBUGCODE(this->validate();)
michael@0 1096 return SkRegion::Oper(rgna, rgnb, op, this);
michael@0 1097 }
michael@0 1098
michael@0 1099 ///////////////////////////////////////////////////////////////////////////////
michael@0 1100
michael@0 1101 #include "SkBuffer.h"
michael@0 1102
michael@0 1103 size_t SkRegion::writeToMemory(void* storage) const {
michael@0 1104 if (NULL == storage) {
michael@0 1105 size_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
michael@0 1106 if (!this->isEmpty()) {
michael@0 1107 size += sizeof(fBounds);
michael@0 1108 if (this->isComplex()) {
michael@0 1109 size += 2 * sizeof(int32_t); // ySpanCount + intervalCount
michael@0 1110 size += fRunHead->fRunCount * sizeof(RunType);
michael@0 1111 }
michael@0 1112 }
michael@0 1113 return size;
michael@0 1114 }
michael@0 1115
michael@0 1116 SkWBuffer buffer(storage);
michael@0 1117
michael@0 1118 if (this->isEmpty()) {
michael@0 1119 buffer.write32(-1);
michael@0 1120 } else {
michael@0 1121 bool isRect = this->isRect();
michael@0 1122
michael@0 1123 buffer.write32(isRect ? 0 : fRunHead->fRunCount);
michael@0 1124 buffer.write(&fBounds, sizeof(fBounds));
michael@0 1125
michael@0 1126 if (!isRect) {
michael@0 1127 buffer.write32(fRunHead->getYSpanCount());
michael@0 1128 buffer.write32(fRunHead->getIntervalCount());
michael@0 1129 buffer.write(fRunHead->readonly_runs(),
michael@0 1130 fRunHead->fRunCount * sizeof(RunType));
michael@0 1131 }
michael@0 1132 }
michael@0 1133 return buffer.pos();
michael@0 1134 }
michael@0 1135
michael@0 1136 size_t SkRegion::readFromMemory(const void* storage, size_t length) {
michael@0 1137 SkRBufferWithSizeCheck buffer(storage, length);
michael@0 1138 SkRegion tmp;
michael@0 1139 int32_t count;
michael@0 1140
michael@0 1141 if (buffer.readS32(&count) && (count >= 0) && buffer.read(&tmp.fBounds, sizeof(tmp.fBounds))) {
michael@0 1142 if (count == 0) {
michael@0 1143 tmp.fRunHead = SkRegion_gRectRunHeadPtr;
michael@0 1144 } else {
michael@0 1145 int32_t ySpanCount, intervalCount;
michael@0 1146 if (buffer.readS32(&ySpanCount) && buffer.readS32(&intervalCount)) {
michael@0 1147 tmp.allocateRuns(count, ySpanCount, intervalCount);
michael@0 1148 buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
michael@0 1149 }
michael@0 1150 }
michael@0 1151 }
michael@0 1152 size_t sizeRead = 0;
michael@0 1153 if (buffer.isValid()) {
michael@0 1154 this->swap(tmp);
michael@0 1155 sizeRead = buffer.pos();
michael@0 1156 }
michael@0 1157 return sizeRead;
michael@0 1158 }
michael@0 1159
michael@0 1160 ///////////////////////////////////////////////////////////////////////////////
michael@0 1161
michael@0 1162 const SkRegion& SkRegion::GetEmptyRegion() {
michael@0 1163 static SkRegion gEmpty;
michael@0 1164 return gEmpty;
michael@0 1165 }
michael@0 1166
michael@0 1167 ///////////////////////////////////////////////////////////////////////////////
michael@0 1168
michael@0 1169 #ifdef SK_DEBUG
michael@0 1170
michael@0 1171 // Starts with first X-interval, and returns a ptr to the X-sentinel
michael@0 1172 static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) {
michael@0 1173 // want to track that our intevals are all disjoint, such that
michael@0 1174 // prev-right < next-left. We rely on this optimization in places such as
michael@0 1175 // contains().
michael@0 1176 //
michael@0 1177 SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel;
michael@0 1178
michael@0 1179 while (runs[0] < SkRegion::kRunTypeSentinel) {
michael@0 1180 SkASSERT(prevR < runs[0]);
michael@0 1181 SkASSERT(runs[0] < runs[1]);
michael@0 1182 SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
michael@0 1183 prevR = runs[1];
michael@0 1184 runs += 2;
michael@0 1185 }
michael@0 1186 return runs;
michael@0 1187 }
michael@0 1188
michael@0 1189 static void compute_bounds(const SkRegion::RunType runs[],
michael@0 1190 SkIRect* bounds, int* ySpanCountPtr,
michael@0 1191 int* intervalCountPtr) {
michael@0 1192 assert_sentinel(runs[0], false); // top
michael@0 1193
michael@0 1194 int left = SK_MaxS32;
michael@0 1195 int rite = SK_MinS32;
michael@0 1196 int bot;
michael@0 1197 int ySpanCount = 0;
michael@0 1198 int intervalCount = 0;
michael@0 1199
michael@0 1200 bounds->fTop = *runs++;
michael@0 1201 do {
michael@0 1202 bot = *runs++;
michael@0 1203 SkASSERT(SkRegion::kRunTypeSentinel > bot);
michael@0 1204
michael@0 1205 ySpanCount += 1;
michael@0 1206
michael@0 1207 runs += 1; // skip intervalCount for now
michael@0 1208 if (*runs < SkRegion::kRunTypeSentinel) {
michael@0 1209 if (left > *runs) {
michael@0 1210 left = *runs;
michael@0 1211 }
michael@0 1212
michael@0 1213 const SkRegion::RunType* prev = runs;
michael@0 1214 runs = skip_intervals_slow(runs);
michael@0 1215 int intervals = (runs - prev) >> 1;
michael@0 1216 SkASSERT(prev[-1] == intervals);
michael@0 1217 intervalCount += intervals;
michael@0 1218
michael@0 1219 if (rite < runs[-1]) {
michael@0 1220 rite = runs[-1];
michael@0 1221 }
michael@0 1222 } else {
michael@0 1223 SkASSERT(0 == runs[-1]); // no intervals
michael@0 1224 }
michael@0 1225 SkASSERT(SkRegion::kRunTypeSentinel == *runs);
michael@0 1226 runs += 1;
michael@0 1227 } while (SkRegion::kRunTypeSentinel != *runs);
michael@0 1228
michael@0 1229 bounds->fLeft = left;
michael@0 1230 bounds->fRight = rite;
michael@0 1231 bounds->fBottom = bot;
michael@0 1232 *ySpanCountPtr = ySpanCount;
michael@0 1233 *intervalCountPtr = intervalCount;
michael@0 1234 }
michael@0 1235
michael@0 1236 void SkRegion::validate() const {
michael@0 1237 if (this->isEmpty()) {
michael@0 1238 // check for explicit empty (the zero rect), so we can compare rects to know when
michael@0 1239 // two regions are equal (i.e. emptyRectA == emptyRectB)
michael@0 1240 // this is stricter than just asserting fBounds.isEmpty()
michael@0 1241 SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
michael@0 1242 } else {
michael@0 1243 SkASSERT(!fBounds.isEmpty());
michael@0 1244 if (!this->isRect()) {
michael@0 1245 SkASSERT(fRunHead->fRefCnt >= 1);
michael@0 1246 SkASSERT(fRunHead->fRunCount > kRectRegionRuns);
michael@0 1247
michael@0 1248 const RunType* run = fRunHead->readonly_runs();
michael@0 1249
michael@0 1250 // check that our bounds match our runs
michael@0 1251 {
michael@0 1252 SkIRect bounds;
michael@0 1253 int ySpanCount, intervalCount;
michael@0 1254 compute_bounds(run, &bounds, &ySpanCount, &intervalCount);
michael@0 1255
michael@0 1256 SkASSERT(bounds == fBounds);
michael@0 1257 SkASSERT(ySpanCount > 0);
michael@0 1258 SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
michael@0 1259 // SkASSERT(intervalCount > 1);
michael@0 1260 SkASSERT(fRunHead->getIntervalCount() == intervalCount);
michael@0 1261 }
michael@0 1262 }
michael@0 1263 }
michael@0 1264 }
michael@0 1265
michael@0 1266 void SkRegion::dump() const {
michael@0 1267 if (this->isEmpty()) {
michael@0 1268 SkDebugf(" rgn: empty\n");
michael@0 1269 } else {
michael@0 1270 SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
michael@0 1271 if (this->isComplex()) {
michael@0 1272 const RunType* runs = fRunHead->readonly_runs();
michael@0 1273 for (int i = 0; i < fRunHead->fRunCount; i++)
michael@0 1274 SkDebugf(" %d", runs[i]);
michael@0 1275 }
michael@0 1276 SkDebugf("\n");
michael@0 1277 }
michael@0 1278 }
michael@0 1279
michael@0 1280 #endif
michael@0 1281
michael@0 1282 ///////////////////////////////////////////////////////////////////////////////
michael@0 1283
michael@0 1284 SkRegion::Iterator::Iterator(const SkRegion& rgn) {
michael@0 1285 this->reset(rgn);
michael@0 1286 }
michael@0 1287
michael@0 1288 bool SkRegion::Iterator::rewind() {
michael@0 1289 if (fRgn) {
michael@0 1290 this->reset(*fRgn);
michael@0 1291 return true;
michael@0 1292 }
michael@0 1293 return false;
michael@0 1294 }
michael@0 1295
michael@0 1296 void SkRegion::Iterator::reset(const SkRegion& rgn) {
michael@0 1297 fRgn = &rgn;
michael@0 1298 if (rgn.isEmpty()) {
michael@0 1299 fDone = true;
michael@0 1300 } else {
michael@0 1301 fDone = false;
michael@0 1302 if (rgn.isRect()) {
michael@0 1303 fRect = rgn.fBounds;
michael@0 1304 fRuns = NULL;
michael@0 1305 } else {
michael@0 1306 fRuns = rgn.fRunHead->readonly_runs();
michael@0 1307 fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
michael@0 1308 fRuns += 5;
michael@0 1309 // Now fRuns points to the 2nd interval (or x-sentinel)
michael@0 1310 }
michael@0 1311 }
michael@0 1312 }
michael@0 1313
michael@0 1314 void SkRegion::Iterator::next() {
michael@0 1315 if (fDone) {
michael@0 1316 return;
michael@0 1317 }
michael@0 1318
michael@0 1319 if (fRuns == NULL) { // rect case
michael@0 1320 fDone = true;
michael@0 1321 return;
michael@0 1322 }
michael@0 1323
michael@0 1324 const RunType* runs = fRuns;
michael@0 1325
michael@0 1326 if (runs[0] < kRunTypeSentinel) { // valid X value
michael@0 1327 fRect.fLeft = runs[0];
michael@0 1328 fRect.fRight = runs[1];
michael@0 1329 runs += 2;
michael@0 1330 } else { // we're at the end of a line
michael@0 1331 runs += 1;
michael@0 1332 if (runs[0] < kRunTypeSentinel) { // valid Y value
michael@0 1333 int intervals = runs[1];
michael@0 1334 if (0 == intervals) { // empty line
michael@0 1335 fRect.fTop = runs[0];
michael@0 1336 runs += 3;
michael@0 1337 } else {
michael@0 1338 fRect.fTop = fRect.fBottom;
michael@0 1339 }
michael@0 1340
michael@0 1341 fRect.fBottom = runs[0];
michael@0 1342 assert_sentinel(runs[2], false);
michael@0 1343 assert_sentinel(runs[3], false);
michael@0 1344 fRect.fLeft = runs[2];
michael@0 1345 fRect.fRight = runs[3];
michael@0 1346 runs += 4;
michael@0 1347 } else { // end of rgn
michael@0 1348 fDone = true;
michael@0 1349 }
michael@0 1350 }
michael@0 1351 fRuns = runs;
michael@0 1352 }
michael@0 1353
michael@0 1354 SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
michael@0 1355 : fIter(rgn), fClip(clip), fDone(true) {
michael@0 1356 const SkIRect& r = fIter.rect();
michael@0 1357
michael@0 1358 while (!fIter.done()) {
michael@0 1359 if (r.fTop >= clip.fBottom) {
michael@0 1360 break;
michael@0 1361 }
michael@0 1362 if (fRect.intersect(clip, r)) {
michael@0 1363 fDone = false;
michael@0 1364 break;
michael@0 1365 }
michael@0 1366 fIter.next();
michael@0 1367 }
michael@0 1368 }
michael@0 1369
michael@0 1370 void SkRegion::Cliperator::next() {
michael@0 1371 if (fDone) {
michael@0 1372 return;
michael@0 1373 }
michael@0 1374
michael@0 1375 const SkIRect& r = fIter.rect();
michael@0 1376
michael@0 1377 fDone = true;
michael@0 1378 fIter.next();
michael@0 1379 while (!fIter.done()) {
michael@0 1380 if (r.fTop >= fClip.fBottom) {
michael@0 1381 break;
michael@0 1382 }
michael@0 1383 if (fRect.intersect(fClip, r)) {
michael@0 1384 fDone = false;
michael@0 1385 break;
michael@0 1386 }
michael@0 1387 fIter.next();
michael@0 1388 }
michael@0 1389 }
michael@0 1390
michael@0 1391 ///////////////////////////////////////////////////////////////////////////////
michael@0 1392
michael@0 1393 SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
michael@0 1394 int right) {
michael@0 1395 SkDEBUGCODE(rgn.validate();)
michael@0 1396
michael@0 1397 const SkIRect& r = rgn.getBounds();
michael@0 1398
michael@0 1399 fDone = true;
michael@0 1400 if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom &&
michael@0 1401 right > r.fLeft && left < r.fRight) {
michael@0 1402 if (rgn.isRect()) {
michael@0 1403 if (left < r.fLeft) {
michael@0 1404 left = r.fLeft;
michael@0 1405 }
michael@0 1406 if (right > r.fRight) {
michael@0 1407 right = r.fRight;
michael@0 1408 }
michael@0 1409 fLeft = left;
michael@0 1410 fRight = right;
michael@0 1411 fRuns = NULL; // means we're a rect, not a rgn
michael@0 1412 fDone = false;
michael@0 1413 } else {
michael@0 1414 const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
michael@0 1415 runs += 2; // skip Bottom and IntervalCount
michael@0 1416 for (;;) {
michael@0 1417 // runs[0..1] is to the right of the span, so we're done
michael@0 1418 if (runs[0] >= right) {
michael@0 1419 break;
michael@0 1420 }
michael@0 1421 // runs[0..1] is to the left of the span, so continue
michael@0 1422 if (runs[1] <= left) {
michael@0 1423 runs += 2;
michael@0 1424 continue;
michael@0 1425 }
michael@0 1426 // runs[0..1] intersects the span
michael@0 1427 fRuns = runs;
michael@0 1428 fLeft = left;
michael@0 1429 fRight = right;
michael@0 1430 fDone = false;
michael@0 1431 break;
michael@0 1432 }
michael@0 1433 }
michael@0 1434 }
michael@0 1435 }
michael@0 1436
michael@0 1437 bool SkRegion::Spanerator::next(int* left, int* right) {
michael@0 1438 if (fDone) {
michael@0 1439 return false;
michael@0 1440 }
michael@0 1441
michael@0 1442 if (fRuns == NULL) { // we're a rect
michael@0 1443 fDone = true; // ok, now we're done
michael@0 1444 if (left) {
michael@0 1445 *left = fLeft;
michael@0 1446 }
michael@0 1447 if (right) {
michael@0 1448 *right = fRight;
michael@0 1449 }
michael@0 1450 return true; // this interval is legal
michael@0 1451 }
michael@0 1452
michael@0 1453 const SkRegion::RunType* runs = fRuns;
michael@0 1454
michael@0 1455 if (runs[0] >= fRight) {
michael@0 1456 fDone = true;
michael@0 1457 return false;
michael@0 1458 }
michael@0 1459
michael@0 1460 SkASSERT(runs[1] > fLeft);
michael@0 1461
michael@0 1462 if (left) {
michael@0 1463 *left = SkMax32(fLeft, runs[0]);
michael@0 1464 }
michael@0 1465 if (right) {
michael@0 1466 *right = SkMin32(fRight, runs[1]);
michael@0 1467 }
michael@0 1468 fRuns = runs + 2;
michael@0 1469 return true;
michael@0 1470 }
michael@0 1471
michael@0 1472 ///////////////////////////////////////////////////////////////////////////////
michael@0 1473
michael@0 1474 #ifdef SK_DEBUG
michael@0 1475
michael@0 1476 bool SkRegion::debugSetRuns(const RunType runs[], int count) {
michael@0 1477 // we need to make a copy, since the real method may modify the array, and
michael@0 1478 // so it cannot be const.
michael@0 1479
michael@0 1480 SkAutoTArray<RunType> storage(count);
michael@0 1481 memcpy(storage.get(), runs, count * sizeof(RunType));
michael@0 1482 return this->setRuns(storage.get(), count);
michael@0 1483 }
michael@0 1484
michael@0 1485 #endif

mercurial