1 /* 2 * Copyright 2013 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 SkOpContour_DEFINED 8 #define SkOpContour_DEFINED 9 10 #include "include/private/SkTDArray.h" 11 #include "src/pathops/SkOpSegment.h" 12 13 enum class SkOpRayDir; 14 struct SkOpRayHit; 15 class SkPathWriter; 16 17 class SkOpContour { 18 public: SkOpContour()19 SkOpContour() { 20 reset(); 21 } 22 23 bool operator<(const SkOpContour& rh) const { 24 return fBounds.fTop == rh.fBounds.fTop 25 ? fBounds.fLeft < rh.fBounds.fLeft 26 : fBounds.fTop < rh.fBounds.fTop; 27 } 28 addConic(SkPoint pts[3],SkScalar weight)29 void addConic(SkPoint pts[3], SkScalar weight) { 30 appendSegment().addConic(pts, weight, this); 31 } 32 addCubic(SkPoint pts[4])33 void addCubic(SkPoint pts[4]) { 34 appendSegment().addCubic(pts, this); 35 } 36 addLine(SkPoint pts[2])37 SkOpSegment* addLine(SkPoint pts[2]) { 38 SkASSERT(pts[0] != pts[1]); 39 return appendSegment().addLine(pts, this); 40 } 41 addQuad(SkPoint pts[3])42 void addQuad(SkPoint pts[3]) { 43 appendSegment().addQuad(pts, this); 44 } 45 appendSegment()46 SkOpSegment& appendSegment() { 47 SkOpSegment* result = fCount++ ? this->globalState()->allocator()->make<SkOpSegment>() 48 : &fHead; 49 result->setPrev(fTail); 50 if (fTail) { 51 fTail->setNext(result); 52 } 53 fTail = result; 54 return *result; 55 } 56 bounds()57 const SkPathOpsBounds& bounds() const { 58 return fBounds; 59 } 60 calcAngles()61 void calcAngles() { 62 SkASSERT(fCount > 0); 63 SkOpSegment* segment = &fHead; 64 do { 65 segment->calcAngles(); 66 } while ((segment = segment->next())); 67 } 68 complete()69 void complete() { 70 setBounds(); 71 } 72 count()73 int count() const { 74 return fCount; 75 } 76 debugID()77 int debugID() const { 78 return SkDEBUGRELEASE(fID, -1); 79 } 80 debugIndent()81 int debugIndent() const { 82 return SkDEBUGRELEASE(fDebugIndent, 0); 83 } 84 85 debugAngle(int id)86 const SkOpAngle* debugAngle(int id) const { 87 return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr); 88 } 89 debugCoincidence()90 const SkOpCoincidence* debugCoincidence() const { 91 return this->globalState()->coincidence(); 92 } 93 94 #if DEBUG_COIN 95 void debugCheckHealth(SkPathOpsDebug::GlitchLog* ) const; 96 #endif 97 debugContour(int id)98 SkOpContour* debugContour(int id) const { 99 return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr); 100 } 101 102 #if DEBUG_COIN 103 void debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const; 104 void debugMoveMultiples(SkPathOpsDebug::GlitchLog* ) const; 105 void debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const; 106 #endif 107 debugPtT(int id)108 const SkOpPtT* debugPtT(int id) const { 109 return SkDEBUGRELEASE(this->globalState()->debugPtT(id), nullptr); 110 } 111 debugSegment(int id)112 const SkOpSegment* debugSegment(int id) const { 113 return SkDEBUGRELEASE(this->globalState()->debugSegment(id), nullptr); 114 } 115 116 #if DEBUG_ACTIVE_SPANS debugShowActiveSpans(SkString * str)117 void debugShowActiveSpans(SkString* str) { 118 SkOpSegment* segment = &fHead; 119 do { 120 segment->debugShowActiveSpans(str); 121 } while ((segment = segment->next())); 122 } 123 #endif 124 debugSpan(int id)125 const SkOpSpanBase* debugSpan(int id) const { 126 return SkDEBUGRELEASE(this->globalState()->debugSpan(id), nullptr); 127 } 128 globalState()129 SkOpGlobalState* globalState() const { 130 return fState; 131 } 132 debugValidate()133 void debugValidate() const { 134 #if DEBUG_VALIDATE 135 const SkOpSegment* segment = &fHead; 136 const SkOpSegment* prior = nullptr; 137 do { 138 segment->debugValidate(); 139 SkASSERT(segment->prev() == prior); 140 prior = segment; 141 } while ((segment = segment->next())); 142 SkASSERT(prior == fTail); 143 #endif 144 } 145 done()146 bool done() const { 147 return fDone; 148 } 149 150 void dump() const; 151 void dumpAll() const; 152 void dumpAngles() const; 153 void dumpContours() const; 154 void dumpContoursAll() const; 155 void dumpContoursAngles() const; 156 void dumpContoursPts() const; 157 void dumpContoursPt(int segmentID) const; 158 void dumpContoursSegment(int segmentID) const; 159 void dumpContoursSpan(int segmentID) const; 160 void dumpContoursSpans() const; 161 void dumpPt(int ) const; 162 void dumpPts(const char* prefix = "seg") const; 163 void dumpPtsX(const char* prefix) const; 164 void dumpSegment(int ) const; 165 void dumpSegments(const char* prefix = "seg", SkPathOp op = (SkPathOp) -1) const; 166 void dumpSpan(int ) const; 167 void dumpSpans() const; 168 end()169 const SkPoint& end() const { 170 return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())]; 171 } 172 173 SkOpSpan* findSortableTop(SkOpContour* ); 174 first()175 SkOpSegment* first() { 176 SkASSERT(fCount > 0); 177 return &fHead; 178 } 179 first()180 const SkOpSegment* first() const { 181 SkASSERT(fCount > 0); 182 return &fHead; 183 } 184 indentDump()185 void indentDump() const { 186 SkDEBUGCODE(fDebugIndent += 2); 187 } 188 init(SkOpGlobalState * globalState,bool operand,bool isXor)189 void init(SkOpGlobalState* globalState, bool operand, bool isXor) { 190 fState = globalState; 191 fOperand = operand; 192 fXor = isXor; 193 SkDEBUGCODE(fID = globalState->nextContourID()); 194 } 195 isCcw()196 int isCcw() const { 197 return fCcw; 198 } 199 isXor()200 bool isXor() const { 201 return fXor; 202 } 203 joinSegments()204 void joinSegments() { 205 SkOpSegment* segment = &fHead; 206 SkOpSegment* next; 207 do { 208 next = segment->next(); 209 segment->joinEnds(next ? next : &fHead); 210 } while ((segment = next)); 211 } 212 markAllDone()213 void markAllDone() { 214 SkOpSegment* segment = &fHead; 215 do { 216 segment->markAllDone(); 217 } while ((segment = segment->next())); 218 } 219 220 // Please keep this aligned with debugMissingCoincidence() missingCoincidence()221 bool missingCoincidence() { 222 SkASSERT(fCount > 0); 223 SkOpSegment* segment = &fHead; 224 bool result = false; 225 do { 226 if (segment->missingCoincidence()) { 227 result = true; 228 } 229 segment = segment->next(); 230 } while (segment); 231 return result; 232 } 233 moveMultiples()234 bool moveMultiples() { 235 SkASSERT(fCount > 0); 236 SkOpSegment* segment = &fHead; 237 do { 238 if (!segment->moveMultiples()) { 239 return false; 240 } 241 } while ((segment = segment->next())); 242 return true; 243 } 244 moveNearby()245 bool moveNearby() { 246 SkASSERT(fCount > 0); 247 SkOpSegment* segment = &fHead; 248 do { 249 if (!segment->moveNearby()) { 250 return false; 251 } 252 } while ((segment = segment->next())); 253 return true; 254 } 255 next()256 SkOpContour* next() { 257 return fNext; 258 } 259 next()260 const SkOpContour* next() const { 261 return fNext; 262 } 263 operand()264 bool operand() const { 265 return fOperand; 266 } 267 oppXor()268 bool oppXor() const { 269 return fOppXor; 270 } 271 outdentDump()272 void outdentDump() const { 273 SkDEBUGCODE(fDebugIndent -= 2); 274 } 275 276 void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkArenaAlloc*); 277 reset()278 void reset() { 279 fTail = nullptr; 280 fNext = nullptr; 281 fCount = 0; 282 fDone = false; 283 SkDEBUGCODE(fBounds.setLTRB(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin)); 284 SkDEBUGCODE(fFirstSorted = -1); 285 SkDEBUGCODE(fDebugIndent = 0); 286 } 287 resetReverse()288 void resetReverse() { 289 SkOpContour* next = this; 290 do { 291 if (!next->count()) { 292 continue; 293 } 294 next->fCcw = -1; 295 next->fReverse = false; 296 } while ((next = next->next())); 297 } 298 reversed()299 bool reversed() const { 300 return fReverse; 301 } 302 setBounds()303 void setBounds() { 304 SkASSERT(fCount > 0); 305 const SkOpSegment* segment = &fHead; 306 fBounds = segment->bounds(); 307 while ((segment = segment->next())) { 308 fBounds.add(segment->bounds()); 309 } 310 } 311 setCcw(int ccw)312 void setCcw(int ccw) { 313 fCcw = ccw; 314 } 315 setGlobalState(SkOpGlobalState * state)316 void setGlobalState(SkOpGlobalState* state) { 317 fState = state; 318 } 319 setNext(SkOpContour * contour)320 void setNext(SkOpContour* contour) { 321 // SkASSERT(!fNext == !!contour); 322 fNext = contour; 323 } 324 setOperand(bool isOp)325 void setOperand(bool isOp) { 326 fOperand = isOp; 327 } 328 setOppXor(bool isOppXor)329 void setOppXor(bool isOppXor) { 330 fOppXor = isOppXor; 331 } 332 setReverse()333 void setReverse() { 334 fReverse = true; 335 } 336 setXor(bool isXor)337 void setXor(bool isXor) { 338 fXor = isXor; 339 } 340 sortAngles()341 bool sortAngles() { 342 SkASSERT(fCount > 0); 343 SkOpSegment* segment = &fHead; 344 do { 345 FAIL_IF(!segment->sortAngles()); 346 } while ((segment = segment->next())); 347 return true; 348 } 349 start()350 const SkPoint& start() const { 351 return fHead.pts()[0]; 352 } 353 toPartialBackward(SkPathWriter * path)354 void toPartialBackward(SkPathWriter* path) const { 355 const SkOpSegment* segment = fTail; 356 do { 357 SkAssertResult(segment->addCurveTo(segment->tail(), segment->head(), path)); 358 } while ((segment = segment->prev())); 359 } 360 toPartialForward(SkPathWriter * path)361 void toPartialForward(SkPathWriter* path) const { 362 const SkOpSegment* segment = &fHead; 363 do { 364 SkAssertResult(segment->addCurveTo(segment->head(), segment->tail(), path)); 365 } while ((segment = segment->next())); 366 } 367 368 void toReversePath(SkPathWriter* path) const; 369 void toPath(SkPathWriter* path) const; 370 SkOpSpan* undoneSpan(); 371 372 protected: 373 SkOpGlobalState* fState; 374 SkOpSegment fHead; 375 SkOpSegment* fTail; 376 SkOpContour* fNext; 377 SkPathOpsBounds fBounds; 378 int fCcw; 379 int fCount; 380 int fFirstSorted; 381 bool fDone; // set by find top segment 382 bool fOperand; // true for the second argument to a binary operator 383 bool fReverse; // true if contour should be reverse written to path (used only by fix winding) 384 bool fXor; // set if original path had even-odd fill 385 bool fOppXor; // set if opposite path had even-odd fill 386 SkDEBUGCODE(int fID); 387 SkDEBUGCODE(mutable int fDebugIndent); 388 }; 389 390 class SkOpContourHead : public SkOpContour { 391 public: appendContour()392 SkOpContour* appendContour() { 393 SkOpContour* contour = this->globalState()->allocator()->make<SkOpContour>(); 394 contour->setNext(nullptr); 395 SkOpContour* prev = this; 396 SkOpContour* next; 397 while ((next = prev->next())) { 398 prev = next; 399 } 400 prev->setNext(contour); 401 return contour; 402 } 403 joinAllSegments()404 void joinAllSegments() { 405 SkOpContour* next = this; 406 do { 407 if (!next->count()) { 408 continue; 409 } 410 next->joinSegments(); 411 } while ((next = next->next())); 412 } 413 remove(SkOpContour * contour)414 void remove(SkOpContour* contour) { 415 if (contour == this) { 416 SkASSERT(this->count() == 0); 417 return; 418 } 419 SkASSERT(contour->next() == nullptr); 420 SkOpContour* prev = this; 421 SkOpContour* next; 422 while ((next = prev->next()) != contour) { 423 SkASSERT(next); 424 prev = next; 425 } 426 SkASSERT(prev); 427 prev->setNext(nullptr); 428 } 429 430 }; 431 432 class SkOpContourBuilder { 433 public: SkOpContourBuilder(SkOpContour * contour)434 SkOpContourBuilder(SkOpContour* contour) 435 : fContour(contour) 436 , fLastIsLine(false) { 437 } 438 439 void addConic(SkPoint pts[3], SkScalar weight); 440 void addCubic(SkPoint pts[4]); 441 void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkScalar weight = 1); 442 void addLine(const SkPoint pts[2]); 443 void addQuad(SkPoint pts[3]); 444 void flush(); contour()445 SkOpContour* contour() { return fContour; } setContour(SkOpContour * contour)446 void setContour(SkOpContour* contour) { flush(); fContour = contour; } 447 protected: 448 SkOpContour* fContour; 449 SkPoint fLastLine[2]; 450 bool fLastIsLine; 451 }; 452 453 #endif 454