SkPathOpsCommon.cpp revision 30b9fdd6a1d607bde20c793af65b5e2e8a1737ca
1/* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7#include "SkAddIntersections.h" 8#include "SkOpCoincidence.h" 9#include "SkOpEdgeBuilder.h" 10#include "SkPathOpsCommon.h" 11#include "SkPathWriter.h" 12#include "SkTSort.h" 13 14SkScalar ScaleFactor(const SkPath& path) { 15 static const SkScalar twoTo10 = 1024.f; 16 SkScalar largest = 0; 17 const SkScalar* oneBounds = &path.getBounds().fLeft; 18 for (int index = 0; index < 4; ++index) { 19 largest = SkTMax(largest, SkScalarAbs(oneBounds[index])); 20 } 21 SkScalar scale = twoTo10; 22 SkScalar next; 23 while ((next = scale * twoTo10) < largest) { 24 scale = next; 25 } 26 return scale == twoTo10 ? SK_Scalar1 : scale; 27} 28 29void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) { 30 SkMatrix matrix; 31 matrix.setScale(scale, scale); 32 *scaled = path; 33 scaled->transform(matrix); 34} 35 36const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr, 37 bool* sortablePtr) { 38 // find first angle, initialize winding to computed fWindSum 39 SkOpSegment* segment = start->segment(); 40 const SkOpAngle* angle = segment->spanToAngle(start, end); 41 if (!angle) { 42 *windingPtr = SK_MinS32; 43 return nullptr; 44 } 45 bool computeWinding = false; 46 const SkOpAngle* firstAngle = angle; 47 bool loop = false; 48 bool unorderable = false; 49 int winding = SK_MinS32; 50 do { 51 angle = angle->next(); 52 if (!angle) { 53 return nullptr; 54 } 55 unorderable |= angle->unorderable(); 56 if ((computeWinding = unorderable || (angle == firstAngle && loop))) { 57 break; // if we get here, there's no winding, loop is unorderable 58 } 59 loop |= angle == firstAngle; 60 segment = angle->segment(); 61 winding = segment->windSum(angle); 62 } while (winding == SK_MinS32); 63 // if the angle loop contains an unorderable span, the angle order may be useless 64 // directly compute the winding in this case for each span 65 if (computeWinding) { 66 firstAngle = angle; 67 winding = SK_MinS32; 68 do { 69 SkOpSpanBase* startSpan = angle->start(); 70 SkOpSpanBase* endSpan = angle->end(); 71 SkOpSpan* lesser = startSpan->starter(endSpan); 72 int testWinding = lesser->windSum(); 73 if (testWinding == SK_MinS32) { 74 testWinding = lesser->computeWindSum(); 75 } 76 if (testWinding != SK_MinS32) { 77 segment = angle->segment(); 78 winding = testWinding; 79 } 80 angle = angle->next(); 81 } while (angle != firstAngle); 82 } 83 *sortablePtr = !unorderable; 84 *windingPtr = winding; 85 return angle; 86} 87 88SkOpSegment* FindUndone(SkOpContourHead* contourList, SkOpSpanBase** startPtr, 89 SkOpSpanBase** endPtr) { 90 SkOpSegment* result; 91 SkOpContour* contour = contourList; 92 do { 93 result = contour->undoneSegment(startPtr, endPtr); 94 if (result) { 95 return result; 96 } 97 } while ((contour = contour->next())); 98 return nullptr; 99} 100 101SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr, 102 SkOpSpanBase** endPtr) { 103 while (chase->count()) { 104 SkOpSpanBase* span; 105 chase->pop(&span); 106 SkOpSegment* segment = span->segment(); 107 *startPtr = span->ptT()->next()->span(); 108 bool done = true; 109 *endPtr = nullptr; 110 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { 111 *startPtr = last->start(); 112 *endPtr = last->end(); 113 #if TRY_ROTATE 114 *chase->insert(0) = span; 115 #else 116 *chase->append() = span; 117 #endif 118 return last->segment(); 119 } 120 if (done) { 121 continue; 122 } 123 // find first angle, initialize winding to computed wind sum 124 int winding; 125 bool sortable; 126 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); 127 if (!angle) { 128 return nullptr; 129 } 130 if (winding == SK_MinS32) { 131 continue; 132 } 133 int sumWinding SK_INIT_TO_AVOID_WARNING; 134 if (sortable) { 135 segment = angle->segment(); 136 sumWinding = segment->updateWindingReverse(angle); 137 } 138 SkOpSegment* first = nullptr; 139 const SkOpAngle* firstAngle = angle; 140 while ((angle = angle->next()) != firstAngle) { 141 segment = angle->segment(); 142 SkOpSpanBase* start = angle->start(); 143 SkOpSpanBase* end = angle->end(); 144 int maxWinding SK_INIT_TO_AVOID_WARNING; 145 if (sortable) { 146 segment->setUpWinding(start, end, &maxWinding, &sumWinding); 147 } 148 if (!segment->done(angle)) { 149 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { 150 first = segment; 151 *startPtr = start; 152 *endPtr = end; 153 } 154 // OPTIMIZATION: should this also add to the chase? 155 if (sortable) { 156 (void) segment->markAngle(maxWinding, sumWinding, angle); 157 } 158 } 159 } 160 if (first) { 161 #if TRY_ROTATE 162 *chase->insert(0) = span; 163 #else 164 *chase->append() = span; 165 #endif 166 return first; 167 } 168 } 169 return nullptr; 170} 171 172bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) { 173 SkTDArray<SkOpContour* > list; 174 SkOpContour* contour = *contourList; 175 do { 176 if (contour->count()) { 177 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); 178 *list.append() = contour; 179 } 180 } while ((contour = contour->next())); 181 int count = list.count(); 182 if (!count) { 183 return false; 184 } 185 if (count > 1) { 186 SkTQSort<SkOpContour>(list.begin(), list.end() - 1); 187 } 188 contour = list[0]; 189 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour); 190 contour->globalState()->setContourHead(contourHead); 191 *contourList = contourHead; 192 for (int index = 1; index < count; ++index) { 193 SkOpContour* next = list[index]; 194 contour->setNext(next); 195 contour = next; 196 } 197 contour->setNext(nullptr); 198 return true; 199} 200 201class DistanceLessThan { 202public: 203 DistanceLessThan(double* distances) : fDistances(distances) { } 204 double* fDistances; 205 bool operator()(const int one, const int two) { 206 return fDistances[one] < fDistances[two]; 207 } 208}; 209 210 /* 211 check start and end of each contour 212 if not the same, record them 213 match them up 214 connect closest 215 reassemble contour pieces into new path 216 */ 217void Assemble(const SkPathWriter& path, SkPathWriter* simple) { 218 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune 219 SkOpContourHead contour; 220 SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false) 221 SkDEBUGPARAMS(nullptr)); 222#if DEBUG_SHOW_TEST_NAME 223 SkDebugf("</div>\n"); 224#endif 225#if DEBUG_PATH_CONSTRUCTION 226 SkDebugf("%s\n", __FUNCTION__); 227#endif 228 SkOpEdgeBuilder builder(path, &contour, &globalState); 229 builder.finish(); 230 SkTDArray<const SkOpContour* > runs; // indices of partial contours 231 const SkOpContour* eContour = builder.head(); 232 do { 233 if (!eContour->count()) { 234 continue; 235 } 236 const SkPoint& eStart = eContour->start(); 237 const SkPoint& eEnd = eContour->end(); 238#if DEBUG_ASSEMBLE 239 SkDebugf("%s contour", __FUNCTION__); 240 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) { 241 SkDebugf("[%d]", runs.count()); 242 } else { 243 SkDebugf(" "); 244 } 245 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n", 246 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY); 247#endif 248 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) { 249 eContour->toPath(simple); 250 continue; 251 } 252 *runs.append() = eContour; 253 } while ((eContour = eContour->next())); 254 int count = runs.count(); 255 if (count == 0) { 256 return; 257 } 258 SkTDArray<int> sLink, eLink; 259 sLink.append(count); 260 eLink.append(count); 261 int rIndex, iIndex; 262 for (rIndex = 0; rIndex < count; ++rIndex) { 263 sLink[rIndex] = eLink[rIndex] = SK_MaxS32; 264 } 265 const int ends = count * 2; // all starts and ends 266 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2 267 SkTDArray<double> distances; 268 distances.append(entries); 269 for (rIndex = 0; rIndex < ends - 1; ++rIndex) { 270 const SkOpContour* oContour = runs[rIndex >> 1]; 271 const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start(); 272 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2) 273 * ends - rIndex - 1; 274 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) { 275 const SkOpContour* iContour = runs[iIndex >> 1]; 276 const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start(); 277 double dx = iPt.fX - oPt.fX; 278 double dy = iPt.fY - oPt.fY; 279 double dist = dx * dx + dy * dy; 280 distances[row + iIndex] = dist; // oStart distance from iStart 281 } 282 } 283 SkTDArray<int> sortedDist; 284 sortedDist.append(entries); 285 for (rIndex = 0; rIndex < entries; ++rIndex) { 286 sortedDist[rIndex] = rIndex; 287 } 288 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin())); 289 int remaining = count; // number of start/end pairs 290 for (rIndex = 0; rIndex < entries; ++rIndex) { 291 int pair = sortedDist[rIndex]; 292 int row = pair / ends; 293 int col = pair - row * ends; 294 int thingOne = row < col ? row : ends - row - 2; 295 int ndxOne = thingOne >> 1; 296 bool endOne = thingOne & 1; 297 int* linkOne = endOne ? eLink.begin() : sLink.begin(); 298 if (linkOne[ndxOne] != SK_MaxS32) { 299 continue; 300 } 301 int thingTwo = row < col ? col : ends - row + col - 1; 302 int ndxTwo = thingTwo >> 1; 303 bool endTwo = thingTwo & 1; 304 int* linkTwo = endTwo ? eLink.begin() : sLink.begin(); 305 if (linkTwo[ndxTwo] != SK_MaxS32) { 306 continue; 307 } 308 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]); 309 bool flip = endOne == endTwo; 310 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo; 311 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne; 312 if (!--remaining) { 313 break; 314 } 315 } 316 SkASSERT(!remaining); 317#if DEBUG_ASSEMBLE 318 for (rIndex = 0; rIndex < count; ++rIndex) { 319 int s = sLink[rIndex]; 320 int e = eLink[rIndex]; 321 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e', 322 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e); 323 } 324#endif 325 rIndex = 0; 326 do { 327 bool forward = true; 328 bool first = true; 329 int sIndex = sLink[rIndex]; 330 SkASSERT(sIndex != SK_MaxS32); 331 sLink[rIndex] = SK_MaxS32; 332 int eIndex; 333 if (sIndex < 0) { 334 eIndex = sLink[~sIndex]; 335 sLink[~sIndex] = SK_MaxS32; 336 } else { 337 eIndex = eLink[sIndex]; 338 eLink[sIndex] = SK_MaxS32; 339 } 340 SkASSERT(eIndex != SK_MaxS32); 341#if DEBUG_ASSEMBLE 342 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e', 343 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e', 344 eIndex < 0 ? ~eIndex : eIndex); 345#endif 346 do { 347 const SkOpContour* contour = runs[rIndex]; 348 if (first) { 349 first = false; 350 const SkPoint* startPtr = &contour->start(); 351 simple->deferredMove(startPtr[0]); 352 } 353 if (forward) { 354 contour->toPartialForward(simple); 355 } else { 356 contour->toPartialBackward(simple); 357 } 358#if DEBUG_ASSEMBLE 359 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex, 360 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex, 361 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)); 362#endif 363 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) { 364 simple->close(); 365 break; 366 } 367 if (forward) { 368 eIndex = eLink[rIndex]; 369 SkASSERT(eIndex != SK_MaxS32); 370 eLink[rIndex] = SK_MaxS32; 371 if (eIndex >= 0) { 372 SkASSERT(sLink[eIndex] == rIndex); 373 sLink[eIndex] = SK_MaxS32; 374 } else { 375 SkASSERT(eLink[~eIndex] == ~rIndex); 376 eLink[~eIndex] = SK_MaxS32; 377 } 378 } else { 379 eIndex = sLink[rIndex]; 380 SkASSERT(eIndex != SK_MaxS32); 381 sLink[rIndex] = SK_MaxS32; 382 if (eIndex >= 0) { 383 SkASSERT(eLink[eIndex] == rIndex); 384 eLink[eIndex] = SK_MaxS32; 385 } else { 386 SkASSERT(sLink[~eIndex] == ~rIndex); 387 sLink[~eIndex] = SK_MaxS32; 388 } 389 } 390 rIndex = eIndex; 391 if (rIndex < 0) { 392 forward ^= 1; 393 rIndex = ~rIndex; 394 } 395 } while (true); 396 for (rIndex = 0; rIndex < count; ++rIndex) { 397 if (sLink[rIndex] != SK_MaxS32) { 398 break; 399 } 400 } 401 } while (rIndex < count); 402#if DEBUG_ASSEMBLE 403 for (rIndex = 0; rIndex < count; ++rIndex) { 404 SkASSERT(sLink[rIndex] == SK_MaxS32); 405 SkASSERT(eLink[rIndex] == SK_MaxS32); 406 } 407#endif 408} 409 410static void calcAngles(SkOpContourHead* contourList) { 411 SkOpContour* contour = contourList; 412 do { 413 contour->calcAngles(); 414 } while ((contour = contour->next())); 415} 416 417static bool missingCoincidence(SkOpContourHead* contourList) { 418 SkOpContour* contour = contourList; 419 bool result = false; 420 do { 421 result |= contour->missingCoincidence(); 422 } while ((contour = contour->next())); 423 return result; 424} 425 426static bool moveMultiples(SkOpContourHead* contourList) { 427 SkOpContour* contour = contourList; 428 do { 429 if (!contour->moveMultiples()) { 430 return false; 431 } 432 } while ((contour = contour->next())); 433 return true; 434} 435 436static void moveNearby(SkOpContourHead* contourList) { 437 SkOpContour* contour = contourList; 438 do { 439 contour->moveNearby(); 440 } while ((contour = contour->next())); 441} 442 443static void sortAngles(SkOpContourHead* contourList) { 444 SkOpContour* contour = contourList; 445 do { 446 contour->sortAngles(); 447 } while ((contour = contour->next())); 448} 449 450bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) { 451 SkOpGlobalState* globalState = contourList->globalState(); 452 DEBUG_COINCIDENCE_HEALTH(contourList, "start"); 453#if DEBUG_VALIDATE 454 globalState->setPhase(SkOpGlobalState::kIntersecting); 455#endif 456 457 // match up points within the coincident runs 458 if (!coincidence->addExpanded()) { 459 return false; 460 } 461 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded"); 462#if DEBUG_VALIDATE 463 globalState->setPhase(SkOpGlobalState::kWalking); 464#endif 465 // combine t values when multiple intersections occur on some segments but not others 466 if (!moveMultiples(contourList)) { 467 return false; 468 } 469 DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples"); 470 // move t values and points together to eliminate small/tiny gaps 471 (void) moveNearby(contourList); 472 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby"); 473#if DEBUG_VALIDATE 474 globalState->setPhase(SkOpGlobalState::kIntersecting); 475#endif 476 // add coincidence formed by pairing on curve points and endpoints 477 coincidence->correctEnds(); 478 if (!coincidence->addEndMovedSpans()) { 479 return false; 480 } 481 DEBUG_COINCIDENCE_HEALTH(contourList, "addEndMovedSpans"); 482 483 const int SAFETY_COUNT = 100; // FIXME: tune 484 int safetyHatch = SAFETY_COUNT; 485 // look for coincidence present in A-B and A-C but missing in B-C 486 while (coincidence->addMissing()) { 487 if (!--safetyHatch) { 488 SkASSERT(globalState->debugSkipAssert()); 489 return false; 490 } 491 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing"); 492 moveNearby(contourList); 493 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby"); 494 } 495 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2"); 496 // FIXME: only call this if addMissing modified something when returning false 497 moveNearby(contourList); 498 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2"); 499 // check to see if, loosely, coincident ranges may be expanded 500 if (coincidence->expand()) { 501 DEBUG_COINCIDENCE_HEALTH(contourList, "expand1"); 502 coincidence->addMissing(); 503 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2"); 504 if (!coincidence->addExpanded()) { 505 return false; 506 } 507 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2"); 508 if (!moveMultiples(contourList)) { 509 return false; 510 } 511 DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples2"); 512 moveNearby(contourList); 513 } 514#if DEBUG_VALIDATE 515 globalState->setPhase(SkOpGlobalState::kWalking); 516#endif 517 DEBUG_COINCIDENCE_HEALTH(contourList, "expand2"); 518 // the expanded ranges may not align -- add the missing spans 519 if (!coincidence->addExpanded()) { 520 return false; 521 } 522 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3"); 523 coincidence->correctEnds(); 524 if (!coincidence->mark()) { // mark spans of coincident segments as coincident 525 return false; 526 } 527 DEBUG_COINCIDENCE_HEALTH(contourList, "mark1"); 528 // look for coincidence lines and curves undetected by intersection 529 if (missingCoincidence(contourList)) { 530#if DEBUG_VALIDATE 531 globalState->setPhase(SkOpGlobalState::kIntersecting); 532#endif 533 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1"); 534 (void) coincidence->expand(); 535 DEBUG_COINCIDENCE_HEALTH(contourList, "expand3"); 536 if (!coincidence->addExpanded()) { 537 return false; 538 } 539#if DEBUG_VALIDATE 540 globalState->setPhase(SkOpGlobalState::kWalking); 541#endif 542 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3"); 543 if (!coincidence->mark()) { 544 return false; 545 } 546 } else { 547 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2"); 548 (void) coincidence->expand(); 549 } 550 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence3"); 551 552 (void) coincidence->expand(); 553 554#if 0 // under development 555 // coincident runs may cross two or more spans, but the opposite spans may be out of order 556 if (!coincidence->reorder()) { 557 return false; 558 } 559#endif 560 DEBUG_COINCIDENCE_HEALTH(contourList, "coincidence.reorder"); 561 SkOpCoincidence overlaps(globalState); 562 do { 563 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps; 564 if (!pairs->apply()) { // adjust the winding value to account for coincident edges 565 return false; 566 } 567 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply"); 568 // For each coincident pair that overlaps another, when the receivers (the 1st of the pair) 569 // are different, construct a new pair to resolve their mutual span 570 if (!pairs->findOverlaps(&overlaps)) { 571 return false; 572 } 573 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps"); 574 } while (!overlaps.isEmpty()); 575 calcAngles(contourList); 576 sortAngles(contourList); 577 if (globalState->angleCoincidence()) { 578 (void) missingCoincidence(contourList); 579 if (!coincidence->apply()) { 580 return false; 581 } 582 } 583#if DEBUG_COINCIDENCE_VERBOSE 584 coincidence->debugShowCoincidence(); 585#endif 586#if DEBUG_COINCIDENCE 587 coincidence->debugValidate(); 588#endif 589 SkPathOpsDebug::ShowActiveSpans(contourList); 590 return true; 591} 592