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