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:
parent
8bbcd5aab8
commit
eed356d281
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -54,6 +54,7 @@ bool FixWinding(SkPath* path) {
|
||||
return true;
|
||||
}
|
||||
SkASSERT(contourHead.next());
|
||||
contourHead.joinAllSegments();
|
||||
contourHead.resetReverse();
|
||||
bool writePath = false;
|
||||
SkOpSpan* topSpan;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)];
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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* );
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__) */
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user