/*
|
* 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
|