/*
|
* 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 SkOpSpan_DEFINED
|
#define SkOpSpan_DEFINED
|
|
#include "SkPathOpsDebug.h"
|
#include "SkPathOpsTypes.h"
|
#include "SkPoint.h"
|
|
class SkArenaAlloc;
|
class SkOpAngle;
|
class SkOpContour;
|
class SkOpGlobalState;
|
class SkOpSegment;
|
class SkOpSpanBase;
|
class SkOpSpan;
|
struct SkPathOpsBounds;
|
|
// subset of op span used by terminal span (when t is equal to one)
|
class SkOpPtT {
|
public:
|
enum {
|
kIsAlias = 1,
|
kIsDuplicate = 1
|
};
|
|
const SkOpPtT* active() const;
|
|
// please keep in sync with debugAddOpp()
|
void addOpp(SkOpPtT* opp, SkOpPtT* oppPrev) {
|
SkOpPtT* oldNext = this->fNext;
|
SkASSERT(this != opp);
|
this->fNext = opp;
|
SkASSERT(oppPrev != oldNext);
|
oppPrev->fNext = oldNext;
|
}
|
|
bool alias() const;
|
bool coincident() const { return fCoincident; }
|
bool contains(const SkOpPtT* ) const;
|
bool contains(const SkOpSegment*, const SkPoint& ) const;
|
bool contains(const SkOpSegment*, double t) const;
|
const SkOpPtT* contains(const SkOpSegment* ) const;
|
SkOpContour* contour() const;
|
|
int debugID() const {
|
return SkDEBUGRELEASE(fID, -1);
|
}
|
|
void debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const;
|
const SkOpAngle* debugAngle(int id) const;
|
const SkOpCoincidence* debugCoincidence() const;
|
bool debugContains(const SkOpPtT* ) const;
|
const SkOpPtT* debugContains(const SkOpSegment* check) const;
|
SkOpContour* debugContour(int id) const;
|
const SkOpPtT* debugEnder(const SkOpPtT* end) const;
|
int debugLoopLimit(bool report) const;
|
bool debugMatchID(int id) const;
|
const SkOpPtT* debugOppPrev(const SkOpPtT* opp) const;
|
const SkOpPtT* debugPtT(int id) const;
|
void debugResetCoinT() const;
|
const SkOpSegment* debugSegment(int id) const;
|
void debugSetCoinT(int ) const;
|
const SkOpSpanBase* debugSpan(int id) const;
|
void debugValidate() const;
|
|
bool deleted() const {
|
return fDeleted;
|
}
|
|
bool duplicate() const {
|
return fDuplicatePt;
|
}
|
|
void dump() const; // available to testing only
|
void dumpAll() const;
|
void dumpBase() const;
|
|
const SkOpPtT* find(const SkOpSegment* ) const;
|
SkOpGlobalState* globalState() const;
|
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
|
|
void insert(SkOpPtT* span) {
|
SkASSERT(span != this);
|
span->fNext = fNext;
|
fNext = span;
|
}
|
|
const SkOpPtT* next() const {
|
return fNext;
|
}
|
|
SkOpPtT* next() {
|
return fNext;
|
}
|
|
bool onEnd() const;
|
|
// returns nullptr if this is already in the opp ptT loop
|
SkOpPtT* oppPrev(const SkOpPtT* opp) const {
|
// find the fOpp ptr to opp
|
SkOpPtT* oppPrev = opp->fNext;
|
if (oppPrev == this) {
|
return nullptr;
|
}
|
while (oppPrev->fNext != opp) {
|
oppPrev = oppPrev->fNext;
|
if (oppPrev == this) {
|
return nullptr;
|
}
|
}
|
return oppPrev;
|
}
|
|
static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
|
const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
|
const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
|
const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
|
*sOut = between(s1->fT, start2->fT, e1->fT) ? start2
|
: between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr;
|
const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
|
const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
|
*eOut = between(s1->fT, end2->fT, e1->fT) ? end2
|
: between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr;
|
if (*sOut == *eOut) {
|
SkOPOBJASSERT(s1, start1->fT >= end2->fT || start2->fT >= end1->fT);
|
return false;
|
}
|
SkASSERT(!*sOut || *sOut != *eOut);
|
return *sOut && *eOut;
|
}
|
|
bool ptAlreadySeen(const SkOpPtT* head) const;
|
SkOpPtT* prev();
|
|
const SkOpSegment* segment() const;
|
SkOpSegment* segment();
|
|
void setCoincident() const {
|
SkOPASSERT(!fDeleted);
|
fCoincident = true;
|
}
|
|
void setDeleted();
|
|
void setSpan(const SkOpSpanBase* span) {
|
fSpan = const_cast<SkOpSpanBase*>(span);
|
}
|
|
const SkOpSpanBase* span() const {
|
return fSpan;
|
}
|
|
SkOpSpanBase* span() {
|
return fSpan;
|
}
|
|
const SkOpPtT* starter(const SkOpPtT* end) const {
|
return fT < end->fT ? this : end;
|
}
|
|
double fT;
|
SkPoint fPt; // cache of point value at this t
|
protected:
|
SkOpSpanBase* fSpan; // contains winding data
|
SkOpPtT* fNext; // intersection on opposite curve or alias on this curve
|
bool fDeleted; // set if removed from span list
|
bool fDuplicatePt; // set if identical pt is somewhere in the next loop
|
// below mutable since referrer is otherwise always const
|
mutable bool fCoincident; // set if at some point a coincident span pointed here
|
SkDEBUGCODE(int fID);
|
};
|
|
class SkOpSpanBase {
|
public:
|
enum class Collapsed {
|
kNo,
|
kYes,
|
kError,
|
};
|
|
bool addOpp(SkOpSpanBase* opp);
|
|
void bumpSpanAdds() {
|
++fSpanAdds;
|
}
|
|
bool chased() const {
|
return fChased;
|
}
|
|
void checkForCollapsedCoincidence();
|
|
const SkOpSpanBase* coinEnd() const {
|
return fCoinEnd;
|
}
|
|
Collapsed collapsed(double s, double e) const;
|
bool contains(const SkOpSpanBase* ) const;
|
const SkOpPtT* contains(const SkOpSegment* ) const;
|
|
bool containsCoinEnd(const SkOpSpanBase* coin) const {
|
SkASSERT(this != coin);
|
const SkOpSpanBase* next = this;
|
while ((next = next->fCoinEnd) != this) {
|
if (next == coin) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
bool containsCoinEnd(const SkOpSegment* ) const;
|
SkOpContour* contour() const;
|
|
int debugBumpCount() {
|
return SkDEBUGRELEASE(++fCount, -1);
|
}
|
|
int debugID() const {
|
return SkDEBUGRELEASE(fID, -1);
|
}
|
|
#if DEBUG_COIN
|
void debugAddOpp(SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* opp) const;
|
#endif
|
bool debugAlignedEnd(double t, const SkPoint& pt) const;
|
bool debugAlignedInner() const;
|
const SkOpAngle* debugAngle(int id) const;
|
#if DEBUG_COIN
|
void debugCheckForCollapsedCoincidence(SkPathOpsDebug::GlitchLog* ) const;
|
#endif
|
const SkOpCoincidence* debugCoincidence() const;
|
bool debugCoinEndLoopCheck() const;
|
SkOpContour* debugContour(int id) const;
|
#ifdef SK_DEBUG
|
bool debugDeleted() const { return fDebugDeleted; }
|
#endif
|
#if DEBUG_COIN
|
void debugInsertCoinEnd(SkPathOpsDebug::GlitchLog* ,
|
const SkOpSpanBase* ) const;
|
void debugMergeMatches(SkPathOpsDebug::GlitchLog* log,
|
const SkOpSpanBase* opp) const;
|
#endif
|
const SkOpPtT* debugPtT(int id) const;
|
void debugResetCoinT() const;
|
const SkOpSegment* debugSegment(int id) const;
|
void debugSetCoinT(int ) const;
|
#ifdef SK_DEBUG
|
void debugSetDeleted() { fDebugDeleted = true; }
|
#endif
|
const SkOpSpanBase* debugSpan(int id) const;
|
const SkOpSpan* debugStarter(SkOpSpanBase const** endPtr) const;
|
SkOpGlobalState* globalState() const;
|
void debugValidate() const;
|
|
bool deleted() const {
|
return fPtT.deleted();
|
}
|
|
void dump() const; // available to testing only
|
void dumpCoin() const;
|
void dumpAll() const;
|
void dumpBase() const;
|
void dumpHead() const;
|
|
bool final() const {
|
return fPtT.fT == 1;
|
}
|
|
SkOpAngle* fromAngle() const {
|
return fFromAngle;
|
}
|
|
void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
|
|
// Please keep this in sync with debugInsertCoinEnd()
|
void insertCoinEnd(SkOpSpanBase* coin) {
|
if (containsCoinEnd(coin)) {
|
SkASSERT(coin->containsCoinEnd(this));
|
return;
|
}
|
debugValidate();
|
SkASSERT(this != coin);
|
SkOpSpanBase* coinNext = coin->fCoinEnd;
|
coin->fCoinEnd = this->fCoinEnd;
|
this->fCoinEnd = coinNext;
|
debugValidate();
|
}
|
|
void merge(SkOpSpan* span);
|
bool mergeMatches(SkOpSpanBase* opp);
|
|
const SkOpSpan* prev() const {
|
return fPrev;
|
}
|
|
SkOpSpan* prev() {
|
return fPrev;
|
}
|
|
const SkPoint& pt() const {
|
return fPtT.fPt;
|
}
|
|
const SkOpPtT* ptT() const {
|
return &fPtT;
|
}
|
|
SkOpPtT* ptT() {
|
return &fPtT;
|
}
|
|
SkOpSegment* segment() const {
|
return fSegment;
|
}
|
|
void setAligned() {
|
fAligned = true;
|
}
|
|
void setChased(bool chased) {
|
fChased = chased;
|
}
|
|
void setFromAngle(SkOpAngle* angle) {
|
fFromAngle = angle;
|
}
|
|
void setPrev(SkOpSpan* prev) {
|
fPrev = prev;
|
}
|
|
bool simple() const {
|
fPtT.debugValidate();
|
return fPtT.next()->next() == &fPtT;
|
}
|
|
int spanAddsCount() const {
|
return fSpanAdds;
|
}
|
|
const SkOpSpan* starter(const SkOpSpanBase* end) const {
|
const SkOpSpanBase* result = t() < end->t() ? this : end;
|
return result->upCast();
|
}
|
|
SkOpSpan* starter(SkOpSpanBase* end) {
|
SkASSERT(this->segment() == end->segment());
|
SkOpSpanBase* result = t() < end->t() ? this : end;
|
return result->upCast();
|
}
|
|
SkOpSpan* starter(SkOpSpanBase** endPtr) {
|
SkOpSpanBase* end = *endPtr;
|
SkASSERT(this->segment() == end->segment());
|
SkOpSpanBase* result;
|
if (t() < end->t()) {
|
result = this;
|
} else {
|
result = end;
|
*endPtr = this;
|
}
|
return result->upCast();
|
}
|
|
int step(const SkOpSpanBase* end) const {
|
return t() < end->t() ? 1 : -1;
|
}
|
|
double t() const {
|
return fPtT.fT;
|
}
|
|
void unaligned() {
|
fAligned = false;
|
}
|
|
SkOpSpan* upCast() {
|
SkASSERT(!final());
|
return (SkOpSpan*) this;
|
}
|
|
const SkOpSpan* upCast() const {
|
SkOPASSERT(!final());
|
return (const SkOpSpan*) this;
|
}
|
|
SkOpSpan* upCastable() {
|
return final() ? nullptr : upCast();
|
}
|
|
const SkOpSpan* upCastable() const {
|
return final() ? nullptr : upCast();
|
}
|
|
private:
|
void alignInner();
|
|
protected: // no direct access to internals to avoid treating a span base as a span
|
SkOpPtT fPtT; // list of points and t values associated with the start of this span
|
SkOpSegment* fSegment; // segment that contains this span
|
SkOpSpanBase* fCoinEnd; // linked list of coincident spans that end here (may point to itself)
|
SkOpAngle* fFromAngle; // points to next angle from span start to end
|
SkOpSpan* fPrev; // previous intersection point
|
int fSpanAdds; // number of times intersections have been added to span
|
bool fAligned;
|
bool fChased; // set after span has been added to chase array
|
SkDEBUGCODE(int fCount); // number of pt/t pairs added
|
SkDEBUGCODE(int fID);
|
SkDEBUGCODE(bool fDebugDeleted); // set when span was merged with another span
|
};
|
|
class SkOpSpan : public SkOpSpanBase {
|
public:
|
bool alreadyAdded() const {
|
if (fAlreadyAdded) {
|
return true;
|
}
|
return false;
|
}
|
|
bool clearCoincident() {
|
SkASSERT(!final());
|
if (fCoincident == this) {
|
return false;
|
}
|
fCoincident = this;
|
return true;
|
}
|
|
int computeWindSum();
|
bool containsCoincidence(const SkOpSegment* ) const;
|
|
bool containsCoincidence(const SkOpSpan* coin) const {
|
SkASSERT(this != coin);
|
const SkOpSpan* next = this;
|
while ((next = next->fCoincident) != this) {
|
if (next == coin) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
bool debugCoinLoopCheck() const;
|
#if DEBUG_COIN
|
void debugInsertCoincidence(SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const;
|
void debugInsertCoincidence(SkPathOpsDebug::GlitchLog* ,
|
const SkOpSegment* , bool flipped, bool ordered) const;
|
#endif
|
void dumpCoin() const;
|
bool dumpSpan() const;
|
|
bool done() const {
|
SkASSERT(!final());
|
return fDone;
|
}
|
|
void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
|
bool insertCoincidence(const SkOpSegment* , bool flipped, bool ordered);
|
|
// Please keep this in sync with debugInsertCoincidence()
|
void insertCoincidence(SkOpSpan* coin) {
|
if (containsCoincidence(coin)) {
|
SkASSERT(coin->containsCoincidence(this));
|
return;
|
}
|
debugValidate();
|
SkASSERT(this != coin);
|
SkOpSpan* coinNext = coin->fCoincident;
|
coin->fCoincident = this->fCoincident;
|
this->fCoincident = coinNext;
|
debugValidate();
|
}
|
|
bool isCanceled() const {
|
SkASSERT(!final());
|
return fWindValue == 0 && fOppValue == 0;
|
}
|
|
bool isCoincident() const {
|
SkASSERT(!final());
|
return fCoincident != this;
|
}
|
|
void markAdded() {
|
fAlreadyAdded = true;
|
}
|
|
SkOpSpanBase* next() const {
|
SkASSERT(!final());
|
return fNext;
|
}
|
|
int oppSum() const {
|
SkASSERT(!final());
|
return fOppSum;
|
}
|
|
int oppValue() const {
|
SkASSERT(!final());
|
return fOppValue;
|
}
|
|
void release(const SkOpPtT* );
|
|
SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
|
|
void setDone(bool done) {
|
SkASSERT(!final());
|
fDone = done;
|
}
|
|
void setNext(SkOpSpanBase* nextT) {
|
SkASSERT(!final());
|
fNext = nextT;
|
}
|
|
void setOppSum(int oppSum);
|
|
void setOppValue(int oppValue) {
|
SkASSERT(!final());
|
SkASSERT(fOppSum == SK_MinS32);
|
SkOPASSERT(!oppValue || !fDone);
|
fOppValue = oppValue;
|
}
|
|
void setToAngle(SkOpAngle* angle) {
|
SkASSERT(!final());
|
fToAngle = angle;
|
}
|
|
void setWindSum(int windSum);
|
|
void setWindValue(int windValue) {
|
SkASSERT(!final());
|
SkASSERT(windValue >= 0);
|
SkASSERT(fWindSum == SK_MinS32);
|
SkOPASSERT(!windValue || !fDone);
|
fWindValue = windValue;
|
}
|
|
bool sortableTop(SkOpContour* );
|
|
SkOpAngle* toAngle() const {
|
SkASSERT(!final());
|
return fToAngle;
|
}
|
|
int windSum() const {
|
SkASSERT(!final());
|
return fWindSum;
|
}
|
|
int windValue() const {
|
SkOPASSERT(!final());
|
return fWindValue;
|
}
|
|
private: // no direct access to internals to avoid treating a span base as a span
|
SkOpSpan* fCoincident; // linked list of spans coincident with this one (may point to itself)
|
SkOpAngle* fToAngle; // points to next angle from span start to end
|
SkOpSpanBase* fNext; // next intersection point
|
int fWindSum; // accumulated from contours surrounding this one.
|
int fOppSum; // for binary operators: the opposite winding sum
|
int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
|
int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
|
int fTopTTry; // specifies direction and t value to try next
|
bool fDone; // if set, this span to next higher T has been processed
|
bool fAlreadyAdded;
|
};
|
|
#endif
|