/* * 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" enum class SkOpRayDir; struct SkOpRayHit; class SkPathWriter; class SkOpContour { public: SkOpContour() { reset(); } bool operator<(const SkOpContour& rh) const { return fBounds.fTop == rh.fBounds.fTop ? fBounds.fLeft < rh.fBounds.fLeft : fBounds.fTop < rh.fBounds.fTop; } void addConic(SkPoint pts[3], SkScalar weight) { appendSegment().addConic(pts, weight, this); } void addCubic(SkPoint pts[4]) { appendSegment().addCubic(pts, this); } SkOpSegment* addLine(SkPoint pts[2]) { SkASSERT(pts[0] != pts[1]); return appendSegment().addLine(pts, this); } void addQuad(SkPoint pts[3]) { appendSegment().addQuad(pts, this); } SkOpSegment& appendSegment() { SkOpSegment* result = fCount++ ? this->globalState()->allocator()->make<SkOpSegment>() : &fHead; result->setPrev(fTail); if (fTail) { fTail->setNext(result); } fTail = result; return *result; } const SkPathOpsBounds& bounds() const { return fBounds; } void calcAngles() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { segment->calcAngles(); } 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); } const SkOpAngle* debugAngle(int id) const { return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr); } const SkOpCoincidence* debugCoincidence() const { return this->globalState()->coincidence(); } #if DEBUG_COIN void debugCheckHealth(SkPathOpsDebug::GlitchLog* ) const; #endif SkOpContour* debugContour(int id) const { return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr); } #if DEBUG_COIN void debugMissingCoincidence(SkPathOpsDebug::GlitchLog* log) const; void debugMoveMultiples(SkPathOpsDebug::GlitchLog* ) const; void debugMoveNearby(SkPathOpsDebug::GlitchLog* log) const; #endif 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); } #if DEBUG_ACTIVE_SPANS void debugShowActiveSpans(SkString* str) { SkOpSegment* segment = &fHead; do { segment->debugShowActiveSpans(str); } while ((segment = segment->next())); } #endif 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())]; } 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 joinSegments() { SkOpSegment* segment = &fHead; SkOpSegment* next; do { next = segment->next(); segment->joinEnds(next ? next : &fHead); } while ((segment = next)); } void markAllDone() { SkOpSegment* segment = &fHead; do { segment->markAllDone(); } while ((segment = segment->next())); } // Please keep this aligned with debugMissingCoincidence() bool missingCoincidence() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; bool result = false; do { if (segment->missingCoincidence()) { result = true; } 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; } bool moveNearby() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { if (!segment->moveNearby()) { return false; } } while ((segment = segment->next())); return true; } 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, SkArenaAlloc*); 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 { if (!next->count()) { continue; } 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; } bool sortAngles() { SkASSERT(fCount > 0); SkOpSegment* segment = &fHead; do { FAIL_IF(!segment->sortAngles()); } while ((segment = segment->next())); return true; } 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; SkOpSpan* undoneSpan(); protected: 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 { public: SkOpContour* appendContour() { SkOpContour* contour = this->globalState()->allocator()->make<SkOpContour>(); contour->setNext(nullptr); SkOpContour* prev = this; SkOpContour* next; while ((next = prev->next())) { prev = next; } prev->setNext(contour); return contour; } void joinAllSegments() { SkOpContour* next = this; do { if (!next->count()) { continue; } next->joinSegments(); } while ((next = next->next())); } void remove(SkOpContour* contour) { if (contour == this) { SkASSERT(this->count() == 0); return; } SkASSERT(contour->next() == nullptr); SkOpContour* prev = this; SkOpContour* next; while ((next = prev->next()) != contour) { SkASSERT(next); prev = next; } SkASSERT(prev); prev->setNext(nullptr); } }; class SkOpContourBuilder { public: SkOpContourBuilder(SkOpContour* contour) : fContour(contour) , fLastIsLine(false) { } void addConic(SkPoint pts[3], SkScalar weight); void addCubic(SkPoint pts[4]); void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkScalar weight = 1); void addLine(const SkPoint pts[2]); void addQuad(SkPoint pts[3]); void flush(); SkOpContour* contour() { return fContour; } void setContour(SkOpContour* contour) { flush(); fContour = contour; } protected: SkOpContour* fContour; SkPoint fLastLine[2]; bool fLastIsLine; }; #endif