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 14 const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr, 15 bool* sortablePtr) { 16 // find first angle, initialize winding to computed fWindSum 17 SkOpSegment* segment = start->segment(); 18 const SkOpAngle* angle = segment->spanToAngle(start, end); 19 if (!angle) { 20 *windingPtr = SK_MinS32; 21 return NULL; 22 } 23 bool computeWinding = false; 24 const SkOpAngle* firstAngle = angle; 25 bool loop = false; 26 bool unorderable = false; 27 int winding = SK_MinS32; 28 do { 29 angle = angle->next(); 30 unorderable |= angle->unorderable(); 31 if ((computeWinding = unorderable || (angle == firstAngle && loop))) { 32 break; // if we get here, there's no winding, loop is unorderable 33 } 34 loop |= angle == firstAngle; 35 segment = angle->segment(); 36 winding = segment->windSum(angle); 37 } while (winding == SK_MinS32); 38 // if the angle loop contains an unorderable span, the angle order may be useless 39 // directly compute the winding in this case for each span 40 if (computeWinding) { 41 firstAngle = angle; 42 winding = SK_MinS32; 43 do { 44 SkOpSpanBase* startSpan = angle->start(); 45 SkOpSpanBase* endSpan = angle->end(); 46 SkOpSpan* lesser = startSpan->starter(endSpan); 47 int testWinding = lesser->windSum(); 48 if (testWinding == SK_MinS32) { 49 testWinding = lesser->computeWindSum(); 50 } 51 if (testWinding != SK_MinS32) { 52 segment = angle->segment(); 53 winding = testWinding; 54 } 55 angle = angle->next(); 56 } while (angle != firstAngle); 57 } 58 *sortablePtr = !unorderable; 59 *windingPtr = winding; 60 return angle; 61 } 62 63 SkOpSegment* FindUndone(SkOpContourHead* contourList, SkOpSpanBase** startPtr, 64 SkOpSpanBase** endPtr) { 65 SkOpSegment* result; 66 SkOpContour* contour = contourList; 67 do { 68 result = contour->undoneSegment(startPtr, endPtr); 69 if (result) { 70 return result; 71 } 72 } while ((contour = contour->next())); 73 return NULL; 74 } 75 76 SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr, 77 SkOpSpanBase** endPtr) { 78 while (chase->count()) { 79 SkOpSpanBase* span; 80 chase->pop(&span); 81 SkOpSegment* segment = span->segment(); 82 *startPtr = span->ptT()->next()->span(); 83 bool done = true; 84 *endPtr = NULL; 85 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { 86 *startPtr = last->start(); 87 *endPtr = last->end(); 88 #if TRY_ROTATE 89 *chase->insert(0) = span; 90 #else 91 *chase->append() = span; 92 #endif 93 return last->segment(); 94 } 95 if (done) { 96 continue; 97 } 98 // find first angle, initialize winding to computed wind sum 99 int winding; 100 bool sortable; 101 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable); 102 if (winding == SK_MinS32) { 103 continue; 104 } 105 int sumWinding SK_INIT_TO_AVOID_WARNING; 106 if (sortable) { 107 segment = angle->segment(); 108 sumWinding = segment->updateWindingReverse(angle); 109 } 110 SkOpSegment* first = NULL; 111 const SkOpAngle* firstAngle = angle; 112 while ((angle = angle->next()) != firstAngle) { 113 segment = angle->segment(); 114 SkOpSpanBase* start = angle->start(); 115 SkOpSpanBase* end = angle->end(); 116 int maxWinding; 117 if (sortable) { 118 segment->setUpWinding(start, end, &maxWinding, &sumWinding); 119 } 120 if (!segment->done(angle)) { 121 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) { 122 first = segment; 123 *startPtr = start; 124 *endPtr = end; 125 } 126 // OPTIMIZATION: should this also add to the chase? 127 if (sortable) { 128 (void) segment->markAngle(maxWinding, sumWinding, angle); 129 } 130 } 131 } 132 if (first) { 133 #if TRY_ROTATE 134 *chase->insert(0) = span; 135 #else 136 *chase->append() = span; 137 #endif 138 return first; 139 } 140 } 141 return NULL; 142 } 143 144 #if DEBUG_ACTIVE_SPANS 145 void DebugShowActiveSpans(SkOpContourHead* contourList) { 146 SkOpContour* contour = contourList; 147 do { 148 contour->debugShowActiveSpans(); 149 } while ((contour = contour->next())); 150 } 151 #endif 152 153 bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) { 154 SkTDArray<SkOpContour* > list; 155 SkOpContour* contour = *contourList; 156 do { 157 if (contour->count()) { 158 contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd); 159 *list.append() = contour; 160 } 161 } while ((contour = contour->next())); 162 int count = list.count(); 163 if (!count) { 164 return false; 165 } 166 if (count > 1) { 167 SkTQSort<SkOpContour>(list.begin(), list.end() - 1); 168 } 169 contour = list[0]; 170 SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour); 171 contour->globalState()->setContourHead(contourHead); 172 *contourList = contourHead; 173 for (int index = 1; index < count; ++index) { 174 SkOpContour* next = list[index]; 175 contour->setNext(next); 176 contour = next; 177 } 178 contour->setNext(NULL); 179 return true; 180 } 181 182 class DistanceLessThan { 183 public: 184 DistanceLessThan(double* distances) : fDistances(distances) { } 185 double* fDistances; 186 bool operator()(const int one, const int two) { 187 return fDistances[one] < fDistances[two]; 188 } 189 }; 190 191 /* 192 check start and end of each contour 193 if not the same, record them 194 match them up 195 connect closest 196 reassemble contour pieces into new path 197 */ 198 void Assemble(const SkPathWriter& path, SkPathWriter* simple) { 199 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune 200 SkOpContourHead contour; 201 SkOpGlobalState globalState(NULL, &contour); 202 #if DEBUG_SHOW_TEST_NAME 203 SkDebugf("</div>\n"); 204 #endif 205 #if DEBUG_PATH_CONSTRUCTION 206 SkDebugf("%s\n", __FUNCTION__); 207 #endif 208 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState); 209 builder.finish(&allocator); 210 SkTDArray<const SkOpContour* > runs; // indices of partial contours 211 const SkOpContour* eContour = builder.head(); 212 do { 213 if (!eContour->count()) { 214 continue; 215 } 216 const SkPoint& eStart = eContour->start(); 217 const SkPoint& eEnd = eContour->end(); 218 #if DEBUG_ASSEMBLE 219 SkDebugf("%s contour", __FUNCTION__); 220 if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) { 221 SkDebugf("[%d]", runs.count()); 222 } else { 223 SkDebugf(" "); 224 } 225 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n", 226 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY); 227 #endif 228 if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) { 229 eContour->toPath(simple); 230 continue; 231 } 232 *runs.append() = eContour; 233 } while ((eContour = eContour->next())); 234 int count = runs.count(); 235 if (count == 0) { 236 return; 237 } 238 SkTDArray<int> sLink, eLink; 239 sLink.append(count); 240 eLink.append(count); 241 int rIndex, iIndex; 242 for (rIndex = 0; rIndex < count; ++rIndex) { 243 sLink[rIndex] = eLink[rIndex] = SK_MaxS32; 244 } 245 const int ends = count * 2; // all starts and ends 246 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2 247 SkTDArray<double> distances; 248 distances.append(entries); 249 for (rIndex = 0; rIndex < ends - 1; ++rIndex) { 250 const SkOpContour* oContour = runs[rIndex >> 1]; 251 const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start(); 252 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2) 253 * ends - rIndex - 1; 254 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) { 255 const SkOpContour* iContour = runs[iIndex >> 1]; 256 const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start(); 257 double dx = iPt.fX - oPt.fX; 258 double dy = iPt.fY - oPt.fY; 259 double dist = dx * dx + dy * dy; 260 distances[row + iIndex] = dist; // oStart distance from iStart 261 } 262 } 263 SkTDArray<int> sortedDist; 264 sortedDist.append(entries); 265 for (rIndex = 0; rIndex < entries; ++rIndex) { 266 sortedDist[rIndex] = rIndex; 267 } 268 SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin())); 269 int remaining = count; // number of start/end pairs 270 for (rIndex = 0; rIndex < entries; ++rIndex) { 271 int pair = sortedDist[rIndex]; 272 int row = pair / ends; 273 int col = pair - row * ends; 274 int thingOne = row < col ? row : ends - row - 2; 275 int ndxOne = thingOne >> 1; 276 bool endOne = thingOne & 1; 277 int* linkOne = endOne ? eLink.begin() : sLink.begin(); 278 if (linkOne[ndxOne] != SK_MaxS32) { 279 continue; 280 } 281 int thingTwo = row < col ? col : ends - row + col - 1; 282 int ndxTwo = thingTwo >> 1; 283 bool endTwo = thingTwo & 1; 284 int* linkTwo = endTwo ? eLink.begin() : sLink.begin(); 285 if (linkTwo[ndxTwo] != SK_MaxS32) { 286 continue; 287 } 288 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]); 289 bool flip = endOne == endTwo; 290 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo; 291 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne; 292 if (!--remaining) { 293 break; 294 } 295 } 296 SkASSERT(!remaining); 297 #if DEBUG_ASSEMBLE 298 for (rIndex = 0; rIndex < count; ++rIndex) { 299 int s = sLink[rIndex]; 300 int e = eLink[rIndex]; 301 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e', 302 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e); 303 } 304 #endif 305 rIndex = 0; 306 do { 307 bool forward = true; 308 bool first = true; 309 int sIndex = sLink[rIndex]; 310 SkASSERT(sIndex != SK_MaxS32); 311 sLink[rIndex] = SK_MaxS32; 312 int eIndex; 313 if (sIndex < 0) { 314 eIndex = sLink[~sIndex]; 315 sLink[~sIndex] = SK_MaxS32; 316 } else { 317 eIndex = eLink[sIndex]; 318 eLink[sIndex] = SK_MaxS32; 319 } 320 SkASSERT(eIndex != SK_MaxS32); 321 #if DEBUG_ASSEMBLE 322 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e', 323 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e', 324 eIndex < 0 ? ~eIndex : eIndex); 325 #endif 326 do { 327 const SkOpContour* contour = runs[rIndex]; 328 if (first) { 329 first = false; 330 const SkPoint* startPtr = &contour->start(); 331 simple->deferredMove(startPtr[0]); 332 } 333 if (forward) { 334 contour->toPartialForward(simple); 335 } else { 336 contour->toPartialBackward(simple); 337 } 338 #if DEBUG_ASSEMBLE 339 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex, 340 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex, 341 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)); 342 #endif 343 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) { 344 simple->close(); 345 break; 346 } 347 if (forward) { 348 eIndex = eLink[rIndex]; 349 SkASSERT(eIndex != SK_MaxS32); 350 eLink[rIndex] = SK_MaxS32; 351 if (eIndex >= 0) { 352 SkASSERT(sLink[eIndex] == rIndex); 353 sLink[eIndex] = SK_MaxS32; 354 } else { 355 SkASSERT(eLink[~eIndex] == ~rIndex); 356 eLink[~eIndex] = SK_MaxS32; 357 } 358 } else { 359 eIndex = sLink[rIndex]; 360 SkASSERT(eIndex != SK_MaxS32); 361 sLink[rIndex] = SK_MaxS32; 362 if (eIndex >= 0) { 363 SkASSERT(eLink[eIndex] == rIndex); 364 eLink[eIndex] = SK_MaxS32; 365 } else { 366 SkASSERT(sLink[~eIndex] == ~rIndex); 367 sLink[~eIndex] = SK_MaxS32; 368 } 369 } 370 rIndex = eIndex; 371 if (rIndex < 0) { 372 forward ^= 1; 373 rIndex = ~rIndex; 374 } 375 } while (true); 376 for (rIndex = 0; rIndex < count; ++rIndex) { 377 if (sLink[rIndex] != SK_MaxS32) { 378 break; 379 } 380 } 381 } while (rIndex < count); 382 #if DEBUG_ASSEMBLE 383 for (rIndex = 0; rIndex < count; ++rIndex) { 384 SkASSERT(sLink[rIndex] == SK_MaxS32); 385 SkASSERT(eLink[rIndex] == SK_MaxS32); 386 } 387 #endif 388 } 389 390 static void align(SkOpContourHead* contourList) { 391 SkOpContour* contour = contourList; 392 do { 393 contour->align(); 394 } while ((contour = contour->next())); 395 } 396 397 static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) { 398 SkOpContour* contour = contourList; 399 do { 400 contour->calcAngles(allocator); 401 } while ((contour = contour->next())); 402 } 403 404 static void missingCoincidence(SkOpContourHead* contourList, 405 SkOpCoincidence* coincidence, SkChunkAlloc* allocator) { 406 SkOpContour* contour = contourList; 407 do { 408 contour->missingCoincidence(coincidence, allocator); 409 } while ((contour = contour->next())); 410 } 411 412 static void moveMultiples(SkOpContourHead* contourList) { 413 SkOpContour* contour = contourList; 414 do { 415 contour->moveMultiples(); 416 } while ((contour = contour->next())); 417 } 418 419 static void moveNearby(SkOpContourHead* contourList) { 420 SkOpContour* contour = contourList; 421 do { 422 contour->moveNearby(); 423 } while ((contour = contour->next())); 424 } 425 426 static void sortAngles(SkOpContourHead* contourList) { 427 SkOpContour* contour = contourList; 428 do { 429 contour->sortAngles(); 430 } while ((contour = contour->next())); 431 } 432 433 bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence, 434 SkChunkAlloc* allocator) { 435 SkOpGlobalState* globalState = contourList->globalState(); 436 // combine t values when multiple intersections occur on some segments but not others 437 moveMultiples(contourList); 438 // move t values and points together to eliminate small/tiny gaps 439 moveNearby(contourList); 440 align(contourList); // give all span members common values 441 #if DEBUG_VALIDATE 442 globalState->setPhase(SkOpGlobalState::kIntersecting); 443 #endif 444 coincidence->addMissing(allocator); 445 #if DEBUG_VALIDATE 446 globalState->setPhase(SkOpGlobalState::kWalking); 447 #endif 448 coincidence->expand(); // check to see if, loosely, coincident ranges may be expanded 449 coincidence->mark(); // mark spans of coincident segments as coincident 450 missingCoincidence(contourList, coincidence, allocator); // look for coincidence missed earlier 451 if (!coincidence->apply()) { // adjust the winding value to account for coincident edges 452 return false; 453 } 454 calcAngles(contourList, allocator); 455 sortAngles(contourList); 456 if (globalState->angleCoincidence()) { 457 missingCoincidence(contourList, coincidence, allocator); 458 if (!coincidence->apply()) { 459 return false; 460 } 461 } 462 #if DEBUG_ACTIVE_SPANS 463 coincidence->debugShowCoincidence(); 464 DebugShowActiveSpans(contourList); 465 #endif 466 return true; 467 } 468