/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkOpContour_DEFINED #define SkOpContour_DEFINED #include "SkOpSegment.h" #include "SkTDArray.h" #include "SkTSort.h" class SkChunkAlloc; enum class SkOpRayDir; struct SkOpRayHit; class SkPathWriter; class SkOpContour { public: SkOpContour() { reset(); } ~SkOpContour() { if (fNext) { fNext->~SkOpContour(); } } bool operator<(const SkOpContour& rh) const { return fBounds.fTop == rh.fBounds.fTop ? fBounds.fLeft < rh.fBounds.fLeft : fBounds.fTop < rh.fBounds.fTop; } void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->addAlignIntersections(contourList, allocator); } while ((segment = segment->next())); } void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) { appendSegment(allocator).addConic(pts, weight, this); } void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) { appendSegment(allocator).addCubic(pts, this); } SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator); void addLine(SkPoint pts[2], SkChunkAlloc* allocator) { appendSegment(allocator).addLine(pts, this); } void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) { appendSegment(allocator).addQuad(pts, this); } void align() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->align(); } while ((segment = segment->next())); } SkOpSegment& appendSegment(SkChunkAlloc* allocator) { SkOpSegment* result = fCount++ ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead; result->setPrev(fTail); if (fTail) { fTail->setNext(result); } fTail = result; return *result; } SkOpContour* appendContour(SkChunkAlloc* allocator) { SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator); contour->setNext(nullptr); SkOpContour* prev = this; SkOpContour* next; while ((next = prev->next())) { prev = next; } prev->setNext(contour); return contour; } const SkPathOpsBounds& bounds() const { return fBounds; } void calcAngles(SkChunkAlloc* allocator) { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->calcAngles(allocator); } while ((segment = segment->next())); } void complete() { setBounds(); } int count() const { return fCount; } int debugID() const { return SkDEBUGRELEASE(fID, -1); } int debugIndent() const { return SkDEBUGRELEASE(fDebugIndent, 0); } #if DEBUG_ACTIVE_SPANS void debugShowActiveSpans() { SkOpSegment* segment = &fHead; do { segment->debugShowActiveSpans(); } while ((segment = segment->next())); } #endif const SkOpAngle* debugAngle(int id) const { return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr); } void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const; SkOpContour* debugContour(int id) { return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr); } void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log, const SkOpCoincidence* coincidence) const; const SkOpPtT* debugPtT(int id) const { return SkDEBUGRELEASE(this->globalState()->debugPtT(id), nullptr); } const SkOpSegment* debugSegment(int id) const { return SkDEBUGRELEASE(this->globalState()->debugSegment(id), nullptr); } const SkOpSpanBase* debugSpan(int id) const { return SkDEBUGRELEASE(this->globalState()->debugSpan(id), nullptr); } SkOpGlobalState* globalState() const { return fState; } void debugValidate() const { #if DEBUG_VALIDATE const SkOpSegment* segment = &fHead; const SkOpSegment* prior = nullptr; do { segment->debugValidate(); SkASSERT(segment->prev() == prior); prior = segment; } while ((segment = segment->next())); SkASSERT(prior == fTail); #endif } bool done() const { return fDone; } void dump() const; void dumpAll() const; void dumpAngles() const; void dumpContours() const; void dumpContoursAll() const; void dumpContoursAngles() const; void dumpContoursPts() const; void dumpContoursPt(int segmentID) const; void dumpContoursSegment(int segmentID) const; void dumpContoursSpan(int segmentID) const; void dumpContoursSpans() const; void dumpPt(int ) const; void dumpPts(const char* prefix = "seg") const; void dumpPtsX(const char* prefix) const; void dumpSegment(int ) const; void dumpSegments(const char* prefix = "seg", SkPathOp op = (SkPathOp) -1) const; void dumpSpan(int ) const; void dumpSpans() const; const SkPoint& end() const { return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())]; } bool findCollapsed() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->findCollapsed(); } while ((segment = segment->next())); return true; } SkOpSpan* findSortableTop(SkOpContour* ); SkOpSegment* first() { SkASSERT(fCount > 0); return &fHead; } const SkOpSegment* first() const { SkASSERT(fCount > 0); return &fHead; } void indentDump() const { SkDEBUGCODE(fDebugIndent += 2); } void init(SkOpGlobalState* globalState, bool operand, bool isXor) { fState = globalState; fOperand = operand; fXor = isXor; SkDEBUGCODE(fID = globalState->nextContourID()); } int isCcw() const { return fCcw; } bool isXor() const { return fXor; } void markDone() { SkOpSegment* segment = &fHead; do { segment->markAllDone(); } while ((segment = segment->next())); } bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; bool result = false; do { if (fState->angleCoincidence()) { #if DEBUG_ANGLE segment->debugCheckAngleCoin(); #endif } else if (segment->missingCoincidence(coincidences, allocator)) { result = true; // FIXME: trying again loops forever in issue3651_6 // The continue below is speculative -- once there's an actual case that requires it, // add the plumbing necessary to look for another missing coincidence in the same segment // continue; // try again in case another missing coincidence is further along } segment = segment->next(); } while (segment); return result; } bool moveMultiples() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { if (!segment->moveMultiples()) { return false; } } while ((segment = segment->next())); return true; } void moveNearby() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->moveNearby(); } while ((segment = segment->next())); } SkOpContour* next() { return fNext; } const SkOpContour* next() const { return fNext; } bool operand() const { return fOperand; } bool oppXor() const { return fOppXor; } void outdentDump() const { SkDEBUGCODE(fDebugIndent -= 2); } void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc* ); void remove(SkOpContour* contour) { if (contour == this) { SkASSERT(fCount == 0); return; } SkASSERT(contour->fNext == nullptr); SkOpContour* prev = this; SkOpContour* next; while ((next = prev->next()) != contour) { SkASSERT(next); prev = next; } SkASSERT(prev); prev->setNext(nullptr); } void reset() { fTail = nullptr; fNext = nullptr; fCount = 0; fDone = false; SkDEBUGCODE(fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin)); SkDEBUGCODE(fFirstSorted = -1); SkDEBUGCODE(fDebugIndent = 0); } void resetReverse() { SkOpContour* next = this; do { next->fCcw = -1; next->fReverse = false; } while ((next = next->next())); } bool reversed() const { return fReverse; } void setBounds() { SkASSERT(fCount > 0); const SkOpSegment* segment = &fHead; fBounds = segment->bounds(); while ((segment = segment->next())) { fBounds.add(segment->bounds()); } } void setCcw(int ccw) { fCcw = ccw; } void setGlobalState(SkOpGlobalState* state) { fState = state; } void setNext(SkOpContour* contour) { // SkASSERT(!fNext == !!contour); fNext = contour; } void setOperand(bool isOp) { fOperand = isOp; } void setOppXor(bool isOppXor) { fOppXor = isOppXor; } void setReverse() { fReverse = true; } void setXor(bool isXor) { fXor = isXor; } SkPath::Verb simplifyCubic(SkPoint pts[4]); void sortAngles() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->sortAngles(); } while ((segment = segment->next())); } const SkPoint& start() const { return fHead.pts()[0]; } void toPartialBackward(SkPathWriter* path) const { const SkOpSegment* segment = fTail; do { SkAssertResult(segment->addCurveTo(segment->tail(), segment->head(), path)); } while ((segment = segment->prev())); } void toPartialForward(SkPathWriter* path) const { const SkOpSegment* segment = &fHead; do { SkAssertResult(segment->addCurveTo(segment->head(), segment->tail(), path)); } while ((segment = segment->next())); } void toReversePath(SkPathWriter* path) const; void toPath(SkPathWriter* path) const; SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr); private: SkOpGlobalState* fState; SkOpSegment fHead; SkOpSegment* fTail; SkOpContour* fNext; SkPathOpsBounds fBounds; int fCcw; int fCount; int fFirstSorted; bool fDone; // set by find top segment bool fOperand; // true for the second argument to a binary operator bool fReverse; // true if contour should be reverse written to path (used only by fix winding) bool fXor; // set if original path had even-odd fill bool fOppXor; // set if opposite path had even-odd fill SkDEBUGCODE(int fID); SkDEBUGCODE(mutable int fDebugIndent); }; class SkOpContourHead : public SkOpContour { }; #endif