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 #ifndef SkOpSpan_DEFINED 8 #define SkOpSpan_DEFINED 9 10 #include "include/core/SkPoint.h" 11 #include "src/pathops/SkPathOpsDebug.h" 12 #include "src/pathops/SkPathOpsTypes.h" 13 14 class SkArenaAlloc; 15 class SkOpAngle; 16 class SkOpContour; 17 class SkOpGlobalState; 18 class SkOpSegment; 19 class SkOpSpanBase; 20 class SkOpSpan; 21 struct SkPathOpsBounds; 22 23 // subset of op span used by terminal span (when t is equal to one) 24 class SkOpPtT { 25 public: 26 enum { 27 kIsAlias = 1, 28 kIsDuplicate = 1 29 }; 30 31 const SkOpPtT* active() const; 32 33 // please keep in sync with debugAddOpp() addOpp(SkOpPtT * opp,SkOpPtT * oppPrev)34 void addOpp(SkOpPtT* opp, SkOpPtT* oppPrev) { 35 SkOpPtT* oldNext = this->fNext; 36 SkASSERT(this != opp); 37 this->fNext = opp; 38 SkASSERT(oppPrev != oldNext); 39 oppPrev->fNext = oldNext; 40 } 41 42 bool alias() const; coincident()43 bool coincident() const { return fCoincident; } 44 bool contains(const SkOpPtT* ) const; 45 bool contains(const SkOpSegment*, const SkPoint& ) const; 46 bool contains(const SkOpSegment*, double t) const; 47 const SkOpPtT* contains(const SkOpSegment* ) const; 48 SkOpContour* contour() const; 49 debugID()50 int debugID() const { 51 return SkDEBUGRELEASE(fID, -1); 52 } 53 54 void debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const; 55 const SkOpAngle* debugAngle(int id) const; 56 const SkOpCoincidence* debugCoincidence() const; 57 bool debugContains(const SkOpPtT* ) const; 58 const SkOpPtT* debugContains(const SkOpSegment* check) const; 59 SkOpContour* debugContour(int id) const; 60 const SkOpPtT* debugEnder(const SkOpPtT* end) const; 61 int debugLoopLimit(bool report) const; 62 bool debugMatchID(int id) const; 63 const SkOpPtT* debugOppPrev(const SkOpPtT* opp) const; 64 const SkOpPtT* debugPtT(int id) const; 65 void debugResetCoinT() const; 66 const SkOpSegment* debugSegment(int id) const; 67 void debugSetCoinT(int ) const; 68 const SkOpSpanBase* debugSpan(int id) const; 69 void debugValidate() const; 70 deleted()71 bool deleted() const { 72 return fDeleted; 73 } 74 duplicate()75 bool duplicate() const { 76 return fDuplicatePt; 77 } 78 79 void dump() const; // available to testing only 80 void dumpAll() const; 81 void dumpBase() const; 82 83 const SkOpPtT* find(const SkOpSegment* ) const; 84 SkOpGlobalState* globalState() const; 85 void init(SkOpSpanBase* , double t, const SkPoint& , bool dup); 86 insert(SkOpPtT * span)87 void insert(SkOpPtT* span) { 88 SkASSERT(span != this); 89 span->fNext = fNext; 90 fNext = span; 91 } 92 next()93 const SkOpPtT* next() const { 94 return fNext; 95 } 96 next()97 SkOpPtT* next() { 98 return fNext; 99 } 100 101 bool onEnd() const; 102 103 // returns nullptr if this is already in the opp ptT loop oppPrev(const SkOpPtT * opp)104 SkOpPtT* oppPrev(const SkOpPtT* opp) const { 105 // find the fOpp ptr to opp 106 SkOpPtT* oppPrev = opp->fNext; 107 if (oppPrev == this) { 108 return nullptr; 109 } 110 while (oppPrev->fNext != opp) { 111 oppPrev = oppPrev->fNext; 112 if (oppPrev == this) { 113 return nullptr; 114 } 115 } 116 return oppPrev; 117 } 118 Overlaps(const SkOpPtT * s1,const SkOpPtT * e1,const SkOpPtT * s2,const SkOpPtT * e2,const SkOpPtT ** sOut,const SkOpPtT ** eOut)119 static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2, 120 const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) { 121 const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1; 122 const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2; 123 *sOut = between(s1->fT, start2->fT, e1->fT) ? start2 124 : between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr; 125 const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1; 126 const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2; 127 *eOut = between(s1->fT, end2->fT, e1->fT) ? end2 128 : between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr; 129 if (*sOut == *eOut) { 130 SkOPOBJASSERT(s1, start1->fT >= end2->fT || start2->fT >= end1->fT); 131 return false; 132 } 133 SkASSERT(!*sOut || *sOut != *eOut); 134 return *sOut && *eOut; 135 } 136 137 bool ptAlreadySeen(const SkOpPtT* head) const; 138 SkOpPtT* prev(); 139 140 const SkOpSegment* segment() const; 141 SkOpSegment* segment(); 142 setCoincident()143 void setCoincident() const { 144 SkOPASSERT(!fDeleted); 145 fCoincident = true; 146 } 147 148 void setDeleted(); 149 setSpan(const SkOpSpanBase * span)150 void setSpan(const SkOpSpanBase* span) { 151 fSpan = const_cast<SkOpSpanBase*>(span); 152 } 153 span()154 const SkOpSpanBase* span() const { 155 return fSpan; 156 } 157 span()158 SkOpSpanBase* span() { 159 return fSpan; 160 } 161 starter(const SkOpPtT * end)162 const SkOpPtT* starter(const SkOpPtT* end) const { 163 return fT < end->fT ? this : end; 164 } 165 166 double fT; 167 SkPoint fPt; // cache of point value at this t 168 protected: 169 SkOpSpanBase* fSpan; // contains winding data 170 SkOpPtT* fNext; // intersection on opposite curve or alias on this curve 171 bool fDeleted; // set if removed from span list 172 bool fDuplicatePt; // set if identical pt is somewhere in the next loop 173 // below mutable since referrer is otherwise always const 174 mutable bool fCoincident; // set if at some point a coincident span pointed here 175 SkDEBUGCODE(int fID); 176 }; 177 178 class SkOpSpanBase { 179 public: 180 enum class Collapsed { 181 kNo, 182 kYes, 183 kError, 184 }; 185 186 bool addOpp(SkOpSpanBase* opp); 187 bumpSpanAdds()188 void bumpSpanAdds() { 189 ++fSpanAdds; 190 } 191 chased()192 bool chased() const { 193 return fChased; 194 } 195 196 void checkForCollapsedCoincidence(); 197 coinEnd()198 const SkOpSpanBase* coinEnd() const { 199 return fCoinEnd; 200 } 201 202 Collapsed collapsed(double s, double e) const; 203 bool contains(const SkOpSpanBase* ) const; 204 const SkOpPtT* contains(const SkOpSegment* ) const; 205 containsCoinEnd(const SkOpSpanBase * coin)206 bool containsCoinEnd(const SkOpSpanBase* coin) const { 207 SkASSERT(this != coin); 208 const SkOpSpanBase* next = this; 209 while ((next = next->fCoinEnd) != this) { 210 if (next == coin) { 211 return true; 212 } 213 } 214 return false; 215 } 216 217 bool containsCoinEnd(const SkOpSegment* ) const; 218 SkOpContour* contour() const; 219 debugBumpCount()220 int debugBumpCount() { 221 return SkDEBUGRELEASE(++fCount, -1); 222 } 223 debugID()224 int debugID() const { 225 return SkDEBUGRELEASE(fID, -1); 226 } 227 228 #if DEBUG_COIN 229 void debugAddOpp(SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* opp) const; 230 #endif 231 bool debugAlignedEnd(double t, const SkPoint& pt) const; 232 bool debugAlignedInner() const; 233 const SkOpAngle* debugAngle(int id) const; 234 #if DEBUG_COIN 235 void debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* ) const; 236 #endif 237 const SkOpCoincidence* debugCoincidence() const; 238 bool debugCoinEndLoopCheck() const; 239 SkOpContour* debugContour(int id) const; 240 #ifdef SK_DEBUG debugDeleted()241 bool debugDeleted() const { return fDebugDeleted; } 242 #endif 243 #if DEBUG_COIN 244 void debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* , 245 const SkOpSpanBase* ) const; 246 void debugMergeMatches(SkPathOpsDebug::GlitchLog* log, 247 const SkOpSpanBase* opp) const; 248 #endif 249 const SkOpPtT* debugPtT(int id) const; 250 void debugResetCoinT() const; 251 const SkOpSegment* debugSegment(int id) const; 252 void debugSetCoinT(int ) const; 253 #ifdef SK_DEBUG debugSetDeleted()254 void debugSetDeleted() { fDebugDeleted = true; } 255 #endif 256 const SkOpSpanBase* debugSpan(int id) const; 257 const SkOpSpan* debugStarter(SkOpSpanBase const** endPtr) const; 258 SkOpGlobalState* globalState() const; 259 void debugValidate() const; 260 deleted()261 bool deleted() const { 262 return fPtT.deleted(); 263 } 264 265 void dump() const; // available to testing only 266 void dumpCoin() const; 267 void dumpAll() const; 268 void dumpBase() const; 269 void dumpHead() const; 270 final()271 bool final() const { 272 return fPtT.fT == 1; 273 } 274 fromAngle()275 SkOpAngle* fromAngle() const { 276 return fFromAngle; 277 } 278 279 void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt); 280 281 // Please keep this in sync with debugInsertCoinEnd() insertCoinEnd(SkOpSpanBase * coin)282 void insertCoinEnd(SkOpSpanBase* coin) { 283 if (containsCoinEnd(coin)) { 284 SkASSERT(coin->containsCoinEnd(this)); 285 return; 286 } 287 debugValidate(); 288 SkASSERT(this != coin); 289 SkOpSpanBase* coinNext = coin->fCoinEnd; 290 coin->fCoinEnd = this->fCoinEnd; 291 this->fCoinEnd = coinNext; 292 debugValidate(); 293 } 294 295 void merge(SkOpSpan* span); 296 bool mergeMatches(SkOpSpanBase* opp); 297 prev()298 const SkOpSpan* prev() const { 299 return fPrev; 300 } 301 prev()302 SkOpSpan* prev() { 303 return fPrev; 304 } 305 pt()306 const SkPoint& pt() const { 307 return fPtT.fPt; 308 } 309 ptT()310 const SkOpPtT* ptT() const { 311 return &fPtT; 312 } 313 ptT()314 SkOpPtT* ptT() { 315 return &fPtT; 316 } 317 segment()318 SkOpSegment* segment() const { 319 return fSegment; 320 } 321 setAligned()322 void setAligned() { 323 fAligned = true; 324 } 325 setChased(bool chased)326 void setChased(bool chased) { 327 fChased = chased; 328 } 329 setFromAngle(SkOpAngle * angle)330 void setFromAngle(SkOpAngle* angle) { 331 fFromAngle = angle; 332 } 333 setPrev(SkOpSpan * prev)334 void setPrev(SkOpSpan* prev) { 335 fPrev = prev; 336 } 337 simple()338 bool simple() const { 339 fPtT.debugValidate(); 340 return fPtT.next()->next() == &fPtT; 341 } 342 spanAddsCount()343 int spanAddsCount() const { 344 return fSpanAdds; 345 } 346 starter(const SkOpSpanBase * end)347 const SkOpSpan* starter(const SkOpSpanBase* end) const { 348 const SkOpSpanBase* result = t() < end->t() ? this : end; 349 return result->upCast(); 350 } 351 starter(SkOpSpanBase * end)352 SkOpSpan* starter(SkOpSpanBase* end) { 353 SkASSERT(this->segment() == end->segment()); 354 SkOpSpanBase* result = t() < end->t() ? this : end; 355 return result->upCast(); 356 } 357 starter(SkOpSpanBase ** endPtr)358 SkOpSpan* starter(SkOpSpanBase** endPtr) { 359 SkOpSpanBase* end = *endPtr; 360 SkASSERT(this->segment() == end->segment()); 361 SkOpSpanBase* result; 362 if (t() < end->t()) { 363 result = this; 364 } else { 365 result = end; 366 *endPtr = this; 367 } 368 return result->upCast(); 369 } 370 step(const SkOpSpanBase * end)371 int step(const SkOpSpanBase* end) const { 372 return t() < end->t() ? 1 : -1; 373 } 374 t()375 double t() const { 376 return fPtT.fT; 377 } 378 unaligned()379 void unaligned() { 380 fAligned = false; 381 } 382 upCast()383 SkOpSpan* upCast() { 384 SkASSERT(!final()); 385 return (SkOpSpan*) this; 386 } 387 upCast()388 const SkOpSpan* upCast() const { 389 SkOPASSERT(!final()); 390 return (const SkOpSpan*) this; 391 } 392 upCastable()393 SkOpSpan* upCastable() { 394 return final() ? nullptr : upCast(); 395 } 396 upCastable()397 const SkOpSpan* upCastable() const { 398 return final() ? nullptr : upCast(); 399 } 400 401 private: 402 void alignInner(); 403 404 protected: // no direct access to internals to avoid treating a span base as a span 405 SkOpPtT fPtT; // list of points and t values associated with the start of this span 406 SkOpSegment* fSegment; // segment that contains this span 407 SkOpSpanBase* fCoinEnd; // linked list of coincident spans that end here (may point to itself) 408 SkOpAngle* fFromAngle; // points to next angle from span start to end 409 SkOpSpan* fPrev; // previous intersection point 410 int fSpanAdds; // number of times intersections have been added to span 411 bool fAligned; 412 bool fChased; // set after span has been added to chase array 413 SkDEBUGCODE(int fCount); // number of pt/t pairs added 414 SkDEBUGCODE(int fID); 415 SkDEBUGCODE(bool fDebugDeleted); // set when span was merged with another span 416 }; 417 418 class SkOpSpan : public SkOpSpanBase { 419 public: alreadyAdded()420 bool alreadyAdded() const { 421 if (fAlreadyAdded) { 422 return true; 423 } 424 return false; 425 } 426 clearCoincident()427 bool clearCoincident() { 428 SkASSERT(!final()); 429 if (fCoincident == this) { 430 return false; 431 } 432 fCoincident = this; 433 return true; 434 } 435 436 int computeWindSum(); 437 bool containsCoincidence(const SkOpSegment* ) const; 438 containsCoincidence(const SkOpSpan * coin)439 bool containsCoincidence(const SkOpSpan* coin) const { 440 SkASSERT(this != coin); 441 const SkOpSpan* next = this; 442 while ((next = next->fCoincident) != this) { 443 if (next == coin) { 444 return true; 445 } 446 } 447 return false; 448 } 449 450 bool debugCoinLoopCheck() const; 451 #if DEBUG_COIN 452 void debugInsertCoincidence(SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const; 453 void debugInsertCoincidence(SkPathOpsDebug::GlitchLog* , 454 const SkOpSegment* , bool flipped, bool ordered) const; 455 #endif 456 void dumpCoin() const; 457 bool dumpSpan() const; 458 done()459 bool done() const { 460 SkASSERT(!final()); 461 return fDone; 462 } 463 464 void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt); 465 bool insertCoincidence(const SkOpSegment* , bool flipped, bool ordered); 466 467 // Please keep this in sync with debugInsertCoincidence() insertCoincidence(SkOpSpan * coin)468 void insertCoincidence(SkOpSpan* coin) { 469 if (containsCoincidence(coin)) { 470 SkASSERT(coin->containsCoincidence(this)); 471 return; 472 } 473 debugValidate(); 474 SkASSERT(this != coin); 475 SkOpSpan* coinNext = coin->fCoincident; 476 coin->fCoincident = this->fCoincident; 477 this->fCoincident = coinNext; 478 debugValidate(); 479 } 480 isCanceled()481 bool isCanceled() const { 482 SkASSERT(!final()); 483 return fWindValue == 0 && fOppValue == 0; 484 } 485 isCoincident()486 bool isCoincident() const { 487 SkASSERT(!final()); 488 return fCoincident != this; 489 } 490 markAdded()491 void markAdded() { 492 fAlreadyAdded = true; 493 } 494 next()495 SkOpSpanBase* next() const { 496 SkASSERT(!final()); 497 return fNext; 498 } 499 oppSum()500 int oppSum() const { 501 SkASSERT(!final()); 502 return fOppSum; 503 } 504 oppValue()505 int oppValue() const { 506 SkASSERT(!final()); 507 return fOppValue; 508 } 509 510 void release(const SkOpPtT* ); 511 512 SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment); 513 setDone(bool done)514 void setDone(bool done) { 515 SkASSERT(!final()); 516 fDone = done; 517 } 518 setNext(SkOpSpanBase * nextT)519 void setNext(SkOpSpanBase* nextT) { 520 SkASSERT(!final()); 521 fNext = nextT; 522 } 523 524 void setOppSum(int oppSum); 525 setOppValue(int oppValue)526 void setOppValue(int oppValue) { 527 SkASSERT(!final()); 528 SkASSERT(fOppSum == SK_MinS32); 529 SkOPASSERT(!oppValue || !fDone); 530 fOppValue = oppValue; 531 } 532 setToAngle(SkOpAngle * angle)533 void setToAngle(SkOpAngle* angle) { 534 SkASSERT(!final()); 535 fToAngle = angle; 536 } 537 538 void setWindSum(int windSum); 539 setWindValue(int windValue)540 void setWindValue(int windValue) { 541 SkASSERT(!final()); 542 SkASSERT(windValue >= 0); 543 SkASSERT(fWindSum == SK_MinS32); 544 SkOPASSERT(!windValue || !fDone); 545 fWindValue = windValue; 546 } 547 548 bool sortableTop(SkOpContour* ); 549 toAngle()550 SkOpAngle* toAngle() const { 551 SkASSERT(!final()); 552 return fToAngle; 553 } 554 windSum()555 int windSum() const { 556 SkASSERT(!final()); 557 return fWindSum; 558 } 559 windValue()560 int windValue() const { 561 SkOPASSERT(!final()); 562 return fWindValue; 563 } 564 565 private: // no direct access to internals to avoid treating a span base as a span 566 SkOpSpan* fCoincident; // linked list of spans coincident with this one (may point to itself) 567 SkOpAngle* fToAngle; // points to next angle from span start to end 568 SkOpSpanBase* fNext; // next intersection point 569 int fWindSum; // accumulated from contours surrounding this one. 570 int fOppSum; // for binary operators: the opposite winding sum 571 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident 572 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here 573 int fTopTTry; // specifies direction and t value to try next 574 bool fDone; // if set, this span to next higher T has been processed 575 bool fAlreadyAdded; 576 }; 577 578 #endif 579