| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 #include "SkAddIntersections.h" | 7 #include "SkAddIntersections.h" |
| 8 #include "SkOpCoincidence.h" | 8 #include "SkOpCoincidence.h" |
| 9 #include "SkOpEdgeBuilder.h" | 9 #include "SkOpEdgeBuilder.h" |
| 10 #include "SkPathOpsCommon.h" | 10 #include "SkPathOpsCommon.h" |
| 11 #include "SkPathWriter.h" | 11 #include "SkPathWriter.h" |
| 12 #include "SkTSort.h" | 12 #include "SkTSort.h" |
| 13 | 13 |
| 14 SkScalar 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 |
| 29 void 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 |
| 14 const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windi
ngPtr, | 36 const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windi
ngPtr, |
| 15 bool* sortablePtr) { | 37 bool* sortablePtr) { |
| 16 // find first angle, initialize winding to computed fWindSum | 38 // find first angle, initialize winding to computed fWindSum |
| 17 SkOpSegment* segment = start->segment(); | 39 SkOpSegment* segment = start->segment(); |
| 18 const SkOpAngle* angle = segment->spanToAngle(start, end); | 40 const SkOpAngle* angle = segment->spanToAngle(start, end); |
| 19 if (!angle) { | 41 if (!angle) { |
| 20 *windingPtr = SK_MinS32; | 42 *windingPtr = SK_MinS32; |
| 21 return nullptr; | 43 return nullptr; |
| 22 } | 44 } |
| 23 bool computeWinding = false; | 45 bool computeWinding = false; |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 *chase->insert(0) = span; | 159 *chase->insert(0) = span; |
| 138 #else | 160 #else |
| 139 *chase->append() = span; | 161 *chase->append() = span; |
| 140 #endif | 162 #endif |
| 141 return first; | 163 return first; |
| 142 } | 164 } |
| 143 } | 165 } |
| 144 return nullptr; | 166 return nullptr; |
| 145 } | 167 } |
| 146 | 168 |
| 147 #if DEBUG_ACTIVE_SPANS | |
| 148 void DebugShowActiveSpans(SkOpContourHead* contourList) { | |
| 149 SkOpContour* contour = contourList; | |
| 150 do { | |
| 151 contour->debugShowActiveSpans(); | |
| 152 } while ((contour = contour->next())); | |
| 153 } | |
| 154 #endif | |
| 155 | |
| 156 bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOd
d) { | 169 bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOd
d) { |
| 157 SkTDArray<SkOpContour* > list; | 170 SkTDArray<SkOpContour* > list; |
| 158 SkOpContour* contour = *contourList; | 171 SkOpContour* contour = *contourList; |
| 159 do { | 172 do { |
| 160 if (contour->count()) { | 173 if (contour->count()) { |
| 161 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); | 174 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); |
| 162 *list.append() = contour; | 175 *list.append() = contour; |
| 163 } | 176 } |
| 164 } while ((contour = contour->next())); | 177 } while ((contour = contour->next())); |
| 165 int count = list.count(); | 178 int count = list.count(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 194 /* | 207 /* |
| 195 check start and end of each contour | 208 check start and end of each contour |
| 196 if not the same, record them | 209 if not the same, record them |
| 197 match them up | 210 match them up |
| 198 connect closest | 211 connect closest |
| 199 reassemble contour pieces into new path | 212 reassemble contour pieces into new path |
| 200 */ | 213 */ |
| 201 void Assemble(const SkPathWriter& path, SkPathWriter* simple) { | 214 void Assemble(const SkPathWriter& path, SkPathWriter* simple) { |
| 202 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune | 215 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune |
| 203 SkOpContourHead contour; | 216 SkOpContourHead contour; |
| 204 SkOpGlobalState globalState(nullptr, &contour SkDEBUGPARAMS(false) | 217 SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false) |
| 205 SkDEBUGPARAMS(nullptr)); | 218 SkDEBUGPARAMS(nullptr)); |
| 206 #if DEBUG_SHOW_TEST_NAME | 219 #if DEBUG_SHOW_TEST_NAME |
| 207 SkDebugf("</div>\n"); | 220 SkDebugf("</div>\n"); |
| 208 #endif | 221 #endif |
| 209 #if DEBUG_PATH_CONSTRUCTION | 222 #if DEBUG_PATH_CONSTRUCTION |
| 210 SkDebugf("%s\n", __FUNCTION__); | 223 SkDebugf("%s\n", __FUNCTION__); |
| 211 #endif | 224 #endif |
| 212 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState); | 225 SkOpEdgeBuilder builder(path, &contour, &globalState); |
| 213 builder.finish(&allocator); | 226 builder.finish(); |
| 214 SkTDArray<const SkOpContour* > runs; // indices of partial contours | 227 SkTDArray<const SkOpContour* > runs; // indices of partial contours |
| 215 const SkOpContour* eContour = builder.head(); | 228 const SkOpContour* eContour = builder.head(); |
| 216 do { | 229 do { |
| 217 if (!eContour->count()) { | 230 if (!eContour->count()) { |
| 218 continue; | 231 continue; |
| 219 } | 232 } |
| 220 const SkPoint& eStart = eContour->start(); | 233 const SkPoint& eStart = eContour->start(); |
| 221 const SkPoint& eEnd = eContour->end(); | 234 const SkPoint& eEnd = eContour->end(); |
| 222 #if DEBUG_ASSEMBLE | 235 #if DEBUG_ASSEMBLE |
| 223 SkDebugf("%s contour", __FUNCTION__); | 236 SkDebugf("%s contour", __FUNCTION__); |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 } | 397 } |
| 385 } while (rIndex < count); | 398 } while (rIndex < count); |
| 386 #if DEBUG_ASSEMBLE | 399 #if DEBUG_ASSEMBLE |
| 387 for (rIndex = 0; rIndex < count; ++rIndex) { | 400 for (rIndex = 0; rIndex < count; ++rIndex) { |
| 388 SkASSERT(sLink[rIndex] == SK_MaxS32); | 401 SkASSERT(sLink[rIndex] == SK_MaxS32); |
| 389 SkASSERT(eLink[rIndex] == SK_MaxS32); | 402 SkASSERT(eLink[rIndex] == SK_MaxS32); |
| 390 } | 403 } |
| 391 #endif | 404 #endif |
| 392 } | 405 } |
| 393 | 406 |
| 394 static void align(SkOpContourHead* contourList) { | 407 static void calcAngles(SkOpContourHead* contourList) { |
| 395 SkOpContour* contour = contourList; | 408 SkOpContour* contour = contourList; |
| 396 do { | 409 do { |
| 397 contour->align(); | 410 contour->calcAngles(); |
| 398 } while ((contour = contour->next())); | 411 } while ((contour = contour->next())); |
| 399 } | 412 } |
| 400 | 413 |
| 401 static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* al
locator) { | 414 static bool missingCoincidence(SkOpContourHead* contourList) { |
| 402 SkOpContour* contour = contourList; | |
| 403 do { | |
| 404 contour->addAlignIntersections(contourList, allocator); | |
| 405 } while ((contour = contour->next())); | |
| 406 } | |
| 407 | |
| 408 static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) { | |
| 409 SkOpContour* contour = contourList; | |
| 410 do { | |
| 411 contour->calcAngles(allocator); | |
| 412 } while ((contour = contour->next())); | |
| 413 } | |
| 414 | |
| 415 static void findCollapsed(SkOpContourHead* contourList) { | |
| 416 SkOpContour* contour = contourList; | |
| 417 do { | |
| 418 contour->findCollapsed(); | |
| 419 } while ((contour = contour->next())); | |
| 420 } | |
| 421 | |
| 422 static bool missingCoincidence(SkOpContourHead* contourList, | |
| 423 SkOpCoincidence* coincidence, SkChunkAlloc* allocator) { | |
| 424 SkOpContour* contour = contourList; | 415 SkOpContour* contour = contourList; |
| 425 bool result = false; | 416 bool result = false; |
| 426 do { | 417 do { |
| 427 result |= contour->missingCoincidence(coincidence, allocator); | 418 result |= contour->missingCoincidence(); |
| 428 } while ((contour = contour->next())); | 419 } while ((contour = contour->next())); |
| 429 return result; | 420 return result; |
| 430 } | 421 } |
| 431 | 422 |
| 432 static bool moveMultiples(SkOpContourHead* contourList) { | 423 static bool moveMultiples(SkOpContourHead* contourList) { |
| 433 SkOpContour* contour = contourList; | 424 SkOpContour* contour = contourList; |
| 434 do { | 425 do { |
| 435 if (!contour->moveMultiples()) { | 426 if (!contour->moveMultiples()) { |
| 436 return false; | 427 return false; |
| 437 } | 428 } |
| 438 } while ((contour = contour->next())); | 429 } while ((contour = contour->next())); |
| 439 return true; | 430 return true; |
| 440 } | 431 } |
| 441 | 432 |
| 442 static void moveNearby(SkOpContourHead* contourList) { | 433 static void moveNearby(SkOpContourHead* contourList) { |
| 443 SkOpContour* contour = contourList; | 434 SkOpContour* contour = contourList; |
| 444 do { | 435 do { |
| 445 contour->moveNearby(); | 436 contour->moveNearby(); |
| 446 } while ((contour = contour->next())); | 437 } while ((contour = contour->next())); |
| 447 } | 438 } |
| 448 | 439 |
| 449 static void sortAngles(SkOpContourHead* contourList) { | 440 static void sortAngles(SkOpContourHead* contourList) { |
| 450 SkOpContour* contour = contourList; | 441 SkOpContour* contour = contourList; |
| 451 do { | 442 do { |
| 452 contour->sortAngles(); | 443 contour->sortAngles(); |
| 453 } while ((contour = contour->next())); | 444 } while ((contour = contour->next())); |
| 454 } | 445 } |
| 455 | 446 |
| 456 bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
e, | 447 bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
e) { |
| 457 SkChunkAlloc* allocator) { | |
| 458 SkOpGlobalState* globalState = contourList->globalState(); | 448 SkOpGlobalState* globalState = contourList->globalState(); |
| 449 DEBUG_COINCIDENCE_HEALTH(contourList, "start"); |
| 450 #if DEBUG_VALIDATE |
| 451 globalState->setPhase(SkOpGlobalState::kIntersecting); |
| 452 #endif |
| 453 |
| 454 // match up points within the coincident runs |
| 455 if (!coincidence->addExpanded()) { |
| 456 return false; |
| 457 } |
| 458 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded"); |
| 459 #if DEBUG_VALIDATE |
| 460 globalState->setPhase(SkOpGlobalState::kWalking); |
| 461 #endif |
| 459 // combine t values when multiple intersections occur on some segments but n
ot others | 462 // combine t values when multiple intersections occur on some segments but n
ot others |
| 460 DEBUG_COINCIDENCE_HEALTH(contourList, "start"); | |
| 461 if (!moveMultiples(contourList)) { | 463 if (!moveMultiples(contourList)) { |
| 462 return false; | 464 return false; |
| 463 } | 465 } |
| 464 DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples"); | 466 DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples"); |
| 465 findCollapsed(contourList); | |
| 466 DEBUG_COINCIDENCE_HEALTH(contourList, "findCollapsed"); | |
| 467 // move t values and points together to eliminate small/tiny gaps | 467 // move t values and points together to eliminate small/tiny gaps |
| 468 moveNearby(contourList); | 468 (void) moveNearby(contourList); |
| 469 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby"); | 469 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby"); |
| 470 align(contourList); // give all span members common values | |
| 471 DEBUG_COINCIDENCE_HEALTH(contourList, "align"); | |
| 472 if (!coincidence->fixAligned()) { // aligning may have marked a coincidence
pt-t deleted | |
| 473 return false; | |
| 474 } | |
| 475 DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned"); | |
| 476 #if DEBUG_VALIDATE | 470 #if DEBUG_VALIDATE |
| 477 globalState->setPhase(SkOpGlobalState::kIntersecting); | 471 globalState->setPhase(SkOpGlobalState::kIntersecting); |
| 478 #endif | 472 #endif |
| 479 // look for intersections on line segments formed by moving end points | 473 // add coincidence formed by pairing on curve points and endpoints |
| 480 addAlignIntersections(contourList, allocator); | 474 coincidence->correctEnds(); |
| 481 DEBUG_COINCIDENCE_HEALTH(contourList, "addAlignIntersections"); | 475 if (!coincidence->addEndMovedSpans()) { |
| 482 if (coincidence->addMissing(allocator)) { | 476 return false; |
| 477 } |
| 478 DEBUG_COINCIDENCE_HEALTH(contourList, "addEndMovedSpans"); |
| 479 |
| 480 const int SAFETY_COUNT = 100; // FIXME: tune |
| 481 int safetyHatch = SAFETY_COUNT; |
| 482 // look for coincidence present in A-B and A-C but missing in B-C |
| 483 while (coincidence->addMissing()) { |
| 484 if (!--safetyHatch) { |
| 485 SkASSERT(0); // FIXME: take this out after verifying std tests don'
t trigger |
| 486 return false; |
| 487 } |
| 483 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing"); | 488 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing"); |
| 484 moveNearby(contourList); | 489 moveNearby(contourList); |
| 485 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2"); | 490 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby"); |
| 486 align(contourList); // give all span members common values | 491 } |
| 487 DEBUG_COINCIDENCE_HEALTH(contourList, "align2"); | 492 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2"); |
| 488 if (!coincidence->fixAligned()) { // aligning may have marked a coincid
ence pt-t deleted | 493 // FIXME: only call this if addMissing modified something when returning fal
se |
| 494 moveNearby(contourList); |
| 495 DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2"); |
| 496 // check to see if, loosely, coincident ranges may be expanded |
| 497 if (coincidence->expand()) { |
| 498 DEBUG_COINCIDENCE_HEALTH(contourList, "expand1"); |
| 499 coincidence->addMissing(); |
| 500 DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2"); |
| 501 if (!coincidence->addExpanded()) { |
| 489 return false; | 502 return false; |
| 490 } | 503 } |
| 491 DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2"); | 504 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2"); |
| 505 if (!moveMultiples(contourList)) { |
| 506 return false; |
| 507 } |
| 508 DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples2"); |
| 509 moveNearby(contourList); |
| 492 } | 510 } |
| 493 #if DEBUG_VALIDATE | 511 #if DEBUG_VALIDATE |
| 494 globalState->setPhase(SkOpGlobalState::kWalking); | 512 globalState->setPhase(SkOpGlobalState::kWalking); |
| 495 #endif | 513 #endif |
| 496 // check to see if, loosely, coincident ranges may be expanded | |
| 497 if (coincidence->expand()) { | |
| 498 DEBUG_COINCIDENCE_HEALTH(contourList, "expand1"); | |
| 499 if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(
globalState))) { | |
| 500 return false; | |
| 501 } | |
| 502 } | |
| 503 DEBUG_COINCIDENCE_HEALTH(contourList, "expand2"); | 514 DEBUG_COINCIDENCE_HEALTH(contourList, "expand2"); |
| 504 // the expanded ranges may not align -- add the missing spans | 515 // the expanded ranges may not align -- add the missing spans |
| 516 SkAssertResult(coincidence->addExpanded()); |
| 517 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3"); |
| 518 coincidence->correctEnds(); |
| 505 if (!coincidence->mark()) { // mark spans of coincident segments as coincid
ent | 519 if (!coincidence->mark()) { // mark spans of coincident segments as coincid
ent |
| 506 return false; | 520 return false; |
| 507 } | 521 } |
| 508 DEBUG_COINCIDENCE_HEALTH(contourList, "mark1"); | 522 DEBUG_COINCIDENCE_HEALTH(contourList, "mark1"); |
| 509 // look for coincidence missed earlier | 523 // look for coincidence lines and curves undetected by intersection |
| 510 if (missingCoincidence(contourList, coincidence, allocator)) { | 524 if (missingCoincidence(contourList)) { |
| 525 #if DEBUG_VALIDATE |
| 526 globalState->setPhase(SkOpGlobalState::kIntersecting); |
| 527 #endif |
| 511 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1"); | 528 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1"); |
| 512 (void) coincidence->expand(); | 529 (void) coincidence->expand(); |
| 513 DEBUG_COINCIDENCE_HEALTH(contourList, "expand3"); | 530 DEBUG_COINCIDENCE_HEALTH(contourList, "expand3"); |
| 514 if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(
globalState))) { | 531 if (!coincidence->addExpanded()) { |
| 515 return false; | 532 return false; |
| 516 } | 533 } |
| 517 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2"); | 534 #if DEBUG_VALIDATE |
| 518 coincidence->mark(); | 535 globalState->setPhase(SkOpGlobalState::kWalking); |
| 536 #endif |
| 537 DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3"); |
| 538 if (!coincidence->mark()) { |
| 539 return false; |
| 540 } |
| 541 } else { |
| 542 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2"); |
| 543 (void) coincidence->expand(); |
| 519 } | 544 } |
| 520 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2"); | 545 DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence3"); |
| 521 SkOpCoincidence overlaps; | 546 |
| 547 (void) coincidence->expand(); |
| 548 |
| 549 #if 0 // under development |
| 550 // coincident runs may cross two or more spans, but the opposite spans may b
e out of order |
| 551 if (!coincidence->reorder()) { |
| 552 return false; |
| 553 } |
| 554 #endif |
| 555 DEBUG_COINCIDENCE_HEALTH(contourList, "coincidence.reorder"); |
| 556 SkOpCoincidence overlaps(globalState); |
| 522 do { | 557 do { |
| 523 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps; | 558 SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps; |
| 524 if (!pairs->apply()) { // adjust the winding value to account for coinc
ident edges | 559 if (!pairs->apply()) { // adjust the winding value to account for coinc
ident edges |
| 525 return false; | 560 return false; |
| 526 } | 561 } |
| 527 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply"); | 562 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply"); |
| 528 // For each coincident pair that overlaps another, when the receivers (t
he 1st of the pair) | 563 // For each coincident pair that overlaps another, when the receivers (t
he 1st of the pair) |
| 529 // are different, construct a new pair to resolve their mutual span | 564 // are different, construct a new pair to resolve their mutual span |
| 530 if (!pairs->findOverlaps(&overlaps, allocator)) { | 565 if (!pairs->findOverlaps(&overlaps)) { |
| 531 return false; | 566 return false; |
| 532 } | 567 } |
| 533 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps"); | 568 DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps"); |
| 534 } while (!overlaps.isEmpty()); | 569 } while (!overlaps.isEmpty()); |
| 535 calcAngles(contourList, allocator); | 570 calcAngles(contourList); |
| 536 sortAngles(contourList); | 571 sortAngles(contourList); |
| 537 if (globalState->angleCoincidence()) { | 572 if (globalState->angleCoincidence()) { |
| 538 (void) missingCoincidence(contourList, coincidence, allocator); | 573 (void) missingCoincidence(contourList); |
| 539 if (!coincidence->apply()) { | 574 if (!coincidence->apply()) { |
| 540 return false; | 575 return false; |
| 541 } | 576 } |
| 542 } | 577 } |
| 543 #if DEBUG_ACTIVE_SPANS | 578 #if DEBUG_COINCIDENCE_VERBOSE |
| 544 coincidence->debugShowCoincidence(); | 579 coincidence->debugShowCoincidence(); |
| 545 DebugShowActiveSpans(contourList); | |
| 546 #endif | 580 #endif |
| 581 #if DEBUG_COINCIDENCE |
| 582 coincidence->debugValidate(); |
| 583 #endif |
| 584 SkPathOpsDebug::ShowActiveSpans(contourList); |
| 547 return true; | 585 return true; |
| 548 } | 586 } |
| OLD | NEW |