/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkOpSegment_DEFINE #define SkOpSegment_DEFINE #include "src/core/SkArenaAlloc.h" #include "src/pathops/SkOpAngle.h" #include "src/pathops/SkOpSpan.h" #include "src/pathops/SkPathOpsBounds.h" #include "src/pathops/SkPathOpsCubic.h" #include "src/pathops/SkPathOpsCurve.h" struct SkDCurve; class SkOpCoincidence; class SkOpContour; enum class SkOpRayDir; struct SkOpRayHit; class SkPathWriter; class SkOpSegment { public: bool operator<(const SkOpSegment& rh) const { return fBounds.fTop < rh.fBounds.fTop; } SkOpAngle* activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr, bool* done); SkOpAngle* activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr, bool* done); SkOpAngle* activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr, bool* done); bool activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask, SkPathOp op); bool activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end, SkPathOp op, int* sumMiWinding, int* sumSuWinding); bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end); bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding); SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) { init(pts, weight, parent, SkPath::kConic_Verb); SkDCurve curve; curve.fConic.set(pts, weight); curve.setConicBounds(pts, weight, 0, 1, &fBounds); return this; } SkOpSegment* addCubic(SkPoint pts[4], SkOpContour* parent) { init(pts, 1, parent, SkPath::kCubic_Verb); SkDCurve curve; curve.fCubic.set(pts); curve.setCubicBounds(pts, 1, 0, 1, &fBounds); return this; } bool addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const; SkOpAngle* addEndSpan() { SkOpAngle* angle = this->globalState()->allocator()->make(); angle->set(&fTail, fTail.prev()); fTail.setFromAngle(angle); return angle; } bool addExpanded(double newT, const SkOpSpanBase* test, bool* startOver); SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) { SkASSERT(pts[0] != pts[1]); init(pts, 1, parent, SkPath::kLine_Verb); fBounds.setBounds(pts, 2); return this; } SkOpPtT* addMissing(double t, SkOpSegment* opp, bool* allExist); SkOpAngle* addStartSpan() { SkOpAngle* angle = this->globalState()->allocator()->make(); angle->set(&fHead, fHead.next()); fHead.setToAngle(angle); return angle; } SkOpSegment* addQuad(SkPoint pts[3], SkOpContour* parent) { init(pts, 1, parent, SkPath::kQuad_Verb); SkDCurve curve; curve.fQuad.set(pts); curve.setQuadBounds(pts, 1, 0, 1, &fBounds); return this; } SkOpPtT* addT(double t); SkOpPtT* addT(double t, const SkPoint& pt); const SkPathOpsBounds& bounds() const { return fBounds; } void bumpCount() { ++fCount; } void calcAngles(); SkOpSpanBase::Collapsed collapsed(double startT, double endT) const; static bool ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle, SkOpAngle::IncludeType ); static bool ComputeOneSumReverse(SkOpAngle* baseAngle, SkOpAngle* nextAngle, SkOpAngle::IncludeType ); int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType); void clearAll(); void clearOne(SkOpSpan* span); static void ClearVisited(SkOpSpanBase* span); bool contains(double t) const; SkOpContour* contour() const { return fContour; } int count() const { return fCount; } void debugAddAngle(double startT, double endT); #if DEBUG_COIN const SkOpPtT* debugAddT(double t, SkPathOpsDebug::GlitchLog* ) const; #endif const SkOpAngle* debugAngle(int id) const; #if DEBUG_ANGLE void debugCheckAngleCoin() const; #endif #if DEBUG_COIN void debugCheckHealth(SkPathOpsDebug::GlitchLog* ) const; void debugClearAll(SkPathOpsDebug::GlitchLog* glitches) const; void debugClearOne(const SkOpSpan* span, SkPathOpsDebug::GlitchLog* glitches) const; #endif const SkOpCoincidence* debugCoincidence() const; SkOpContour* debugContour(int id) const; int debugID() const { return SkDEBUGRELEASE(fID, -1); } SkOpAngle* debugLastAngle(); #if DEBUG_COIN void debugMissingCoincidence(SkPathOpsDebug::GlitchLog* glitches) const; void debugMoveMultiples(SkPathOpsDebug::GlitchLog* glitches) const; void debugMoveNearby(SkPathOpsDebug::GlitchLog* glitches) const; #endif const SkOpPtT* debugPtT(int id) const; void debugReset(); const SkOpSegment* debugSegment(int id) const; #if DEBUG_ACTIVE_SPANS void debugShowActiveSpans(SkString* str) const; #endif #if DEBUG_MARK_DONE void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding); void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, int oppWinding); #endif const SkOpSpanBase* debugSpan(int id) const; void debugValidate() const; #if DEBUG_COINCIDENCE_ORDER void debugResetCoinT() const; void debugSetCoinT(int, SkScalar ) const; #endif #if DEBUG_COIN static void DebugClearVisited(const SkOpSpanBase* span); bool debugVisited() const { if (!fDebugVisited) { fDebugVisited = true; return false; } return true; } #endif #if DEBUG_ANGLE double distSq(double t, const SkOpAngle* opp) const; #endif bool done() const { SkOPASSERT(fDoneCount <= fCount); return fDoneCount == fCount; } bool done(const SkOpAngle* angle) const { return angle->start()->starter(angle->end())->done(); } SkDPoint dPtAtT(double mid) const { return (*CurveDPointAtT[fVerb])(fPts, fWeight, mid); } SkDVector dSlopeAtT(double mid) const { return (*CurveDSlopeAtT[fVerb])(fPts, fWeight, mid); } void dump() const; void dumpAll() const; void dumpAngles() const; void dumpCoin() const; void dumpPts(const char* prefix = "seg") const; void dumpPtsInner(const char* prefix = "seg") const; const SkOpPtT* existing(double t, const SkOpSegment* opp) const; SkOpSegment* findNextOp(SkTDArray* chase, SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable, bool* simple, SkPathOp op, int xorMiMask, int xorSuMask); SkOpSegment* findNextWinding(SkTDArray* chase, SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable); SkOpSegment* findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable); SkOpSpan* findSortableTop(SkOpContour* ); SkOpGlobalState* globalState() const; const SkOpSpan* head() const { return &fHead; } SkOpSpan* head() { return &fHead; } void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb); SkOpSpan* insert(SkOpSpan* prev) { SkOpGlobalState* globalState = this->globalState(); globalState->setAllocatedOpSpan(); SkOpSpan* result = globalState->allocator()->make(); SkOpSpanBase* next = prev->next(); result->setPrev(prev); prev->setNext(result); SkDEBUGCODE(result->ptT()->fT = 0); result->setNext(next); if (next) { next->setPrev(result); } return result; } bool isClose(double t, const SkOpSegment* opp) const; bool isHorizontal() const { return fBounds.fTop == fBounds.fBottom; } SkOpSegment* isSimple(SkOpSpanBase** end, int* step) const { return nextChase(end, step, nullptr, nullptr); } bool isVertical() const { return fBounds.fLeft == fBounds.fRight; } bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const { return (*CurveIsVertical[fVerb])(fPts, fWeight, start->t(), end->t()); } bool isXor() const; void joinEnds(SkOpSegment* start) { fTail.ptT()->addOpp(start->fHead.ptT(), start->fHead.ptT()); } const SkPoint& lastPt() const { return fPts[SkPathOpsVerbToPoints(fVerb)]; } void markAllDone(); bool markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end, SkOpSpanBase** found); bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding, SkOpSpanBase** lastPtr); bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding, int oppWinding, SkOpSpanBase** lastPtr); bool markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle, SkOpSpanBase** result); bool markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding, const SkOpAngle* angle, SkOpSpanBase** result); void markDone(SkOpSpan* ); bool markWinding(SkOpSpan* , int winding); bool markWinding(SkOpSpan* , int winding, int oppWinding); bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const; bool missingCoincidence(); bool moveMultiples(); bool moveNearby(); SkOpSegment* next() const { return fNext; } SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const; bool operand() const; static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) { int result = start->t() < end->t() ? -start->upCast()->oppValue() : end->upCast()->oppValue(); return result; } bool oppXor() const; const SkOpSegment* prev() const { return fPrev; } SkPoint ptAtT(double mid) const { return (*CurvePointAtT[fVerb])(fPts, fWeight, mid); } const SkPoint* pts() const { return fPts; } bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const { SkASSERT(this == span.segment()); SkASSERT(this == test.segment()); return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt); } bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const { SkASSERT(this == span.segment()); return ptsDisjoint(span.fT, span.fPt, t, pt); } bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const; void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkArenaAlloc*); void release(const SkOpSpan* ); #if DEBUG_COIN void resetDebugVisited() const { fDebugVisited = false; } #endif void resetVisited() { fVisited = false; } void setContour(SkOpContour* contour) { fContour = contour; } void setNext(SkOpSegment* next) { fNext = next; } void setPrev(SkOpSegment* prev) { fPrev = prev; } void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) { int deltaSum = SpanSign(start, end); *maxWinding = *sumWinding; if (*sumWinding == SK_MinS32) { return; } *sumWinding -= deltaSum; } void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* maxWinding, int* sumWinding); void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding); bool sortAngles(); bool spansNearby(const SkOpSpanBase* ref, const SkOpSpanBase* check, bool* found) const; static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) { int result = start->t() < end->t() ? -start->upCast()->windValue() : end->upCast()->windValue(); return result; } SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) { SkASSERT(start != end); return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle(); } bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkDCurve* result) const; const SkOpSpanBase* tail() const { return &fTail; } SkOpSpanBase* tail() { return &fTail; } bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp) const; SkOpSpan* undoneSpan(); int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const; int updateOppWinding(const SkOpAngle* angle) const; int updateOppWindingReverse(const SkOpAngle* angle) const; int updateWinding(SkOpSpanBase* start, SkOpSpanBase* end); int updateWinding(SkOpAngle* angle); int updateWindingReverse(const SkOpAngle* angle); static bool UseInnerWinding(int outerWinding, int innerWinding); SkPath::Verb verb() const { return fVerb; } // look for two different spans that point to the same opposite segment bool visited() { if (!fVisited) { fVisited = true; return false; } return true; } SkScalar weight() const { return fWeight; } SkOpSpan* windingSpanAtT(double tHit); int windSum(const SkOpAngle* angle) const; private: SkOpSpan fHead; // the head span always has its t set to zero SkOpSpanBase fTail; // the tail span always has its t set to one SkOpContour* fContour; SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments const SkOpSegment* fPrev; SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked SkPathOpsBounds fBounds; // tight bounds SkScalar fWeight; int fCount; // number of spans (one for a non-intersecting segment) int fDoneCount; // number of processed spans (zero initially) SkPath::Verb fVerb; bool fVisited; // used by missing coincidence check #if DEBUG_COIN mutable bool fDebugVisited; // used by debug missing coincidence check #endif #if DEBUG_COINCIDENCE_ORDER mutable int fDebugBaseIndex; mutable SkScalar fDebugBaseMin; // if > 0, the 1st t value in this seg vis-a-vis the ref seg mutable SkScalar fDebugBaseMax; mutable int fDebugLastIndex; mutable SkScalar fDebugLastMin; // if > 0, the last t -- next t val - base has same sign mutable SkScalar fDebugLastMax; #endif SkDEBUGCODE(int fID); }; #endif