SkOpAngle* lh = test;
SkOpAngle* rh = lh->fNext;
SkASSERT(lh != rh);
- fCurvePart = fOriginalCurvePart;
- lh->fCurvePart = lh->fOriginalCurvePart;
- lh->fCurvePart.offset(lh->segment()->verb(), fCurvePart[0] - lh->fCurvePart[0]);
- rh->fCurvePart = rh->fOriginalCurvePart;
- rh->fCurvePart.offset(rh->segment()->verb(), fCurvePart[0] - rh->fCurvePart[0]);
+ fPart.fCurve = fOriginalCurvePart;
+ lh->fPart.fCurve = lh->fOriginalCurvePart;
+ lh->fPart.fCurve.offset(lh->segment()->verb(), fPart.fCurve[0] - lh->fPart.fCurve[0]);
+ rh->fPart.fCurve = rh->fOriginalCurvePart;
+ rh->fPart.fCurve.offset(rh->segment()->verb(), fPart.fCurve[0] - rh->fPart.fCurve[0]);
#if DEBUG_ANGLE
SkString bugOut;
// given a line, see if the opposite curve's convex hull is all on one side
// returns -1=not on one side 0=this CW of test 1=this CCW of test
int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
- SkASSERT(!fIsCurve);
- SkASSERT(test->fIsCurve);
- SkDPoint origin = fCurvePart[0];
- SkDVector line = fCurvePart[1] - origin;
+ SkASSERT(!fPart.isCurve());
+ SkASSERT(test->fPart.isCurve());
+ SkDPoint origin = fPart.fCurve[0];
+ SkDVector line = fPart.fCurve[1] - origin;
double crosses[3];
SkPath::Verb testVerb = test->segment()->verb();
int iMax = SkPathOpsVerbToPoints(testVerb);
// SkASSERT(origin == test.fCurveHalf[0]);
- const SkDCurve& testCurve = test->fCurvePart;
+ const SkDCurve& testCurve = test->fPart.fCurve;
for (int index = 1; index <= iMax; ++index) {
double xy1 = line.fX * (testCurve[index].fY - origin.fY);
double xy2 = line.fY * (testCurve[index].fX - origin.fX);
bool SkOpAngle::checkParallel(SkOpAngle* rh) {
SkDVector scratch[2];
const SkDVector* sweep, * tweep;
- if (!this->fUnorderedSweep) {
- sweep = this->fSweep;
+ if (this->fPart.isOrdered()) {
+ sweep = this->fPart.fSweep;
} else {
- scratch[0] = this->fCurvePart[1] - this->fCurvePart[0];
+ scratch[0] = this->fPart.fCurve[1] - this->fPart.fCurve[0];
sweep = &scratch[0];
}
- if (!rh->fUnorderedSweep) {
- tweep = rh->fSweep;
+ if (rh->fPart.isOrdered()) {
+ tweep = rh->fPart.fSweep;
} else {
- scratch[1] = rh->fCurvePart[1] - rh->fCurvePart[0];
+ scratch[1] = rh->fPart.fCurve[1] - rh->fPart.fCurve[0];
tweep = &scratch[1];
}
double s0xt0 = sweep->crossCheck(*tweep);
return !inside;
}
// compute the cross check from the mid T values (last resort)
- SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
- SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+ SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
+ SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
double m0xm1 = m0.crossCheck(m1);
if (m0xm1 == 0) {
this->fUnorderable = true;
}
int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
- const SkDVector* sweep = this->fSweep;
- const SkDVector* tweep = rh->fSweep;
+ const SkDVector* sweep = this->fPart.fSweep;
+ const SkDVector* tweep = rh->fPart.fSweep;
double s0xs1 = sweep[0].crossCheck(sweep[1]);
double s0xt0 = sweep[0].crossCheck(tweep[0]);
double s1xt0 = sweep[1].crossCheck(tweep[0]);
// if the outside sweeps are greater than 180 degress:
// first assume the inital tangents are the ordering
// if the midpoint direction matches the inital order, that is enough
- SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
- SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+ SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
+ SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
double m0xm1 = m0.crossCheck(m1);
if (s0xt0 > 0 && m0xm1 > 0) {
return 0;
SkPath::Verb rVerb = rh->segment()->verb();
int lPts = SkPathOpsVerbToPoints(lVerb);
int rPts = SkPathOpsVerbToPoints(rVerb);
- SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
- {{this->fCurvePart[0], this->fCurvePart[lPts]}}};
+ SkDLine rays[] = {{{this->fPart.fCurve[0], rh->fPart.fCurve[rPts]}},
+ {{this->fPart.fCurve[0], this->fPart.fCurve[lPts]}}};
if (this->fEnd->contains(rh->fEnd)) {
return checkParallel(rh);
}
double minX, minY, maxX, maxY;
minX = minY = SK_ScalarInfinity;
maxX = maxY = -SK_ScalarInfinity;
- const SkDCurve& curve = index ? rh->fCurvePart : this->fCurvePart;
+ const SkDCurve& curve = index ? rh->fPart.fCurve : this->fPart.fCurve;
int ptCount = index ? rPts : lPts;
for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
minX = SkTMin(minX, curve[idx2].fX);
}
}
if (useIntersect) {
- const SkDCurve& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
+ const SkDCurve& curve = sIndex ? rh->fPart.fCurve : this->fPart.fCurve;
const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
double tStart = sIndex ? rh->fStart->t() : fStart->t();
SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
double minX, minY, maxX, maxY;
minX = minY = SK_ScalarInfinity;
maxX = maxY = -SK_ScalarInfinity;
- const SkDCurve& curve = rh->fCurvePart;
+ const SkDCurve& curve = rh->fPart.fCurve;
int oppPts = SkPathOpsVerbToPoints(oppVerb);
for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
minX = SkTMin(minX, curve[idx2].fX);
bool SkOpAngle::orderable(SkOpAngle* rh) {
int result;
- if (!fIsCurve) {
- if (!rh->fIsCurve) {
+ if (!fPart.isCurve()) {
+ if (!rh->fPart.isCurve()) {
double leftX = fTangentHalf.dx();
double leftY = fTangentHalf.dy();
double rightX = rh->fTangentHalf.dx();
if (fUnorderable || approximately_zero(rh->fSide)) {
goto unorderable;
}
- } else if (!rh->fIsCurve) {
+ } else if (!rh->fPart.isCurve()) {
if ((result = rh->allOnOneSide(this)) >= 0) {
return !result;
}
SkDEBUGCODE(fID = start ? start->globalState()->nextAngleID() : -1);
}
-void SkOpAngle::setCurveHullSweep() {
- fUnorderedSweep = false;
- fSweep[0] = fCurvePart[1] - fCurvePart[0];
- const SkOpSegment* segment = fStart->segment();
- if (SkPath::kLine_Verb == segment->verb()) {
- fSweep[1] = fSweep[0];
- return;
- }
- fSweep[1] = fCurvePart[2] - fCurvePart[0];
- // OPTIMIZE: I do the following float check a lot -- probably need a
- // central place for this val-is-small-compared-to-curve check
- double maxVal = 0;
- for (int index = 0; index < SkPathOpsVerbToPoints(segment->verb()); ++index) {
- maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurvePart[index].fX),
- SkTAbs(fCurvePart[index].fY)));
- }
-
- if (SkPath::kCubic_Verb != segment->verb()) {
- if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
- && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
- fSweep[0] = fSweep[1];
- }
- return;
- }
- SkDVector thirdSweep = fCurvePart[3] - fCurvePart[0];
- if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
- fSweep[0] = fSweep[1];
- fSweep[1] = thirdSweep;
- if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
- && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
- fSweep[0] = fSweep[1];
- fCurvePart[1] = fCurvePart[3];
- fIsCurve = false;
- }
- return;
- }
- double s1x3 = fSweep[0].crossCheck(thirdSweep);
- double s3x2 = thirdSweep.crossCheck(fSweep[1]);
- if (s1x3 * s3x2 >= 0) { // if third vector is on or between first two vectors
- return;
- }
- double s2x1 = fSweep[1].crossCheck(fSweep[0]);
- // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble
- // probably such wide sweeps should be artificially subdivided earlier so that never happens
- SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0);
- if (s3x2 * s2x1 < 0) {
- SkASSERT(s2x1 * s1x3 > 0);
- fSweep[0] = fSweep[1];
- fUnorderedSweep = true;
- }
- fSweep[1] = thirdSweep;
-}
-
void SkOpAngle::setSpans() {
fUnorderable = false;
fLastMarked = nullptr;
}
const SkOpSegment* segment = fStart->segment();
const SkPoint* pts = segment->pts();
- SkDEBUGCODE(fCurvePart.fVerb = SkPath::kCubic_Verb);
- SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
+ SkDEBUGCODE(fPart.fCurve.fVerb = SkPath::kCubic_Verb);
+ SkDEBUGCODE(fPart.fCurve[2].fX = fPart.fCurve[2].fY = fPart.fCurve[3].fX = fPart.fCurve[3].fY
= SK_ScalarNaN);
- SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
- segment->subDivide(fStart, fEnd, &fCurvePart);
- fOriginalCurvePart = fCurvePart;
- setCurveHullSweep();
+ SkDEBUGCODE(fPart.fCurve.fVerb = segment->verb());
+ segment->subDivide(fStart, fEnd, &fPart.fCurve);
+ fOriginalCurvePart = fPart.fCurve;
const SkPath::Verb verb = segment->verb();
- if (verb != SkPath::kLine_Verb
- && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
+ fPart.setCurveHullSweep(verb);
+ if (SkPath::kLine_Verb != verb && !fPart.isCurve()) {
SkDLine lineHalf;
- fCurvePart[1] = fCurvePart[SkPathOpsVerbToPoints(verb)];
- fOriginalCurvePart[1] = fCurvePart[1];
- lineHalf[0].set(fCurvePart[0].asSkPoint());
- lineHalf[1].set(fCurvePart[1].asSkPoint());
+ fPart.fCurve[1] = fPart.fCurve[SkPathOpsVerbToPoints(verb)];
+ fOriginalCurvePart[1] = fPart.fCurve[1];
+ lineHalf[0].set(fPart.fCurve[0].asSkPoint());
+ lineHalf[1].set(fPart.fCurve[1].asSkPoint());
fTangentHalf.lineEndPoints(lineHalf);
fSide = 0;
}
lineHalf[1].set(cP1);
fTangentHalf.lineEndPoints(lineHalf);
fSide = 0;
- fIsCurve = false;
} return;
case SkPath::kQuad_Verb:
case SkPath::kConic_Verb: {
SkLineParameters tangentPart;
- (void) tangentPart.quadEndPoints(fCurvePart.fQuad);
- fSide = -tangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only
+ (void) tangentPart.quadEndPoints(fPart.fCurve.fQuad);
+ fSide = -tangentPart.pointDistance(fPart.fCurve[2]); // not normalized -- compare sign only
} break;
case SkPath::kCubic_Verb: {
SkLineParameters tangentPart;
- (void) tangentPart.cubicPart(fCurvePart.fCubic);
- fSide = -tangentPart.pointDistance(fCurvePart[3]);
+ (void) tangentPart.cubicPart(fPart.fCurve.fCubic);
+ fSide = -tangentPart.pointDistance(fPart.fCurve[3]);
double testTs[4];
// OPTIMIZATION: keep inflections precomputed with cubic segment?
int testCount = SkDCubic::FindInflections(pts, testTs);
// OPTIMIZE: could avoid call for t == startT, endT
SkDPoint pt = dcubic_xy_at_t(pts, segment->weight(), testT);
SkLineParameters tangentPart;
- tangentPart.cubicEndPoints(fCurvePart.fCubic);
+ tangentPart.cubicEndPoints(fPart.fCurve.fCubic);
double testSide = tangentPart.pointDistance(pt);
if (fabs(bestSide) < fabs(testSide)) {
bestSide = testSide;
}
const SkOpSegment* segment = fStart->segment();
SkPath::Verb verb = segment->verb();
- fSectorStart = this->findSector(verb, fSweep[0].fX, fSweep[0].fY);
+ fSectorStart = this->findSector(verb, fPart.fSweep[0].fX, fPart.fSweep[0].fY);
if (fSectorStart < 0) {
goto deferTilLater;
}
- if (!fIsCurve) { // if it's a line or line-like, note that both sectors are the same
+ if (!fPart.isCurve()) { // if it's a line or line-like, note that both sectors are the same
SkASSERT(fSectorStart >= 0);
fSectorEnd = fSectorStart;
fSectorMask = 1 << fSectorStart;
return;
}
SkASSERT(SkPath::kLine_Verb != verb);
- fSectorEnd = this->findSector(verb, fSweep[1].fX, fSweep[1].fY);
+ fSectorEnd = this->findSector(verb, fPart.fSweep[1].fX, fPart.fSweep[1].fY);
if (fSectorEnd < 0) {
deferTilLater:
fSectorStart = fSectorEnd = -1;
// - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x
// m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
// m = v1.cross(v2) / v1.dot(v2)
- const SkDVector* sweep = fSweep;
- const SkDVector* tweep = rh->fSweep;
+ const SkDVector* sweep = fPart.fSweep;
+ const SkDVector* tweep = rh->fPart.fSweep;
double s0dt0 = sweep[0].dot(tweep[0]);
if (!s0dt0) {
return true;
bool midToSide(const SkOpAngle* rh, bool* inside) const;
bool oppositePlanes(const SkOpAngle* rh) const;
bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
- void setCurveHullSweep();
void setSector();
void setSpans();
bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
SkDCurve fOriginalCurvePart; // the curve from start to end
- SkDCurve fCurvePart; // the curve from start to end offset as needed
+ SkDCurveSweep fPart; // the curve from start to end offset as needed
double fSide;
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
SkOpAngle* fNext;
SkOpSpanBase* fLastMarked;
- SkDVector fSweep[2];
SkOpSpanBase* fStart;
SkOpSpanBase* fEnd;
SkOpSpanBase* fComputedEnd;
int fSectorMask;
int8_t fSectorStart; // in 32nds of a circle
int8_t fSectorEnd;
- bool fIsCurve;
bool fUnorderable;
- bool fUnorderedSweep; // set when a cubic's first control point between the sweep vectors
bool fComputeSector;
bool fComputedSector;
bool fCheckCoincidence;
return true;
}
SkASSERT(contourHead.next());
+ contourHead.joinAllSegments();
contourHead.resetReverse();
bool writePath = false;
SkOpSpan* topSpan;
}
void SkOpContour::toPath(SkPathWriter* path) const {
- const SkPoint& pt = fHead.pts()[0];
- path->deferredMove(pt);
const SkOpSegment* segment = &fHead;
do {
SkAssertResult(segment->addCurveTo(segment->head(), segment->tail(), path));
} while ((segment = segment->next()));
- path->close();
+ path->finishContour();
+ path->assemble();
}
void SkOpContour::toReversePath(SkPathWriter* path) const {
- const SkPoint& pt = fTail->pts()[0];
- path->deferredMove(pt);
const SkOpSegment* segment = fTail;
do {
SkAssertResult(segment->addCurveTo(segment->tail(), segment->head(), path));
} while ((segment = segment->prev()));
- path->close();
+ path->finishContour();
+ path->assemble();
}
SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
return *result;
}
- SkOpContour* appendContour() {
- SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->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;
}
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 {
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;
};
class SkOpContourHead : public SkOpContour {
+public:
+ SkOpContour* appendContour() {
+ SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
+ 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 {
+ 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);
+ }
+
};
#endif
preFetch();
}
-int SkOpEdgeBuilder::count() const {
- SkOpContour* contour = fContoursHead;
- int count = 0;
- while (contour) {
- count += contour->count() > 0;
- contour = contour->next();
- }
- return count;
-}
-
bool SkOpEdgeBuilder::finish() {
fOperand = false;
if (fUnparseable || !walk()) {
class SkOpEdgeBuilder {
public:
- SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ SkOpEdgeBuilder(const SkPathWriter& path, SkOpContourHead* contours2,
+ SkOpGlobalState* globalState)
: fGlobalState(globalState)
, fPath(path.nativePath())
, fContoursHead(contours2)
init();
}
- SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+ SkOpEdgeBuilder(const SkPath& path, SkOpContourHead* contours2, SkOpGlobalState* globalState)
: fGlobalState(globalState)
, fPath(&path)
, fContoursHead(contours2)
}
}
- int count() const;
bool finish();
const SkOpContour* head() const {
SkTDArray<SkScalar> fWeights;
SkTDArray<uint8_t> fPathVerbs;
SkOpContour* fCurrentContour;
- SkOpContour* fContoursHead;
+ SkOpContourHead* fContoursHead;
SkPathOpsMask fXorMask[2];
int fSecondHalf;
bool fOperand;
bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
SkPathWriter* path) const {
FAIL_IF(start->starter(end)->alreadyAdded());
- SkOpCurve edge;
- const SkPoint* ePtr;
- SkScalar eWeight;
- if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
- ePtr = fPts;
- eWeight = fWeight;
- } else {
- // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
- subDivide(start, end, &edge);
- ePtr = edge.fPts;
- eWeight = edge.fWeight;
- }
- bool reverse = ePtr == fPts && start != &fHead;
- if (reverse) {
- path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
- switch (fVerb) {
- case SkPath::kLine_Verb:
- path->deferredLine(ePtr[0]);
- break;
- case SkPath::kQuad_Verb:
- path->quadTo(ePtr[1], ePtr[0]);
- break;
- case SkPath::kConic_Verb:
- path->conicTo(ePtr[1], ePtr[0], eWeight);
- break;
- case SkPath::kCubic_Verb:
- path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
- break;
- default:
- SkASSERT(0);
- }
- } else {
- path->deferredMoveLine(ePtr[0]);
- switch (fVerb) {
- case SkPath::kLine_Verb:
- path->deferredLine(ePtr[1]);
- break;
- case SkPath::kQuad_Verb:
- path->quadTo(ePtr[1], ePtr[2]);
- break;
- case SkPath::kConic_Verb:
- path->conicTo(ePtr[1], ePtr[2], eWeight);
- break;
- case SkPath::kCubic_Verb:
- path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
- break;
- default:
- SkASSERT(0);
- }
+ SkDCurveSweep curvePart;
+ start->segment()->subDivide(start, end, &curvePart.fCurve);
+ curvePart.setCurveHullSweep(fVerb);
+ SkPath::Verb verb = curvePart.isCurve() ? fVerb : SkPath::kLine_Verb;
+ path->deferredMove(start->ptT());
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ path->deferredLine(end->ptT());
+ break;
+ case SkPath::kQuad_Verb:
+ path->quadTo(curvePart.fCurve.fQuad.fPts[1].asSkPoint(), end->ptT());
+ break;
+ case SkPath::kConic_Verb:
+ path->conicTo(curvePart.fCurve.fConic.fPts[1].asSkPoint(), end->ptT(),
+ curvePart.fCurve.fConic.fWeight);
+ break;
+ case SkPath::kCubic_Verb:
+ path->cubicTo(curvePart.fCurve.fCubic.fPts[1].asSkPoint(),
+ curvePart.fCurve.fCubic.fPts[2].asSkPoint(), end->ptT());
+ break;
+ default:
+ SkASSERT(0);
}
return true;
}
bool isXor() const;
+ void joinEnds(SkOpSegment* start) {
+ fTail.ptT()->addOpp(start->fHead.ptT(), start->fHead.ptT());
+ }
+
const SkPoint& lastPt() const {
return fPts[SkPathOpsVerbToPoints(fVerb)];
}
return true;
}
-class DistanceLessThan {
-public:
- DistanceLessThan(double* distances) : fDistances(distances) { }
- double* fDistances;
- bool operator()(const int one, const int two) {
- return fDistances[one] < fDistances[two];
- }
-};
-
- /*
- check start and end of each contour
- if not the same, record them
- match them up
- connect closest
- reassemble contour pieces into new path
- */
-void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
- SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
- SkOpContourHead contour;
- SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false)
- SkDEBUGPARAMS(nullptr));
-#if DEBUG_SHOW_TEST_NAME
- SkDebugf("</div>\n");
-#endif
-#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s\n", __FUNCTION__);
-#endif
- SkOpEdgeBuilder builder(path, &contour, &globalState);
- builder.finish();
- SkTDArray<const SkOpContour* > runs; // indices of partial contours
- const SkOpContour* eContour = builder.head();
- do {
- if (!eContour->count()) {
- continue;
- }
- const SkPoint& eStart = eContour->start();
- const SkPoint& eEnd = eContour->end();
-#if DEBUG_ASSEMBLE
- SkDebugf("%s contour", __FUNCTION__);
- if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
- SkDebugf("[%d]", runs.count());
- } else {
- SkDebugf(" ");
- }
- SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
- eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
-#endif
- if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
- eContour->toPath(simple);
- continue;
- }
- *runs.append() = eContour;
- } while ((eContour = eContour->next()));
- int count = runs.count();
- if (count == 0) {
- return;
- }
- SkTDArray<int> sLink, eLink;
- sLink.append(count);
- eLink.append(count);
- int rIndex, iIndex;
- for (rIndex = 0; rIndex < count; ++rIndex) {
- sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
- }
- const int ends = count * 2; // all starts and ends
- const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
- SkTDArray<double> distances;
- distances.append(entries);
- for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
- const SkOpContour* oContour = runs[rIndex >> 1];
- const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
- const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
- * ends - rIndex - 1;
- for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
- const SkOpContour* iContour = runs[iIndex >> 1];
- const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
- double dx = iPt.fX - oPt.fX;
- double dy = iPt.fY - oPt.fY;
- double dist = dx * dx + dy * dy;
- distances[row + iIndex] = dist; // oStart distance from iStart
- }
- }
- SkTDArray<int> sortedDist;
- sortedDist.append(entries);
- for (rIndex = 0; rIndex < entries; ++rIndex) {
- sortedDist[rIndex] = rIndex;
- }
- SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
- int remaining = count; // number of start/end pairs
- for (rIndex = 0; rIndex < entries; ++rIndex) {
- int pair = sortedDist[rIndex];
- int row = pair / ends;
- int col = pair - row * ends;
- int thingOne = row < col ? row : ends - row - 2;
- int ndxOne = thingOne >> 1;
- bool endOne = thingOne & 1;
- int* linkOne = endOne ? eLink.begin() : sLink.begin();
- if (linkOne[ndxOne] != SK_MaxS32) {
- continue;
- }
- int thingTwo = row < col ? col : ends - row + col - 1;
- int ndxTwo = thingTwo >> 1;
- bool endTwo = thingTwo & 1;
- int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
- if (linkTwo[ndxTwo] != SK_MaxS32) {
- continue;
- }
- SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
- bool flip = endOne == endTwo;
- linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
- linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
- if (!--remaining) {
- break;
- }
- }
- SkASSERT(!remaining);
-#if DEBUG_ASSEMBLE
- for (rIndex = 0; rIndex < count; ++rIndex) {
- int s = sLink[rIndex];
- int e = eLink[rIndex];
- SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
- s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
- }
-#endif
- rIndex = 0;
- do {
- bool forward = true;
- bool first = true;
- int sIndex = sLink[rIndex];
- SkASSERT(sIndex != SK_MaxS32);
- sLink[rIndex] = SK_MaxS32;
- int eIndex;
- if (sIndex < 0) {
- eIndex = sLink[~sIndex];
- sLink[~sIndex] = SK_MaxS32;
- } else {
- eIndex = eLink[sIndex];
- eLink[sIndex] = SK_MaxS32;
- }
- SkASSERT(eIndex != SK_MaxS32);
-#if DEBUG_ASSEMBLE
- SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
- sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
- eIndex < 0 ? ~eIndex : eIndex);
-#endif
- do {
- const SkOpContour* contour = runs[rIndex];
- if (first) {
- first = false;
- const SkPoint* startPtr = &contour->start();
- simple->deferredMove(startPtr[0]);
- }
- if (forward) {
- contour->toPartialForward(simple);
- } else {
- contour->toPartialBackward(simple);
- }
-#if DEBUG_ASSEMBLE
- SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
- eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
- sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
-#endif
- if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
- simple->close();
- break;
- }
- if (forward) {
- eIndex = eLink[rIndex];
- SkASSERT(eIndex != SK_MaxS32);
- eLink[rIndex] = SK_MaxS32;
- if (eIndex >= 0) {
- SkASSERT(sLink[eIndex] == rIndex);
- sLink[eIndex] = SK_MaxS32;
- } else {
- SkASSERT(eLink[~eIndex] == ~rIndex);
- eLink[~eIndex] = SK_MaxS32;
- }
- } else {
- eIndex = sLink[rIndex];
- SkASSERT(eIndex != SK_MaxS32);
- sLink[rIndex] = SK_MaxS32;
- if (eIndex >= 0) {
- SkASSERT(eLink[eIndex] == rIndex);
- eLink[eIndex] = SK_MaxS32;
- } else {
- SkASSERT(sLink[~eIndex] == ~rIndex);
- sLink[~eIndex] = SK_MaxS32;
- }
- }
- rIndex = eIndex;
- if (rIndex < 0) {
- forward ^= 1;
- rIndex = ~rIndex;
- }
- } while (true);
- for (rIndex = 0; rIndex < count; ++rIndex) {
- if (sLink[rIndex] != SK_MaxS32) {
- break;
- }
- }
- } while (rIndex < count);
-#if DEBUG_ASSEMBLE
- for (rIndex = 0; rIndex < count; ++rIndex) {
- SkASSERT(sLink[rIndex] == SK_MaxS32);
- SkASSERT(eLink[rIndex] == SK_MaxS32);
- }
-#endif
-}
-
static void calcAngles(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
do {
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
bool* sortable);
-void Assemble(const SkPathWriter& path, SkPathWriter* simple);
SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
SkOpSpan* FindSortableTop(SkOpContourHead* );
bounds->set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
}
+
+void SkDCurveSweep::setCurveHullSweep(SkPath::Verb verb) {
+ fOrdered = true;
+ fSweep[0] = fCurve[1] - fCurve[0];
+ if (SkPath::kLine_Verb == verb) {
+ fSweep[1] = fSweep[0];
+ fIsCurve = false;
+ return;
+ }
+ fSweep[1] = fCurve[2] - fCurve[0];
+ // OPTIMIZE: I do the following float check a lot -- probably need a
+ // central place for this val-is-small-compared-to-curve check
+ double maxVal = 0;
+ for (int index = 0; index < SkPathOpsVerbToPoints(verb); ++index) {
+ maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurve[index].fX),
+ SkTAbs(fCurve[index].fY)));
+ }
+ {
+ if (SkPath::kCubic_Verb != verb) {
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
+ fSweep[0] = fSweep[1];
+ }
+ goto setIsCurve;
+ }
+ SkDVector thirdSweep = fCurve[3] - fCurve[0];
+ if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
+ fSweep[0] = fSweep[1];
+ fSweep[1] = thirdSweep;
+ if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+ && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
+ fSweep[0] = fSweep[1];
+ fCurve[1] = fCurve[3];
+ }
+ goto setIsCurve;
+ }
+ double s1x3 = fSweep[0].crossCheck(thirdSweep);
+ double s3x2 = thirdSweep.crossCheck(fSweep[1]);
+ if (s1x3 * s3x2 >= 0) { // if third vector is on or between first two vectors
+ goto setIsCurve;
+ }
+ double s2x1 = fSweep[1].crossCheck(fSweep[0]);
+ // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble
+ // probably such wide sweeps should be artificially subdivided earlier so that never happens
+ SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0);
+ if (s3x2 * s2x1 < 0) {
+ SkASSERT(s2x1 * s1x3 > 0);
+ fSweep[0] = fSweep[1];
+ fOrdered = false;
+ }
+ fSweep[1] = thirdSweep;
+ }
+setIsCurve:
+ fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0;
+}
double s, double e, SkPathOpsBounds*);
};
+class SkDCurveSweep {
+public:
+ bool isCurve() const { return fIsCurve; }
+ bool isOrdered() const { return fOrdered; }
+ void setCurveHullSweep(SkPath::Verb verb);
+
+ SkDCurve fCurve;
+ SkDVector fSweep[2];
+private:
+ bool fIsCurve;
+ bool fOrdered; // cleared when a cubic's control point isn't between the sweep vectors
+
+};
extern SkDPoint (SkDCurve::* const Top[])(const SkPoint curve[], SkScalar cWeight,
double tStart, double tEnd, double* topT);
SkString result;
switch (this->segment()->verb()) {
case SkPath::kLine_Verb:
- result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
+ result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
case SkPath::kQuad_Verb:
- result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
+ result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
case SkPath::kConic_Verb:
result.printf(CONIC_DEBUG_STR " id=%d",
- CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
+ CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
this->segment()->debugID());
break;
case SkPath::kCubic_Verb:
- result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
+ result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
this->segment()->debugID());
break;
default:
if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
-#define DEBUG_UNDER_DEVELOPMENT 1
+#define DEBUG_UNDER_DEVELOPMENT 01
#if FORCE_RELEASE
#define DEBUG_ASSEMBLE 1
#define DEBUG_COINCIDENCE 01
#define DEBUG_COINCIDENCE_ORDER 0 // tight arc quads may generate out-of-order coincdence spans
-#define DEBUG_COINCIDENCE_VERBOSE 01
+#define DEBUG_COINCIDENCE_VERBOSE 0
#define DEBUG_CUBIC_BINARY_SEARCH 0
#define DEBUG_CUBIC_SPLIT 1
#define DEBUG_DUMP_SEGMENTS 1
current->markDone(spanStart);
}
}
- simple->close();
+ simple->finishContour();
} else {
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
}
} while (true);
} while (true);
- return simple->someAssemblyRequired();
+ return true;
}
// pretty picture:
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
// turn path into list of segments
- SkOpEdgeBuilder builder(*minuend, &contour, &globalState);
+ SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
if (builder.unparseable()) {
return false;
}
result->reset();
result->setFillType(fillType);
SkPathWriter wrapper(*result);
- bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
- { // if some edges could not be resolved, assemble remaining fragments
- SkPath temp;
- temp.setFillType(fillType);
- SkPathWriter assembled(temp);
- Assemble(wrapper, &assembled);
- *result = *assembled.nativePath();
- result->setFillType(fillType);
+ if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
+ return false;
}
+ wrapper.assemble(); // if some edges could not be resolved, assemble remaining
#if DEBUG_T_SECT_LOOP_COUNT
{
SkAutoMutexAcquire autoM(debugWorstLoop);
#include "SkPathOpsCommon.h"
#include "SkPathWriter.h"
-static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
+static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple) {
bool unsortable = false;
do {
SkOpSpan* span = FindSortableTop(contourList);
&& !simple->isClosed()) {
// FIXME: put in the next two lines to avoid handling already added
if (start->starter(end)->checkAlreadyAdded()) {
- simple->close();
+ simple->finishContour();
} else if (!current->addCurveTo(start, end, simple)) {
return false;
}
current->markDone(spanStart);
}
}
- simple->close();
+ simple->finishContour();
} else {
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
}
} while (true);
} while (true);
- *closable = !simple->someAssemblyRequired();
return true;
}
// returns true if all edges were processed
-static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
+static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple) {
SkOpSegment* current;
SkOpSpanBase* start;
SkOpSpanBase* end;
bool unsortable = false;
- *closable = true;
while ((current = FindUndone(contourList, &start, &end))) {
do {
if (!unsortable && current->done()) {
}
current->markDone(spanStart);
}
- *closable = false;
}
- simple->close();
+ simple->finishContour();
SkPathOpsDebug::ShowActiveSpans(contourList);
}
return true;
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
- SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
if (!builder.finish()) {
return false;
}
result->reset();
result->setFillType(fillType);
SkPathWriter wrapper(*result);
- bool closable SK_INIT_TO_AVOID_WARNING;
- if (builder.xorMask() == kWinding_PathOpsMask
- ? !bridgeWinding(contourList, &wrapper, &closable)
- : !bridgeXor(contourList, &wrapper, &closable)) {
+ if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
+ : !bridgeXor(contourList, &wrapper)) {
return false;
}
- if (!closable)
- { // if some edges could not be resolved, assemble remaining fragments
- SkPath temp;
- temp.setFillType(fillType);
- SkPathWriter assembled(temp);
- Assemble(wrapper, &assembled);
- *result = *assembled.nativePath();
- result->setFillType(fillType);
- }
+ wrapper.assemble(); // if some edges could not be resolved, assemble remaining
if (scaleFactor > 1) {
ScalePath(*result, scaleFactor, result);
}
} else {
workingPath = &path;
}
- SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+ SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
if (!builder.finish()) {
return false;
}
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "SkOpSpan.h"
#include "SkPathOpsPoint.h"
#include "SkPathWriter.h"
+#include "SkTSort.h"
// wrap path to keep track of whether the contour is initialized and non-empty
SkPathWriter::SkPathWriter(SkPath& path)
: fPathPtr(&path)
- , fCloses(0)
- , fMoves(0)
{
init();
}
void SkPathWriter::close() {
- if (!fHasMove) {
+ if (fCurrent.isEmpty()) {
return;
}
- bool callClose = isClosed();
- lineTo();
- if (fEmpty) {
- return;
- }
- if (callClose) {
+ SkASSERT(this->isClosed());
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("path.close();\n");
+ SkDebugf("path.close();\n");
#endif
- fPathPtr->close();
- fCloses++;
- }
+ fCurrent.close();
+ fPathPtr->addPath(fCurrent);
+ fCurrent.reset();
init();
}
-void SkPathWriter::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
- lineTo();
- if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
- deferredLine(pt2);
- return;
- }
- moveTo();
- fDefer[1] = pt2;
- nudge();
- fDefer[0] = fDefer[1];
+void SkPathWriter::conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight) {
+ this->update(pt2);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n",
- pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
+ pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY, weight);
#endif
- fPathPtr->conicTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
- fEmpty = false;
+ fCurrent.conicTo(pt1, pt2->fPt, weight);
}
-void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
- lineTo();
- if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)
- && AlmostEqualUlps(pt2, pt3)) {
- deferredLine(pt3);
- return;
- }
- moveTo();
- fDefer[1] = pt3;
- nudge();
- fDefer[0] = fDefer[1];
+void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3) {
+ this->update(pt3);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
- pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
+ pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3->fPt.fX, pt3->fPt.fY);
#endif
- fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
- fEmpty = false;
+ fCurrent.cubicTo(pt1, pt2, pt3->fPt);
}
-void SkPathWriter::deferredLine(const SkPoint& pt) {
- if (pt == fDefer[1]) {
+void SkPathWriter::deferredLine(const SkOpPtT* pt) {
+ SkASSERT(fFirstPtT);
+ SkASSERT(fDefer[0]);
+ if (fDefer[0] == pt) {
+ // FIXME: why we're adding a degenerate line? Caller should have preflighted this.
+ return;
+ }
+ if (pt->contains(fDefer[0])) {
+ // FIXME: why we're adding a degenerate line?
return;
}
- if (changedSlopes(pt)) {
- lineTo();
+ SkASSERT(!this->matchedLast(pt));
+ if (fDefer[1] && this->changedSlopes(pt)) {
+ this->lineTo();
fDefer[0] = fDefer[1];
}
fDefer[1] = pt;
}
-void SkPathWriter::deferredMove(const SkPoint& pt) {
- fMoved = true;
- fHasMove = true;
- fEmpty = true;
- fDefer[0] = fDefer[1] = pt;
-}
-
-void SkPathWriter::deferredMoveLine(const SkPoint& pt) {
- if (!fHasMove) {
- deferredMove(pt);
+void SkPathWriter::deferredMove(const SkOpPtT* pt) {
+ if (!fDefer[1]) {
+ fFirstPtT = fDefer[0] = pt;
+ return;
+ }
+ SkASSERT(fDefer[0]);
+ if (!this->matchedLast(pt)) {
+ this->finishContour();
+ fFirstPtT = fDefer[0] = pt;
}
- deferredLine(pt);
}
-bool SkPathWriter::hasMove() const {
- return fHasMove;
+void SkPathWriter::finishContour() {
+ if (!this->matchedLast(fDefer[0])) {
+ this->lineTo();
+ }
+ if (fCurrent.isEmpty()) {
+ return;
+ }
+ if (this->isClosed()) {
+ this->close();
+ } else {
+ SkASSERT(fDefer[1]);
+ fEndPtTs.push(fFirstPtT);
+ fEndPtTs.push(fDefer[1]);
+ fPartials.push_back(fCurrent);
+ this->init();
+ }
}
void SkPathWriter::init() {
- fEmpty = true;
- fHasMove = false;
- fMoved = false;
+ fCurrent.reset();
+ fFirstPtT = fDefer[0] = fDefer[1] = nullptr;
}
bool SkPathWriter::isClosed() const {
- return !fEmpty && SkDPoint::ApproximatelyEqual(fFirstPt, fDefer[1]);
+ return this->matchedLast(fFirstPtT);
}
void SkPathWriter::lineTo() {
- if (fDefer[0] == fDefer[1]) {
- return;
+ if (fCurrent.isEmpty()) {
+ this->moveTo();
}
- moveTo();
- nudge();
- fEmpty = false;
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
+ SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1]->fPt.fX, fDefer[1]->fPt.fY);
#endif
- fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
- fDefer[0] = fDefer[1];
+ fCurrent.lineTo(fDefer[1]->fPt);
}
-const SkPath* SkPathWriter::nativePath() const {
- return fPathPtr;
+bool SkPathWriter::matchedLast(const SkOpPtT* test) const {
+ if (test == fDefer[1]) {
+ return true;
+ }
+ if (!test) {
+ return false;
+ }
+ if (!fDefer[1]) {
+ return false;
+ }
+ return test->contains(fDefer[1]);
}
-void SkPathWriter::nudge() {
- if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
- || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
- return;
- }
- fDefer[1] = fFirstPt;
+void SkPathWriter::moveTo() {
+#if DEBUG_PATH_CONSTRUCTION
+ SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fFirstPtT->fPt.fX, fFirstPtT->fPt.fY);
+#endif
+ fCurrent.moveTo(fFirstPtT->fPt);
}
-void SkPathWriter::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
- lineTo();
- if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
- deferredLine(pt2);
- return;
- }
- moveTo();
- fDefer[1] = pt2;
- nudge();
- fDefer[0] = fDefer[1];
+void SkPathWriter::quadTo(const SkPoint& pt1, const SkOpPtT* pt2) {
+ this->update(pt2);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
- pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
+ pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY);
#endif
- fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
- fEmpty = false;
+ fCurrent.quadTo(pt1, pt2->fPt);
}
-bool SkPathWriter::someAssemblyRequired() const {
- return fCloses < fMoves;
+void SkPathWriter::update(const SkOpPtT* pt) {
+ if (!fDefer[1]) {
+ this->moveTo();
+ } else if (!this->matchedLast(fDefer[0])) {
+ this->lineTo();
+ }
+ fDefer[0] = fDefer[1] = pt; // set both to know that there is not a pending deferred line
+}
+
+bool SkPathWriter::someAssemblyRequired() {
+ this->finishContour();
+ return fEndPtTs.count() > 0;
}
-bool SkPathWriter::changedSlopes(const SkPoint& pt) const {
- if (fDefer[0] == fDefer[1]) {
+bool SkPathWriter::changedSlopes(const SkOpPtT* ptT) const {
+ if (matchedLast(fDefer[0])) {
return false;
}
- SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
- SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
- SkScalar lineDx = pt.fX - fDefer[1].fX;
- SkScalar lineDy = pt.fY - fDefer[1].fY;
- return deferDx * lineDy != deferDy * lineDx;
+ SkVector deferDxdy = fDefer[1]->fPt - fDefer[0]->fPt;
+ SkVector lineDxdy = ptT->fPt - fDefer[1]->fPt;
+ return deferDxdy.fX * lineDxdy.fY != deferDxdy.fY * lineDxdy.fX;
}
-void SkPathWriter::moveTo() {
- if (!fMoved) {
+class DistanceLessThan {
+public:
+ DistanceLessThan(double* distances) : fDistances(distances) { }
+ double* fDistances;
+ bool operator()(const int one, const int two) {
+ return fDistances[one] < fDistances[two];
+ }
+};
+
+ /*
+ check start and end of each contour
+ if not the same, record them
+ match them up
+ connect closest
+ reassemble contour pieces into new path
+ */
+void SkPathWriter::assemble() {
+#if DEBUG_SHOW_TEST_NAME
+ SkDebugf("</div>\n");
+#endif
+ if (!this->someAssemblyRequired()) {
return;
}
- fFirstPt = fDefer[0];
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
+ SkDebugf("%s\n", __FUNCTION__);
+#endif
+ SkOpPtT const* const* runs = fEndPtTs.begin(); // starts, ends of partial contours
+ int endCount = fEndPtTs.count(); // all starts and ends
+ SkASSERT(endCount > 0);
+ SkASSERT(endCount == fPartials.count() * 2);
+#if DEBUG_ASSEMBLE
+ for (int index = 0; index < endCount; index += 2) {
+ const SkOpPtT* eStart = runs[index];
+ const SkOpPtT* eEnd = runs[index + 1];
+ SkASSERT(eStart != eEnd);
+ SkASSERT(!eStart->contains(eEnd));
+ SkDebugf("%s contour start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n", __FUNCTION__,
+ eStart->fPt.fX, eStart->fPt.fY, eEnd->fPt.fX, eEnd->fPt.fY);
+ }
+#endif
+ SkTDArray<int> sLink, eLink;
+ int linkCount = endCount / 2; // number of partial contours
+ sLink.append(linkCount);
+ eLink.append(linkCount);
+ int rIndex, iIndex;
+ for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+ sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
+ }
+ const int entries = endCount * (endCount - 1) / 2; // folded triangle
+ SkSTArray<8, double, true> distances(entries);
+ SkSTArray<8, int, true> sortedDist(entries);
+ SkSTArray<8, int, true> distLookup(entries);
+ int rRow = 0;
+ int dIndex = 0;
+ for (rIndex = 0; rIndex < endCount - 1; ++rIndex) {
+ const SkOpPtT* oPtT = runs[rIndex];
+ for (iIndex = rIndex + 1; iIndex < endCount; ++iIndex) {
+ const SkOpPtT* iPtT = runs[iIndex];
+ double dx = iPtT->fPt.fX - oPtT->fPt.fX;
+ double dy = iPtT->fPt.fY - oPtT->fPt.fY;
+ double dist = dx * dx + dy * dy;
+ distLookup.push_back(rRow + iIndex);
+ distances.push_back(dist); // oStart distance from iStart
+ sortedDist.push_back(dIndex++);
+ }
+ rRow += endCount;
+ }
+ SkASSERT(dIndex == entries);
+ SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
+ int remaining = linkCount; // number of start/end pairs
+ for (rIndex = 0; rIndex < entries; ++rIndex) {
+ int pair = sortedDist[rIndex];
+ pair = distLookup[pair];
+ int row = pair / endCount;
+ int col = pair - row * endCount;
+ int ndxOne = row >> 1;
+ bool endOne = row & 1;
+ int* linkOne = endOne ? eLink.begin() : sLink.begin();
+ if (linkOne[ndxOne] != SK_MaxS32) {
+ continue;
+ }
+ int ndxTwo = col >> 1;
+ bool endTwo = col & 1;
+ int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
+ if (linkTwo[ndxTwo] != SK_MaxS32) {
+ continue;
+ }
+ SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
+ bool flip = endOne == endTwo;
+ linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
+ linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
+ if (!--remaining) {
+ break;
+ }
+ }
+ SkASSERT(!remaining);
+#if DEBUG_ASSEMBLE
+ for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+ int s = sLink[rIndex];
+ int e = eLink[rIndex];
+ SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
+ s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
+ }
+#endif
+ rIndex = 0;
+ do {
+ bool forward = true;
+ bool first = true;
+ int sIndex = sLink[rIndex];
+ SkASSERT(sIndex != SK_MaxS32);
+ sLink[rIndex] = SK_MaxS32;
+ int eIndex;
+ if (sIndex < 0) {
+ eIndex = sLink[~sIndex];
+ sLink[~sIndex] = SK_MaxS32;
+ } else {
+ eIndex = eLink[sIndex];
+ eLink[sIndex] = SK_MaxS32;
+ }
+ SkASSERT(eIndex != SK_MaxS32);
+#if DEBUG_ASSEMBLE
+ SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
+ sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
+ eIndex < 0 ? ~eIndex : eIndex);
+#endif
+ do {
+ const SkPath& contour = fPartials[rIndex];
+ if (forward) {
+ fPathPtr->addPath(contour,
+ first ? SkPath::kAppend_AddPathMode : SkPath::kExtend_AddPathMode);
+ } else {
+ SkASSERT(!first);
+ fPathPtr->reverseAddPath(contour);
+ }
+ if (first) {
+ first = false;
+ }
+#if DEBUG_ASSEMBLE
+ SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
+ eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
+ sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
+#endif
+ if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
+ fPathPtr->close();
+ break;
+ }
+ if (forward) {
+ eIndex = eLink[rIndex];
+ SkASSERT(eIndex != SK_MaxS32);
+ eLink[rIndex] = SK_MaxS32;
+ if (eIndex >= 0) {
+ SkASSERT(sLink[eIndex] == rIndex);
+ sLink[eIndex] = SK_MaxS32;
+ } else {
+ SkASSERT(eLink[~eIndex] == ~rIndex);
+ eLink[~eIndex] = SK_MaxS32;
+ }
+ } else {
+ eIndex = sLink[rIndex];
+ SkASSERT(eIndex != SK_MaxS32);
+ sLink[rIndex] = SK_MaxS32;
+ if (eIndex >= 0) {
+ SkASSERT(eLink[eIndex] == rIndex);
+ eLink[eIndex] = SK_MaxS32;
+ } else {
+ SkASSERT(sLink[~eIndex] == ~rIndex);
+ sLink[~eIndex] = SK_MaxS32;
+ }
+ }
+ rIndex = eIndex;
+ if (rIndex < 0) {
+ forward ^= 1;
+ rIndex = ~rIndex;
+ }
+ } while (true);
+ for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+ if (sLink[rIndex] != SK_MaxS32) {
+ break;
+ }
+ }
+ } while (rIndex < linkCount);
+#if DEBUG_ASSEMBLE
+ for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+ SkASSERT(sLink[rIndex] == SK_MaxS32);
+ SkASSERT(eLink[rIndex] == SK_MaxS32);
+ }
#endif
- fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
- fMoved = false;
- fMoves++;
+ return;
}
#define SkPathWriter_DEFINED
#include "SkPath.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
+
+class SkOpPtT;
+
+// Construct the path one contour at a time.
+// If the contour is closed, copy it to the final output.
+// Otherwise, keep the partial contour for later assembly.
class SkPathWriter {
public:
SkPathWriter(SkPath& path);
- void close();
- void conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight);
- void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3);
- void deferredLine(const SkPoint& pt);
- void deferredMove(const SkPoint& pt);
- void deferredMoveLine(const SkPoint& pt);
- bool hasMove() const;
+ void assemble();
+ void conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight);
+ void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3);
+ void deferredLine(const SkOpPtT* pt);
+ void deferredMove(const SkOpPtT* pt);
+ void finishContour();
+ bool hasMove() const { return !fFirstPtT; }
void init();
bool isClosed() const;
- bool isEmpty() const { return fEmpty; }
- void lineTo();
- const SkPath* nativePath() const;
- void nudge();
- void quadTo(const SkPoint& pt1, const SkPoint& pt2);
- bool someAssemblyRequired() const;
+ const SkPath* nativePath() const { return fPathPtr; }
+ void quadTo(const SkPoint& pt1, const SkOpPtT* pt2);
private:
- bool changedSlopes(const SkPoint& pt) const;
+ bool changedSlopes(const SkOpPtT* pt) const;
+ void close();
+ const SkTDArray<const SkOpPtT*>& endPtTs() const { return fEndPtTs; }
+ void lineTo();
+ bool matchedLast(const SkOpPtT*) const;
void moveTo();
+ const SkTArray<SkPath>& partials() const { return fPartials; }
+ bool someAssemblyRequired();
+ void update(const SkOpPtT* pt);
- SkPath* fPathPtr;
- SkPoint fDefer[2];
- SkPoint fFirstPt;
- int fCloses;
- int fMoves;
- bool fEmpty;
- bool fHasMove;
- bool fMoved;
+ SkPath fCurrent; // contour under construction
+ SkTArray<SkPath> fPartials; // contours with mismatched starts and ends
+ SkTDArray<const SkOpPtT*> fEndPtTs; // possible pt values for partial starts and ends
+ SkPath* fPathPtr; // closed contours are written here
+ const SkOpPtT* fDefer[2]; // [0] deferred move, [1] deferred line
+ const SkOpPtT* fFirstPtT; // first in current contour
};
#endif /* defined(__PathOps__SkPathWriter__) */
const SkOpAngle* first = this;
const SkOpAngle* next = this;
do {
- next->fCurvePart.dumpID(next->segment()->debugID());
+ next->fPart.fCurve.dumpID(next->segment()->debugID());
next = next->fNext;
} while (next && next != first);
}