Rewriting path writer

The path writer takes constructs the output path out of
curves that satisfy the pathop operation.

Curves contain lists of t/point pairs that may not be
comparable to each other. To match up curve ends in the
output path, look for adjacent curves to have a shared
membership rather than comparing point values.

Use path utilities to connect partial curve lists into
closed contours.

Share the angle code that determines if a curve has become
a degenerate line with the path writer.

Clean up some code on the way, and delete some unused
functions.

TBR=reed@google.com
BUG=5188
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2321973005

Review-Url: https://codereview.chromium.org/2321973005
This commit is contained in:
caryclark 2016-09-14 07:18:20 -07:00 committed by Commit bot
parent 8bbcd5aab8
commit eed356d281
21 changed files with 544 additions and 596 deletions

View File

@ -62,11 +62,11 @@ bool SkOpAngle::after(SkOpAngle* test) {
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;
@ -177,15 +177,15 @@ bool SkOpAngle::after(SkOpAngle* test) {
// 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);
@ -222,16 +222,16 @@ bool SkOpAngle::checkCrossesZero() const {
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);
@ -256,8 +256,8 @@ bool SkOpAngle::checkParallel(SkOpAngle* rh) {
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;
@ -321,8 +321,8 @@ recomputeSector:
}
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]);
@ -352,8 +352,8 @@ int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
// 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;
@ -392,8 +392,8 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
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);
}
@ -464,7 +464,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* 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);
@ -482,7 +482,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
}
}
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];
@ -524,7 +524,7 @@ bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
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);
@ -764,8 +764,8 @@ bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
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();
@ -787,7 +787,7 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
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;
}
@ -832,59 +832,6 @@ void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
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;
@ -894,21 +841,20 @@ void SkOpAngle::setSpans() {
}
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;
}
@ -921,18 +867,17 @@ void SkOpAngle::setSpans() {
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);
@ -964,7 +909,7 @@ void SkOpAngle::setSpans() {
// 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;
@ -984,18 +929,18 @@ void SkOpAngle::setSector() {
}
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;
@ -1045,8 +990,8 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
// - 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;

View File

@ -107,27 +107,23 @@ private:
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;

View File

@ -54,6 +54,7 @@ bool FixWinding(SkPath* path) {
return true;
}
SkASSERT(contourHead.next());
contourHead.joinAllSegments();
contourHead.resetReverse();
bool writePath = false;
SkOpSpan* topSpan;

View File

@ -38,23 +38,21 @@ SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
}
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) {

View File

@ -63,18 +63,6 @@ public:
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;
}
@ -219,6 +207,15 @@ public:
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 {
@ -289,22 +286,6 @@ public:
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;
@ -416,6 +397,42 @@ private:
};
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

View File

@ -26,16 +26,6 @@ void SkOpEdgeBuilder::addOperand(const SkPath& path) {
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()) {

View File

@ -12,7 +12,8 @@
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)
@ -20,7 +21,7 @@ public:
init();
}
SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
SkOpEdgeBuilder(const SkPath& path, SkOpContourHead* contours2, SkOpGlobalState* globalState)
: fGlobalState(globalState)
, fPath(&path)
, fContoursHead(contours2)
@ -37,7 +38,6 @@ public:
}
}
int count() const;
bool finish();
const SkOpContour* head() const {
@ -60,7 +60,7 @@ private:
SkTDArray<SkScalar> fWeights;
SkTDArray<uint8_t> fPathVerbs;
SkOpContour* fCurrentContour;
SkOpContour* fContoursHead;
SkOpContourHead* fContoursHead;
SkPathOpsMask fXorMask[2];
int fSecondHalf;
bool fOperand;

View File

@ -162,55 +162,28 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
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;
}

View File

@ -268,6 +268,10 @@ public:
bool isXor() const;
void joinEnds(SkOpSegment* start) {
fTail.ptT()->addOpp(start->fHead.ptT(), start->fHead.ptT());
}
const SkPoint& lastPt() const {
return fPts[SkPathOpsVerbToPoints(fVerb)];
}

View File

@ -198,215 +198,6 @@ bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOd
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 {

View File

@ -16,7 +16,6 @@ class SkPathWriter;
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* );

View File

@ -88,3 +88,58 @@ void SkDCurve::setQuadBounds(const SkPoint curve[3], SkScalar ,
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;
}

View File

@ -82,6 +82,19 @@ struct SkDCurve {
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);

View File

@ -1152,20 +1152,20 @@ SkString SkOpAngle::debugPart() const {
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:

View File

@ -37,7 +37,7 @@
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
@ -79,7 +79,7 @@
#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

View File

@ -152,7 +152,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
current->markDone(spanStart);
}
}
simple->close();
simple->finishContour();
} else {
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
@ -175,7 +175,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
}
} while (true);
} while (true);
return simple->someAssemblyRequired();
return true;
}
// pretty picture:
@ -286,7 +286,7 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
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;
}
@ -327,15 +327,10 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
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);

View File

@ -10,7 +10,7 @@
#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);
@ -38,7 +38,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
&& !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;
}
@ -69,7 +69,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
current->markDone(spanStart);
}
}
simple->close();
simple->finishContour();
} else {
SkOpSpanBase* last = current->markAndChaseDone(start, end);
if (last && !last->chased()) {
@ -92,17 +92,15 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
}
} 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()) {
@ -146,9 +144,8 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool*
}
current->markDone(spanStart);
}
*closable = false;
}
simple->close();
simple->finishContour();
SkPathOpsDebug::ShowActiveSpans(contourList);
}
return true;
@ -186,7 +183,7 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
if (!builder.finish()) {
return false;
}
@ -218,21 +215,11 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
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);
}

View File

@ -23,7 +23,7 @@ bool TightBounds(const SkPath& path, SkRect* result) {
} else {
workingPath = &path;
}
SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
if (!builder.finish()) {
return false;
}

View File

@ -4,181 +4,356 @@
* 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 (changedSlopes(pt)) {
lineTo();
if (pt->contains(fDefer[0])) {
// FIXME: why we're adding a degenerate line?
return;
}
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;
}
void SkPathWriter::nudge() {
if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
|| !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
return;
bool SkPathWriter::matchedLast(const SkOpPtT* test) const {
if (test == fDefer[1]) {
return true;
}
fDefer[1] = fFirstPt;
}
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];
#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);
#endif
fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
fEmpty = false;
}
bool SkPathWriter::someAssemblyRequired() const {
return fCloses < fMoves;
}
bool SkPathWriter::changedSlopes(const SkPoint& pt) const {
if (fDefer[0] == fDefer[1]) {
if (!test) {
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;
if (!fDefer[1]) {
return false;
}
return test->contains(fDefer[1]);
}
void SkPathWriter::moveTo() {
if (!fMoved) {
#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 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, pt2->fPt.fX, pt2->fPt.fY);
#endif
fCurrent.quadTo(pt1, pt2->fPt);
}
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 SkOpPtT* ptT) const {
if (matchedLast(fDefer[0])) {
return false;
}
SkVector deferDxdy = fDefer[1]->fPt - fDefer[0]->fPt;
SkVector lineDxdy = ptT->fPt - fDefer[1]->fPt;
return deferDxdy.fX * lineDxdy.fY != deferDxdy.fY * lineDxdy.fX;
}
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
fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
fMoved = false;
fMoves++;
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
return;
}

View File

@ -8,38 +8,47 @@
#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__) */

View File

@ -896,7 +896,7 @@ void SkOpAngle::dumpCurves() const {
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);
}