SkRegion_path.cpp revision a306d93cd73c3fc1d81479cbba98638f1e055385
1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkRegionPriv.h" 11#include "SkBlitter.h" 12#include "SkScan.h" 13#include "SkTDArray.h" 14#include "SkPath.h" 15 16class SkRgnBuilder : public SkBlitter { 17public: 18 virtual ~SkRgnBuilder(); 19 20 // returns true if it could allocate the working storage needed 21 bool init(int maxHeight, int maxTransitions, bool pathIsInverse); 22 23 void done() { 24 if (fCurrScanline != NULL) { 25 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX())); 26 if (!this->collapsWithPrev()) { // flush the last line 27 fCurrScanline = fCurrScanline->nextScanline(); 28 } 29 } 30 } 31 32 int computeRunCount() const; 33 void copyToRect(SkIRect*) const; 34 void copyToRgn(SkRegion::RunType runs[]) const; 35 36 virtual void blitH(int x, int y, int width); 37 38#ifdef SK_DEBUG 39 void dump() const { 40 SkDebugf("SkRgnBuilder: Top = %d\n", fTop); 41 const Scanline* line = (Scanline*)fStorage; 42 while (line < fCurrScanline) { 43 SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount); 44 for (int i = 0; i < line->fXCount; i++) { 45 SkDebugf(" %d", line->firstX()[i]); 46 } 47 SkDebugf("\n"); 48 49 line = line->nextScanline(); 50 } 51 } 52#endif 53private: 54 /* 55 * Scanline mimics a row in the region, nearly. A row in a region is: 56 * [Bottom IntervalCount [L R]... Sentinel] 57 * while a Scanline is 58 * [LastY XCount [L R]... uninitialized] 59 * The two are the same length (which is good), but we have to transmute 60 * the scanline a little when we convert it to a region-row. 61 * 62 * Potentially we could recode this to exactly match the row format, in 63 * which case copyToRgn() could be a single memcpy. Not sure that is worth 64 * the effort. 65 */ 66 struct Scanline { 67 SkRegion::RunType fLastY; 68 SkRegion::RunType fXCount; 69 70 SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); } 71 Scanline* nextScanline() const { 72 // add final +1 for the x-sentinel 73 return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1); 74 } 75 }; 76 SkRegion::RunType* fStorage; 77 Scanline* fCurrScanline; 78 Scanline* fPrevScanline; 79 // points at next avialable x[] in fCurrScanline 80 SkRegion::RunType* fCurrXPtr; 81 SkRegion::RunType fTop; // first Y value 82 83 int fStorageCount; 84 85 bool collapsWithPrev() { 86 if (fPrevScanline != NULL && 87 fPrevScanline->fLastY + 1 == fCurrScanline->fLastY && 88 fPrevScanline->fXCount == fCurrScanline->fXCount && 89 !memcmp(fPrevScanline->firstX(), 90 fCurrScanline->firstX(), 91 fCurrScanline->fXCount * sizeof(SkRegion::RunType))) 92 { 93 // update the height of fPrevScanline 94 fPrevScanline->fLastY = fCurrScanline->fLastY; 95 return true; 96 } 97 return false; 98 } 99}; 100 101SkRgnBuilder::~SkRgnBuilder() { 102 sk_free(fStorage); 103} 104 105bool SkRgnBuilder::init(int maxHeight, int maxTransitions, bool pathIsInverse) { 106 if ((maxHeight | maxTransitions) < 0) { 107 return false; 108 } 109 110 if (pathIsInverse) { 111 // allow for additional X transitions to "invert" each scanline 112 // [ L' ... normal transitions ... R' ] 113 // 114 maxTransitions += 2; 115 } 116 117 // compute the count with +1 and +3 slop for the working buffer 118 int64_t count = sk_64_mul(maxHeight + 1, 3 + maxTransitions); 119 120 if (pathIsInverse) { 121 // allow for two "empty" rows for the top and bottom 122 // [ Y, 1, L, R, S] == 5 (*2 for top and bottom) 123 count += 10; 124 } 125 126 if (count < 0 || !sk_64_isS32(count)) { 127 return false; 128 } 129 fStorageCount = sk_64_asS32(count); 130 131 int64_t size = sk_64_mul(fStorageCount, sizeof(SkRegion::RunType)); 132 if (size < 0 || !sk_64_isS32(size)) { 133 return false; 134 } 135 136 fStorage = (SkRegion::RunType*)sk_malloc_flags(sk_64_asS32(size), 0); 137 if (NULL == fStorage) { 138 return false; 139 } 140 141 fCurrScanline = NULL; // signal empty collection 142 fPrevScanline = NULL; // signal first scanline 143 return true; 144} 145 146void SkRgnBuilder::blitH(int x, int y, int width) { 147 if (fCurrScanline == NULL) { // first time 148 fTop = (SkRegion::RunType)(y); 149 fCurrScanline = (Scanline*)fStorage; 150 fCurrScanline->fLastY = (SkRegion::RunType)(y); 151 fCurrXPtr = fCurrScanline->firstX(); 152 } else { 153 SkASSERT(y >= fCurrScanline->fLastY); 154 155 if (y > fCurrScanline->fLastY) { 156 // if we get here, we're done with fCurrScanline 157 fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX())); 158 159 int prevLastY = fCurrScanline->fLastY; 160 if (!this->collapsWithPrev()) { 161 fPrevScanline = fCurrScanline; 162 fCurrScanline = fCurrScanline->nextScanline(); 163 164 } 165 if (y - 1 > prevLastY) { // insert empty run 166 fCurrScanline->fLastY = (SkRegion::RunType)(y - 1); 167 fCurrScanline->fXCount = 0; 168 fCurrScanline = fCurrScanline->nextScanline(); 169 } 170 // setup for the new curr line 171 fCurrScanline->fLastY = (SkRegion::RunType)(y); 172 fCurrXPtr = fCurrScanline->firstX(); 173 } 174 } 175 // check if we should extend the current run, or add a new one 176 if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) { 177 fCurrXPtr[-1] = (SkRegion::RunType)(x + width); 178 } else { 179 fCurrXPtr[0] = (SkRegion::RunType)(x); 180 fCurrXPtr[1] = (SkRegion::RunType)(x + width); 181 fCurrXPtr += 2; 182 } 183 SkASSERT(fCurrXPtr - fStorage < fStorageCount); 184} 185 186int SkRgnBuilder::computeRunCount() const { 187 if (fCurrScanline == NULL) { 188 return 0; 189 } 190 191 const SkRegion::RunType* line = fStorage; 192 const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline; 193 194 return 2 + (int)(stop - line); 195} 196 197void SkRgnBuilder::copyToRect(SkIRect* r) const { 198 SkASSERT(fCurrScanline != NULL); 199 // A rect's scanline is [bottom intervals left right sentinel] == 5 200 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 5); 201 202 const Scanline* line = (const Scanline*)fStorage; 203 SkASSERT(line->fXCount == 2); 204 205 r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1); 206} 207 208void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const { 209 SkASSERT(fCurrScanline != NULL); 210 SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4); 211 212 const Scanline* line = (const Scanline*)fStorage; 213 const Scanline* stop = fCurrScanline; 214 215 *runs++ = fTop; 216 do { 217 *runs++ = (SkRegion::RunType)(line->fLastY + 1); 218 int count = line->fXCount; 219 *runs++ = count >> 1; // intervalCount 220 if (count) { 221 memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType)); 222 runs += count; 223 } 224 *runs++ = SkRegion::kRunTypeSentinel; 225 line = line->nextScanline(); 226 } while (line < stop); 227 SkASSERT(line == stop); 228 *runs = SkRegion::kRunTypeSentinel; 229} 230 231static unsigned verb_to_initial_last_index(unsigned verb) { 232 static const uint8_t gPathVerbToInitialLastIndex[] = { 233 0, // kMove_Verb 234 1, // kLine_Verb 235 2, // kQuad_Verb 236 2, // kConic_Verb 237 3, // kCubic_Verb 238 0, // kClose_Verb 239 0 // kDone_Verb 240 }; 241 SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToInitialLastIndex)); 242 return gPathVerbToInitialLastIndex[verb]; 243} 244 245static unsigned verb_to_max_edges(unsigned verb) { 246 static const uint8_t gPathVerbToMaxEdges[] = { 247 0, // kMove_Verb 248 1, // kLine_Verb 249 2, // kQuad_VerbB 250 2, // kConic_VerbB 251 3, // kCubic_Verb 252 0, // kClose_Verb 253 0 // kDone_Verb 254 }; 255 SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToMaxEdges)); 256 return gPathVerbToMaxEdges[verb]; 257} 258 259 260static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) { 261 SkPath::Iter iter(path, true); 262 SkPoint pts[4]; 263 SkPath::Verb verb; 264 265 int maxEdges = 0; 266 SkScalar top = SkIntToScalar(SK_MaxS16); 267 SkScalar bot = SkIntToScalar(SK_MinS16); 268 269 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { 270 maxEdges += verb_to_max_edges(verb); 271 272 int lastIndex = verb_to_initial_last_index(verb); 273 if (lastIndex > 0) { 274 for (int i = 1; i <= lastIndex; i++) { 275 if (top > pts[i].fY) { 276 top = pts[i].fY; 277 } else if (bot < pts[i].fY) { 278 bot = pts[i].fY; 279 } 280 } 281 } else if (SkPath::kMove_Verb == verb) { 282 if (top > pts[0].fY) { 283 top = pts[0].fY; 284 } else if (bot < pts[0].fY) { 285 bot = pts[0].fY; 286 } 287 } 288 } 289 SkASSERT(top <= bot); 290 291 *itop = SkScalarRoundToInt(top); 292 *ibot = SkScalarRoundToInt(bot); 293 return maxEdges; 294} 295 296bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) { 297 SkDEBUGCODE(this->validate();) 298 299 if (clip.isEmpty()) { 300 return this->setEmpty(); 301 } 302 303 if (path.isEmpty()) { 304 if (path.isInverseFillType()) { 305 return this->set(clip); 306 } else { 307 return this->setEmpty(); 308 } 309 } 310 311 // compute worst-case rgn-size for the path 312 int pathTop, pathBot; 313 int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot); 314 int clipTop, clipBot; 315 int clipTransitions; 316 317 clipTransitions = clip.count_runtype_values(&clipTop, &clipBot); 318 319 int top = SkMax32(pathTop, clipTop); 320 int bot = SkMin32(pathBot, clipBot); 321 322 if (top >= bot) 323 return this->setEmpty(); 324 325 SkRgnBuilder builder; 326 327 if (!builder.init(bot - top, 328 SkMax32(pathTransitions, clipTransitions), 329 path.isInverseFillType())) { 330 // can't allocate working space, so return false 331 return this->setEmpty(); 332 } 333 334 SkScan::FillPath(path, clip, &builder); 335 builder.done(); 336 337 int count = builder.computeRunCount(); 338 if (count == 0) { 339 return this->setEmpty(); 340 } else if (count == kRectRegionRuns) { 341 builder.copyToRect(&fBounds); 342 this->setRect(fBounds); 343 } else { 344 SkRegion tmp; 345 346 tmp.fRunHead = RunHead::Alloc(count); 347 builder.copyToRgn(tmp.fRunHead->writable_runs()); 348 tmp.fRunHead->computeRunBounds(&tmp.fBounds); 349 this->swap(tmp); 350 } 351 SkDEBUGCODE(this->validate();) 352 return true; 353} 354 355///////////////////////////////////////////////////////////////////////////////////////////////// 356///////////////////////////////////////////////////////////////////////////////////////////////// 357 358struct Edge { 359 enum { 360 kY0Link = 0x01, 361 kY1Link = 0x02, 362 363 kCompleteLink = (kY0Link | kY1Link) 364 }; 365 366 SkRegion::RunType fX; 367 SkRegion::RunType fY0, fY1; 368 uint8_t fFlags; 369 Edge* fNext; 370 371 void set(int x, int y0, int y1) { 372 SkASSERT(y0 != y1); 373 374 fX = (SkRegion::RunType)(x); 375 fY0 = (SkRegion::RunType)(y0); 376 fY1 = (SkRegion::RunType)(y1); 377 fFlags = 0; 378 SkDEBUGCODE(fNext = NULL;) 379 } 380 381 int top() const { 382 return SkFastMin32(fY0, fY1); 383 } 384}; 385 386static void find_link(Edge* base, Edge* stop) { 387 SkASSERT(base < stop); 388 389 if (base->fFlags == Edge::kCompleteLink) { 390 SkASSERT(base->fNext); 391 return; 392 } 393 394 SkASSERT(base + 1 < stop); 395 396 int y0 = base->fY0; 397 int y1 = base->fY1; 398 399 Edge* e = base; 400 if ((base->fFlags & Edge::kY0Link) == 0) { 401 for (;;) { 402 e += 1; 403 if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) { 404 SkASSERT(NULL == e->fNext); 405 e->fNext = base; 406 e->fFlags = SkToU8(e->fFlags | Edge::kY1Link); 407 break; 408 } 409 } 410 } 411 412 e = base; 413 if ((base->fFlags & Edge::kY1Link) == 0) { 414 for (;;) { 415 e += 1; 416 if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) { 417 SkASSERT(NULL == base->fNext); 418 base->fNext = e; 419 e->fFlags = SkToU8(e->fFlags | Edge::kY0Link); 420 break; 421 } 422 } 423 } 424 425 base->fFlags = Edge::kCompleteLink; 426} 427 428static int extract_path(Edge* edge, Edge* stop, SkPath* path) { 429 while (0 == edge->fFlags) { 430 edge++; // skip over "used" edges 431 } 432 433 SkASSERT(edge < stop); 434 435 Edge* base = edge; 436 Edge* prev = edge; 437 edge = edge->fNext; 438 SkASSERT(edge != base); 439 440 int count = 1; 441 path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0)); 442 prev->fFlags = 0; 443 do { 444 if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear 445 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V 446 path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H 447 } 448 prev = edge; 449 edge = edge->fNext; 450 count += 1; 451 prev->fFlags = 0; 452 } while (edge != base); 453 path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V 454 path->close(); 455 return count; 456} 457 458#include "SkTSearch.h" 459 460static int EdgeProc(const Edge* a, const Edge* b) { 461 return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX; 462} 463 464bool SkRegion::getBoundaryPath(SkPath* path) const { 465 // path could safely be NULL if we're empty, but the caller shouldn't 466 // *know* that 467 SkASSERT(path); 468 469 if (this->isEmpty()) { 470 return false; 471 } 472 473 const SkIRect& bounds = this->getBounds(); 474 475 if (this->isRect()) { 476 SkRect r; 477 r.set(bounds); // this converts the ints to scalars 478 path->addRect(r); 479 return true; 480 } 481 482 SkRegion::Iterator iter(*this); 483 SkTDArray<Edge> edges; 484 485 for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) { 486 Edge* edge = edges.append(2); 487 edge[0].set(r.fLeft, r.fBottom, r.fTop); 488 edge[1].set(r.fRight, r.fTop, r.fBottom); 489 } 490 qsort(edges.begin(), edges.count(), sizeof(Edge), SkCastForQSort(EdgeProc)); 491 492 int count = edges.count(); 493 Edge* start = edges.begin(); 494 Edge* stop = start + count; 495 Edge* e; 496 497 for (e = start; e != stop; e++) { 498 find_link(e, stop); 499 } 500 501#ifdef SK_DEBUG 502 for (e = start; e != stop; e++) { 503 SkASSERT(e->fNext != NULL); 504 SkASSERT(e->fFlags == Edge::kCompleteLink); 505 } 506#endif 507 508 path->incReserve(count << 1); 509 do { 510 SkASSERT(count > 1); 511 count -= extract_path(start, stop, path); 512 } while (count > 0); 513 514 return true; 515} 516