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 |