pathops coincidence and security rewrite
Most changes stem from working on an examples bracketed by #if DEBUG_UNDER_DEVELOPMENT // tiger These exposed many problems with coincident curves, as well as errors throughout the code. Fixing these errors also fixed a number of fuzzer-inspired bug reports. * Line/Curve Intersections Check to see if the end of the line nearly intersects the curve. This was a FIXME in the old code. * Performance Use a central chunk allocator. Plumb the allocator into the global variable state so that it can be shared. (Note that 'SkGlobalState' is allocated on the stack and is visible to children functions but not other threads.) * Refactor Let SkOpAngle grow up from a structure to a class. Let SkCoincidentSpans grow up from a structure to a class. Rename enum Alias to AliasMatch. * Coincidence Rewrite Add more debugging to coincidence detection. Parallel debugging routines have read-only logic to report the current coincidence state so that steps through the logic can expose whether things got better or worse. More functions can error-out and cause the pathops engine to non-destructively exit. * Accuracy Remove code that adjusted point locations. Instead, offset the curve part so that sorted curves all use the same origin. Reduce the size (and influence) of magic numbers. * Testing The debug suite with verify and the full release suite ./out/Debug/pathops_unittest -v -V ./out/Release/pathops_unittest -v -V -x expose one error. That error is captured as cubics_d3. This error exists in the checked in code as well. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2128633003 Review-Url: https://codereview.chromium.org/2128633003
This commit is contained in:
parent
6451a0cea6
commit
55888e4417
@ -254,8 +254,7 @@ static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
|
||||
SkChunkAlloc* allocator) {
|
||||
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence) {
|
||||
if (test != next) {
|
||||
if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
|
||||
return false;
|
||||
@ -506,12 +505,14 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
|
||||
SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
|
||||
SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
|
||||
wt.segment()->debugValidate();
|
||||
SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
|
||||
allocator);
|
||||
SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAliasMatch,
|
||||
nullptr);
|
||||
wn.segment()->debugValidate();
|
||||
SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
|
||||
allocator);
|
||||
testTAt->addOpp(nextTAt);
|
||||
SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAliasMatch,
|
||||
nullptr);
|
||||
if (testTAt->addOpp(nextTAt)) {
|
||||
testTAt->span()->checkForCollapsedCoincidence();
|
||||
}
|
||||
if (testTAt->fPt != nextTAt->fPt) {
|
||||
testTAt->span()->unaligned();
|
||||
nextTAt->span()->unaligned();
|
||||
@ -540,7 +541,7 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coinc
|
||||
SkTSwap(testTAt, nextTAt);
|
||||
}
|
||||
SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
|
||||
coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
|
||||
coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt);
|
||||
wt.segment()->debugValidate();
|
||||
wn.segment()->debugValidate();
|
||||
coinIndex = -1;
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
class SkOpCoincidence;
|
||||
|
||||
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
|
||||
SkChunkAlloc* allocator);
|
||||
bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence);
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#include "SkIntersections.h"
|
||||
#include "SkPathOpsConic.h"
|
||||
#include "SkPathOpsCurve.h"
|
||||
#include "SkPathOpsLine.h"
|
||||
|
||||
class LineConicIntersections {
|
||||
@ -199,7 +200,22 @@ protected:
|
||||
}
|
||||
fIntersections->insert(conicT, lineT, fConic[cIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on conic
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addLineNearEndPoints() {
|
||||
for (int lIndex = 0; lIndex < 2; ++lIndex) {
|
||||
double lineT = (double) lIndex;
|
||||
if (fIntersections->hasOppT(lineT)) {
|
||||
continue;
|
||||
}
|
||||
double conicT = ((SkDCurve*) &fConic)->nearPoint(SkPath::kConic_Verb,
|
||||
(*fLine)[lIndex], (*fLine)[!lIndex]);
|
||||
if (conicT < 0) {
|
||||
continue;
|
||||
}
|
||||
fIntersections->insert(conicT, lineT, (*fLine)[lIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void addExactHorizontalEndPoints(double left, double right, double y) {
|
||||
@ -225,7 +241,7 @@ protected:
|
||||
}
|
||||
fIntersections->insert(conicT, lineT, fConic[cIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on conic
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addExactVerticalEndPoints(double top, double bottom, double x) {
|
||||
@ -251,7 +267,7 @@ protected:
|
||||
}
|
||||
fIntersections->insert(conicT, lineT, fConic[cIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on conic
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
double findLineT(double t) {
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#include "SkIntersections.h"
|
||||
#include "SkPathOpsCubic.h"
|
||||
#include "SkPathOpsCurve.h"
|
||||
#include "SkPathOpsLine.h"
|
||||
|
||||
/*
|
||||
@ -291,6 +292,22 @@ public:
|
||||
}
|
||||
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
|
||||
}
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addLineNearEndPoints() {
|
||||
for (int lIndex = 0; lIndex < 2; ++lIndex) {
|
||||
double lineT = (double) lIndex;
|
||||
if (fIntersections->hasOppT(lineT)) {
|
||||
continue;
|
||||
}
|
||||
double cubicT = ((SkDCurve*) &fCubic)->nearPoint(SkPath::kCubic_Verb,
|
||||
fLine[lIndex], fLine[!lIndex]);
|
||||
if (cubicT < 0) {
|
||||
continue;
|
||||
}
|
||||
fIntersections->insert(cubicT, lineT, fLine[lIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void addExactHorizontalEndPoints(double left, double right, double y) {
|
||||
@ -316,7 +333,7 @@ public:
|
||||
}
|
||||
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on cubic
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addExactVerticalEndPoints(double top, double bottom, double x) {
|
||||
@ -342,7 +359,7 @@ public:
|
||||
}
|
||||
fIntersections->insert(cubicT, lineT, fCubic[cIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on cubic
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
double findLineT(double t) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "SkIntersections.h"
|
||||
#include "SkPathOpsCurve.h"
|
||||
#include "SkPathOpsLine.h"
|
||||
#include "SkPathOpsQuad.h"
|
||||
|
||||
@ -98,7 +99,7 @@ public:
|
||||
, fLine(&l)
|
||||
, fIntersections(i)
|
||||
, fAllowNear(true) {
|
||||
i->setMax(3); // allow short partial coincidence plus discrete intersection
|
||||
i->setMax(4); // allow short partial coincidence plus discrete intersections
|
||||
}
|
||||
|
||||
LineQuadraticIntersections(const SkDQuad& q)
|
||||
@ -297,7 +298,22 @@ protected:
|
||||
}
|
||||
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on quad
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addLineNearEndPoints() {
|
||||
for (int lIndex = 0; lIndex < 2; ++lIndex) {
|
||||
double lineT = (double) lIndex;
|
||||
if (fIntersections->hasOppT(lineT)) {
|
||||
continue;
|
||||
}
|
||||
double quadT = ((SkDCurve*) &fQuad)->nearPoint(SkPath::kQuad_Verb,
|
||||
(*fLine)[lIndex], (*fLine)[!lIndex]);
|
||||
if (quadT < 0) {
|
||||
continue;
|
||||
}
|
||||
fIntersections->insert(quadT, lineT, (*fLine)[lIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void addExactHorizontalEndPoints(double left, double right, double y) {
|
||||
@ -323,7 +339,7 @@ protected:
|
||||
}
|
||||
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on quad
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
void addExactVerticalEndPoints(double top, double bottom, double x) {
|
||||
@ -349,7 +365,7 @@ protected:
|
||||
}
|
||||
fIntersections->insert(quadT, lineT, fQuad[qIndex]);
|
||||
}
|
||||
// FIXME: see if line end is nearly on quad
|
||||
this->addLineNearEndPoints();
|
||||
}
|
||||
|
||||
double findLineT(double t) {
|
||||
|
@ -112,6 +112,11 @@ public:
|
||||
return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
|
||||
}
|
||||
|
||||
bool hasOppT(double t) const {
|
||||
SkASSERT(t == 0 || t == 1);
|
||||
return fUsed > 0 && (fT[1][0] == t || fT[1][fUsed - 1] == t);
|
||||
}
|
||||
|
||||
int insertSwap(double one, double two, const SkDPoint& pt) {
|
||||
if (fSwap) {
|
||||
return insert(two, one, pt);
|
||||
@ -180,7 +185,6 @@ public:
|
||||
quad.set(a);
|
||||
SkDLine line;
|
||||
line.set(b);
|
||||
fMax = 3; // 2; permit small coincident segment + non-coincident intersection
|
||||
return intersect(quad, line);
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,12 @@ 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]);
|
||||
|
||||
#if DEBUG_ANGLE
|
||||
SkString bugOut;
|
||||
bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
|
||||
@ -150,9 +156,7 @@ bool SkOpAngle::after(SkOpAngle* test) {
|
||||
return COMPARE_RESULT(8, ltOpposite);
|
||||
} else if (ltOrder == 1 && trOrder == 0) {
|
||||
SkASSERT(lrOrder < 0);
|
||||
SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
|
||||
bool trOpposite = oppositePlanes(rh);
|
||||
SkASSERT(ltOpposite != trOpposite);
|
||||
return COMPARE_RESULT(9, trOpposite);
|
||||
} else if (lrOrder == 1 && trOrder == 1) {
|
||||
SkASSERT(ltOrder < 0);
|
||||
@ -175,15 +179,8 @@ bool SkOpAngle::after(SkOpAngle* test) {
|
||||
int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
|
||||
SkASSERT(!fIsCurve);
|
||||
SkASSERT(test->fIsCurve);
|
||||
const SkDPoint& origin = test->fCurvePart[0];
|
||||
SkVector line;
|
||||
if (segment()->verb() == SkPath::kLine_Verb) {
|
||||
const SkPoint* linePts = segment()->pts();
|
||||
int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
|
||||
line = linePts[lineStart ^ 1] - linePts[lineStart];
|
||||
} else {
|
||||
line = (fCurvePart[1] - fCurvePart[0]).asSkVector();
|
||||
}
|
||||
SkDPoint origin = fCurvePart[0];
|
||||
SkDVector line = fCurvePart[1] - origin;
|
||||
float crosses[3];
|
||||
SkPath::Verb testVerb = test->segment()->verb();
|
||||
int iMax = SkPathOpsVerbToPoints(testVerb);
|
||||
@ -244,8 +241,7 @@ bool SkOpAngle::checkParallel(SkOpAngle* rh) {
|
||||
// compute the perpendicular to the endpoints and see where it intersects the opposite curve
|
||||
// if the intersections within the t range, do a cross check on those
|
||||
bool inside;
|
||||
if (!fCurvePart[SkPathOpsVerbToPoints(this->segment()->verb())].approximatelyEqual(
|
||||
rh->fCurvePart[SkPathOpsVerbToPoints(rh->segment()->verb())])) {
|
||||
if (!fEnd->contains(rh->fEnd)) {
|
||||
if (this->endToSide(rh, &inside)) {
|
||||
return inside;
|
||||
}
|
||||
@ -280,7 +276,7 @@ bool SkOpAngle::computeSector() {
|
||||
}
|
||||
fComputedSector = true;
|
||||
bool stepUp = fStart->t() < fEnd->t();
|
||||
const SkOpSpanBase* checkEnd = fEnd;
|
||||
SkOpSpanBase* checkEnd = fEnd;
|
||||
if (checkEnd->final() && stepUp) {
|
||||
fUnorderable = true;
|
||||
return false;
|
||||
@ -398,7 +394,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
|
||||
int rPts = SkPathOpsVerbToPoints(rVerb);
|
||||
SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
|
||||
{{this->fCurvePart[0], this->fCurvePart[lPts]}}};
|
||||
if (rays[0][1] == rays[1][1]) {
|
||||
if (this->fEnd->contains(rh->fEnd)) {
|
||||
return checkParallel(rh);
|
||||
}
|
||||
double smallTs[2] = {-1, -1};
|
||||
@ -538,14 +534,14 @@ bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
|
||||
}
|
||||
double maxWidth = SkTMax(maxX - minX, maxY - minY);
|
||||
endDist /= maxWidth;
|
||||
if (endDist < 5e-11) { // empirically found
|
||||
if (endDist < 5e-12) { // empirically found
|
||||
return false;
|
||||
}
|
||||
const SkDPoint* endPt = &rayEnd[0];
|
||||
SkDPoint oppPt = iEnd.pt(closestEnd);
|
||||
SkDVector vLeft = *endPt - start;
|
||||
SkDVector vRight = oppPt - start;
|
||||
double dir = vLeft.crossCheck(vRight);
|
||||
double dir = vLeft.crossNoNormalCheck(vRight);
|
||||
if (!dir) {
|
||||
return false;
|
||||
}
|
||||
@ -785,7 +781,7 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
|
||||
SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- worth finding earlier
|
||||
return x_ry < rx_y;
|
||||
}
|
||||
if ((result = allOnOneSide(rh)) >= 0) {
|
||||
if ((result = this->allOnOneSide(rh)) >= 0) {
|
||||
return result;
|
||||
}
|
||||
if (fUnorderable || approximately_zero(rh->fSide)) {
|
||||
@ -798,11 +794,10 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
|
||||
if (rh->fUnorderable || approximately_zero(fSide)) {
|
||||
goto unorderable;
|
||||
}
|
||||
}
|
||||
if ((result = convexHullOverlaps(rh)) >= 0) {
|
||||
} else if ((result = this->convexHullOverlaps(rh)) >= 0) {
|
||||
return result;
|
||||
}
|
||||
return endsIntersect(rh);
|
||||
return this->endsIntersect(rh);
|
||||
unorderable:
|
||||
fUnorderable = true;
|
||||
rh->fUnorderable = true;
|
||||
@ -846,8 +841,17 @@ void SkOpAngle::setCurveHullSweep() {
|
||||
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 (!fSweep[0].fX && !fSweep[0].fY) {
|
||||
if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
|
||||
&& roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
|
||||
fSweep[0] = fSweep[1];
|
||||
}
|
||||
return;
|
||||
@ -856,7 +860,8 @@ void SkOpAngle::setCurveHullSweep() {
|
||||
if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
|
||||
fSweep[0] = fSweep[1];
|
||||
fSweep[1] = thirdSweep;
|
||||
if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
|
||||
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;
|
||||
@ -894,6 +899,7 @@ void SkOpAngle::setSpans() {
|
||||
= SK_ScalarNaN);
|
||||
SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
|
||||
segment->subDivide(fStart, fEnd, &fCurvePart);
|
||||
fOriginalCurvePart = fCurvePart;
|
||||
setCurveHullSweep();
|
||||
const SkPath::Verb verb = segment->verb();
|
||||
if (verb != SkPath::kLine_Verb
|
||||
@ -1049,5 +1055,5 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
|
||||
double tDist = tweep[0].length() * m;
|
||||
bool useS = fabs(sDist) < fabs(tDist);
|
||||
double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
|
||||
return mFactor < 2400; // empirically found limit
|
||||
return mFactor < 50; // empirically found limit
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ class SkOpSegment;
|
||||
class SkOpSpanBase;
|
||||
class SkOpSpan;
|
||||
|
||||
struct SkOpAngle {
|
||||
class SkOpAngle {
|
||||
public:
|
||||
enum IncludeType {
|
||||
kUnaryWinding,
|
||||
kUnaryXor,
|
||||
@ -27,14 +28,8 @@ struct SkOpAngle {
|
||||
kBinaryOpp,
|
||||
};
|
||||
|
||||
bool after(SkOpAngle* test);
|
||||
int allOnOneSide(const SkOpAngle* test);
|
||||
bool checkCrossesZero() const;
|
||||
bool checkParallel(SkOpAngle* );
|
||||
bool computeSector();
|
||||
int convexHullOverlaps(const SkOpAngle* ) const;
|
||||
|
||||
const SkOpAngle* debugAngle(int id) const;
|
||||
const SkOpCoincidence* debugCoincidence() const;
|
||||
SkOpContour* debugContour(int id);
|
||||
|
||||
int debugID() const {
|
||||
@ -46,6 +41,7 @@ struct SkOpAngle {
|
||||
#endif
|
||||
|
||||
#if DEBUG_ANGLE
|
||||
bool debugCheckCoincidence() const { return fCheckCoincidence; }
|
||||
void debugCheckNearCoincidence() const;
|
||||
SkString debugPart() const;
|
||||
#endif
|
||||
@ -68,62 +64,56 @@ struct SkOpAngle {
|
||||
return fEnd;
|
||||
}
|
||||
|
||||
bool endsIntersect(SkOpAngle* );
|
||||
bool endToSide(const SkOpAngle* rh, bool* inside) const;
|
||||
int findSector(SkPath::Verb verb, double x, double y) const;
|
||||
SkOpGlobalState* globalState() const;
|
||||
void insert(SkOpAngle* );
|
||||
SkOpSpanBase* lastMarked() const;
|
||||
bool loopContains(const SkOpAngle* ) const;
|
||||
int loopCount() const;
|
||||
bool merge(SkOpAngle* );
|
||||
double midT() const;
|
||||
bool midToSide(const SkOpAngle* rh, bool* inside) const;
|
||||
|
||||
SkOpAngle* next() const {
|
||||
return fNext;
|
||||
}
|
||||
|
||||
bool oppositePlanes(const SkOpAngle* rh) const;
|
||||
bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
|
||||
SkOpAngle* previous() const;
|
||||
|
||||
int sectorEnd() const {
|
||||
return fSectorEnd;
|
||||
}
|
||||
|
||||
int sectorStart() const {
|
||||
return fSectorStart;
|
||||
}
|
||||
|
||||
SkOpSegment* segment() const;
|
||||
|
||||
void set(SkOpSpanBase* start, SkOpSpanBase* end);
|
||||
void setCurveHullSweep();
|
||||
|
||||
void setID(int id) {
|
||||
SkDEBUGCODE(fID = id);
|
||||
}
|
||||
|
||||
void setLastMarked(SkOpSpanBase* marked) {
|
||||
fLastMarked = marked;
|
||||
}
|
||||
|
||||
void setSector();
|
||||
void setSpans();
|
||||
|
||||
SkOpSpanBase* start() const {
|
||||
return fStart;
|
||||
}
|
||||
|
||||
SkOpSpan* starter();
|
||||
bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
|
||||
|
||||
bool unorderable() const {
|
||||
return fUnorderable;
|
||||
}
|
||||
|
||||
SkDCurve fCurvePart; // the curve from start to end
|
||||
private:
|
||||
bool after(SkOpAngle* test);
|
||||
int allOnOneSide(const SkOpAngle* test);
|
||||
bool checkCrossesZero() const;
|
||||
bool checkParallel(SkOpAngle* );
|
||||
bool computeSector();
|
||||
int convexHullOverlaps(const SkOpAngle* ) const;
|
||||
bool endToSide(const SkOpAngle* rh, bool* inside) const;
|
||||
bool endsIntersect(SkOpAngle* );
|
||||
int findSector(SkPath::Verb verb, double x, double y) const;
|
||||
SkOpGlobalState* globalState() const;
|
||||
bool merge(SkOpAngle* );
|
||||
double midT() const;
|
||||
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
|
||||
double fSide;
|
||||
SkLineParameters fTangentHalf; // used only to sort a pair of lines or line-like sections
|
||||
SkOpAngle* fNext;
|
||||
@ -143,6 +133,7 @@ struct SkOpAngle {
|
||||
bool fCheckCoincidence;
|
||||
SkDEBUGCODE(int fID);
|
||||
|
||||
friend class PathOpsAngleTester;
|
||||
};
|
||||
|
||||
|
||||
|
@ -44,13 +44,11 @@ bool FixWinding(SkPath* path) {
|
||||
}
|
||||
SkChunkAlloc allocator(4096);
|
||||
SkOpContourHead contourHead;
|
||||
SkOpGlobalState globalState(nullptr, &contourHead SkDEBUGPARAMS(false)
|
||||
SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
|
||||
SkDEBUGPARAMS(nullptr));
|
||||
SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
|
||||
builder.finish(&allocator);
|
||||
if (!contourHead.next()) {
|
||||
return false;
|
||||
}
|
||||
SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
|
||||
builder.finish();
|
||||
SkASSERT(contourHead.next());
|
||||
contourHead.resetReverse();
|
||||
bool writePath = false;
|
||||
SkOpSpan* topSpan;
|
||||
@ -67,7 +65,7 @@ bool FixWinding(SkPath* path) {
|
||||
topContour->setReverse();
|
||||
writePath = true;
|
||||
}
|
||||
topContour->markDone();
|
||||
topContour->markAllDone();
|
||||
globalState.clearNested();
|
||||
}
|
||||
if (!writePath) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,120 +7,300 @@
|
||||
#ifndef SkOpCoincidence_DEFINED
|
||||
#define SkOpCoincidence_DEFINED
|
||||
|
||||
#include "SkTDArray.h"
|
||||
#include "SkOpTAllocator.h"
|
||||
#include "SkOpSpan.h"
|
||||
#include "SkPathOpsTypes.h"
|
||||
|
||||
class SkOpPtT;
|
||||
class SkOpSpanBase;
|
||||
|
||||
struct SkCoincidentSpans {
|
||||
SkCoincidentSpans* fNext;
|
||||
SkOpPtT* fCoinPtTStart;
|
||||
SkOpPtT* fCoinPtTEnd;
|
||||
SkOpPtT* fOppPtTStart;
|
||||
SkOpPtT* fOppPtTEnd;
|
||||
bool fFlipped;
|
||||
SkDEBUGCODE(int fID);
|
||||
class SkCoincidentSpans {
|
||||
public:
|
||||
const SkOpPtT* coinPtTEnd() const { return fCoinPtTEnd; }
|
||||
const SkOpPtT* coinPtTStart() const { return fCoinPtTStart; }
|
||||
|
||||
// These return non-const pointers so that, as copies, they can be added
|
||||
// to a new span pair
|
||||
SkOpPtT* coinPtTEndWritable() const { return const_cast<SkOpPtT*>(fCoinPtTEnd); }
|
||||
SkOpPtT* coinPtTStartWritable() const { return const_cast<SkOpPtT*>(fCoinPtTStart); }
|
||||
|
||||
bool collapsed(const SkOpPtT* ) const;
|
||||
bool contains(const SkOpPtT* s, const SkOpPtT* e) const;
|
||||
void correctEnds();
|
||||
void correctOneEnd(const SkOpPtT* (SkCoincidentSpans::* getEnd)() const,
|
||||
void (SkCoincidentSpans::* setEnd)(const SkOpPtT* ptT) );
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* log) const;
|
||||
#endif
|
||||
|
||||
int debugID() const {
|
||||
return SkDEBUGRELEASE(fID, -1);
|
||||
}
|
||||
|
||||
void debugShow() const;
|
||||
#ifdef SK_DEBUG
|
||||
void debugStartCheck(const SkOpSpanBase* outer, const SkOpSpanBase* over,
|
||||
const SkOpGlobalState* debugState) const;
|
||||
#endif
|
||||
void dump() const;
|
||||
bool expand();
|
||||
bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
|
||||
bool flipped() const { return fOppPtTStart->fT > fOppPtTEnd->fT; }
|
||||
void init() { sk_bzero(this, sizeof(*this)); }
|
||||
const SkOpPtT* oppPtTStart() const { return fOppPtTStart; }
|
||||
const SkOpPtT* oppPtTEnd() const { return fOppPtTEnd; }
|
||||
// These return non-const pointers so that, as copies, they can be added
|
||||
// to a new span pair
|
||||
SkOpPtT* oppPtTStartWritable() const { return const_cast<SkOpPtT*>(fOppPtTStart); }
|
||||
SkOpPtT* oppPtTEndWritable() const { return const_cast<SkOpPtT*>(fOppPtTEnd); }
|
||||
SkCoincidentSpans* next() { return fNext; }
|
||||
const SkCoincidentSpans* next() const { return fNext; }
|
||||
SkCoincidentSpans** nextPtr() { return &fNext; }
|
||||
int spanCount() const;
|
||||
|
||||
void set(SkCoincidentSpans* next, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd
|
||||
SkDEBUGPARAMS(int id));
|
||||
|
||||
void setCoinPtTEnd(const SkOpPtT* ptT) {
|
||||
SkASSERT(ptT == ptT->span()->ptT())
|
||||
SkASSERT(!fCoinPtTStart || ptT->fT != fCoinPtTStart->fT);
|
||||
SkASSERT(!fCoinPtTStart || fCoinPtTStart->segment() == ptT->segment());
|
||||
fCoinPtTEnd = ptT;
|
||||
ptT->setCoincident();
|
||||
}
|
||||
|
||||
void setCoinPtTStart(const SkOpPtT* ptT) {
|
||||
SkASSERT(ptT == ptT->span()->ptT())
|
||||
SkASSERT(!fCoinPtTEnd || ptT->fT != fCoinPtTEnd->fT);
|
||||
SkASSERT(!fCoinPtTEnd || fCoinPtTEnd->segment() == ptT->segment());
|
||||
fCoinPtTStart = ptT;
|
||||
ptT->setCoincident();
|
||||
}
|
||||
|
||||
void setEnds(const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTEnd) {
|
||||
this->setCoinPtTEnd(coinPtTEnd);
|
||||
this->setOppPtTEnd(oppPtTEnd);
|
||||
}
|
||||
|
||||
void setOppPtTEnd(const SkOpPtT* ptT) {
|
||||
SkASSERT(ptT == ptT->span()->ptT())
|
||||
SkASSERT(!fOppPtTStart || ptT->fT != fOppPtTStart->fT);
|
||||
SkASSERT(!fOppPtTStart || fOppPtTStart->segment() == ptT->segment());
|
||||
fOppPtTEnd = ptT;
|
||||
ptT->setCoincident();
|
||||
}
|
||||
|
||||
void setOppPtTStart(const SkOpPtT* ptT) {
|
||||
SkASSERT(ptT == ptT->span()->ptT())
|
||||
SkASSERT(!fOppPtTEnd || ptT->fT != fOppPtTEnd->fT);
|
||||
SkASSERT(!fOppPtTEnd || fOppPtTEnd->segment() == ptT->segment());
|
||||
fOppPtTStart = ptT;
|
||||
ptT->setCoincident();
|
||||
}
|
||||
|
||||
void setStarts(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
|
||||
this->setCoinPtTStart(coinPtTStart);
|
||||
this->setOppPtTStart(oppPtTStart);
|
||||
}
|
||||
|
||||
void setNext(SkCoincidentSpans* next) { fNext = next; }
|
||||
|
||||
bool startEquals(const SkOpSpanBase* outer, const SkOpSpanBase* over) const {
|
||||
return fCoinPtTStart->span() == over && fOppPtTStart->span() == outer;
|
||||
}
|
||||
private:
|
||||
SkCoincidentSpans* fNext;
|
||||
const SkOpPtT* fCoinPtTStart;
|
||||
const SkOpPtT* fCoinPtTEnd;
|
||||
const SkOpPtT* fOppPtTStart;
|
||||
const SkOpPtT* fOppPtTEnd;
|
||||
SkDEBUGCODE(int fID);
|
||||
};
|
||||
|
||||
class SkOpCoincidence {
|
||||
public:
|
||||
SkOpCoincidence()
|
||||
SkOpCoincidence(SkOpGlobalState* globalState)
|
||||
: fHead(nullptr)
|
||||
, fTop(nullptr)
|
||||
SkDEBUGPARAMS(fDebugState(nullptr))
|
||||
{
|
||||
, fGlobalState(globalState)
|
||||
, fContinue(false)
|
||||
, fSpanDeleted(false)
|
||||
, fPtAllocated(false)
|
||||
, fCoinExtended(false)
|
||||
, fSpanMerged(false) {
|
||||
globalState->setCoincidence(this);
|
||||
}
|
||||
|
||||
void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
|
||||
bool addExpanded(SkChunkAlloc* allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(SkOpGlobalState* ));
|
||||
bool addMissing(SkChunkAlloc* allocator);
|
||||
SkOpPtT* oppPtTEnd);
|
||||
bool addEndMovedSpans();
|
||||
bool addExpanded();
|
||||
bool addMissing();
|
||||
bool addUncommon();
|
||||
bool apply();
|
||||
bool contains(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, bool flipped) const;
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
|
||||
void correctEnds();
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugAddExpanded(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugAddMissing(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugAddOrOverlap(const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
|
||||
double coinTs, double coinTe, double oppTs, double oppTe,
|
||||
const char* id, SkPathOpsDebug::GlitchLog* log) const;
|
||||
#endif
|
||||
|
||||
const SkOpAngle* debugAngle(int id) const {
|
||||
return SkDEBUGRELEASE(fDebugState->debugAngle(id), nullptr);
|
||||
return SkDEBUGRELEASE(fGlobalState->debugAngle(id), nullptr);
|
||||
}
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugCheckOverlap(const char* id, SkPathOpsDebug::GlitchLog* log) const;
|
||||
void debugCheckValid(const char* id, SkPathOpsDebug::GlitchLog* log) const;
|
||||
#endif
|
||||
|
||||
SkOpContour* debugContour(int id) {
|
||||
return SkDEBUGRELEASE(fDebugState->debugContour(id), nullptr);
|
||||
return SkDEBUGRELEASE(fGlobalState->debugContour(id), nullptr);
|
||||
}
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
bool debugExpand(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugMark(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ,
|
||||
const SkCoincidentSpans* coin, const SkOpPtT* test) const;
|
||||
void debugMarkCollapsed(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpPtT* test) const;
|
||||
#endif
|
||||
|
||||
const SkOpPtT* debugPtT(int id) const {
|
||||
return SkDEBUGRELEASE(fDebugState->debugPtT(id), nullptr);
|
||||
return SkDEBUGRELEASE(fGlobalState->debugPtT(id), nullptr);
|
||||
}
|
||||
|
||||
const SkOpSegment* debugSegment(int id) const {
|
||||
return SkDEBUGRELEASE(fDebugState->debugSegment(id), nullptr);
|
||||
return SkDEBUGRELEASE(fGlobalState->debugSegment(id), nullptr);
|
||||
}
|
||||
|
||||
void debugSetGlobalState(SkOpGlobalState* debugState) {
|
||||
SkDEBUGCODE(fDebugState = debugState);
|
||||
}
|
||||
|
||||
void debugFixAligned(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugRemoveCollapsed(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugReorder(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugRelease(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSegment* ) const;
|
||||
#endif
|
||||
void debugShowCoincidence() const;
|
||||
|
||||
const SkOpSpanBase* debugSpan(int id) const {
|
||||
return SkDEBUGRELEASE(fDebugState->debugSpan(id), nullptr);
|
||||
return SkDEBUGRELEASE(fGlobalState->debugSpan(id), nullptr);
|
||||
}
|
||||
|
||||
void release(SkCoincidentSpans* );
|
||||
void debugValidate() const;
|
||||
void dump() const;
|
||||
bool edge(const SkOpPtT* , bool* start) const;
|
||||
bool expand();
|
||||
bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
|
||||
SkOpPtT* oppPtTEnd);
|
||||
bool findOverlaps(SkOpCoincidence* , SkChunkAlloc* allocator) const;
|
||||
bool fixAligned();
|
||||
void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
|
||||
bool extend(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
|
||||
const SkOpPtT* oppPtTEnd);
|
||||
bool findOverlaps(SkOpCoincidence* ) const;
|
||||
void fixUp(SkOpPtT* deleted, const SkOpPtT* kept);
|
||||
|
||||
SkOpGlobalState* globalState() {
|
||||
return fGlobalState;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return !fHead;
|
||||
return !fHead && !fTop;
|
||||
}
|
||||
|
||||
bool mark();
|
||||
void markCollapsed(SkOpPtT* );
|
||||
|
||||
static bool Ordered(const SkOpPtT* coinPtTStart, const SkOpPtT* oppPtTStart) {
|
||||
return Ordered(coinPtTStart->segment(), oppPtTStart->segment());
|
||||
}
|
||||
|
||||
static bool Ordered(const SkOpSegment* coin, const SkOpSegment* opp);
|
||||
void release(const SkOpSegment* );
|
||||
bool removeCollapsed();
|
||||
bool reorder();
|
||||
|
||||
private:
|
||||
bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e,
|
||||
SkChunkAlloc* );
|
||||
void add(const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, const SkOpPtT* oppPtTStart,
|
||||
const SkOpPtT* oppPtTEnd) {
|
||||
this->add(const_cast<SkOpPtT*>(coinPtTStart), const_cast<SkOpPtT*>(coinPtTEnd),
|
||||
const_cast<SkOpPtT*>(oppPtTStart), const_cast<SkOpPtT*>(oppPtTEnd));
|
||||
}
|
||||
|
||||
void addEndMovedSpans(const SkOpSpan* base, const SkOpSpanBase* testSpan);
|
||||
bool addEndMovedSpans(const SkOpPtT* ptT);
|
||||
|
||||
bool addIfMissing(const SkCoincidentSpans* outer, SkOpPtT* over1s, SkOpPtT* over1e);
|
||||
|
||||
bool addIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
|
||||
const SkOpPtT* over1e) {
|
||||
return addIfMissing(outer, const_cast<SkOpPtT*>(over1s), const_cast<SkOpPtT*>(over1e));
|
||||
}
|
||||
|
||||
bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
|
||||
const SkOpPtT* over2s, const SkOpPtT* over2e,
|
||||
double tStart, double tEnd,
|
||||
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
|
||||
SkChunkAlloc* );
|
||||
bool addOverlap(SkOpSegment* seg1, SkOpSegment* seg1o, SkOpSegment* seg2, SkOpSegment* seg2o,
|
||||
SkOpPtT* overS, SkOpPtT* overE, SkChunkAlloc* );
|
||||
bool debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
|
||||
const SkOpPtT* over1e) const;
|
||||
bool debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
|
||||
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd);
|
||||
|
||||
bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
|
||||
const SkOpPtT* over2s, const SkOpPtT* over2e,
|
||||
double tStart, double tEnd,
|
||||
const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) {
|
||||
return addIfMissing(over1s, over1e, over2s, over2e, tStart, tEnd,
|
||||
const_cast<SkOpPtT*>(coinPtTStart), coinPtTEnd,
|
||||
const_cast<SkOpPtT*>(oppPtTStart), oppPtTEnd);
|
||||
}
|
||||
|
||||
bool addOrOverlap(SkOpSegment* coinSeg, SkOpSegment* oppSeg,
|
||||
double coinTs, double coinTe, double oppTs, double oppTe);
|
||||
bool addOverlap(const SkOpSegment* seg1, const SkOpSegment* seg1o,
|
||||
const SkOpSegment* seg2, const SkOpSegment* seg2o,
|
||||
const SkOpPtT* overS, const SkOpPtT* overE);
|
||||
bool alreadyAdded(const SkCoincidentSpans* check, const SkCoincidentSpans* outer,
|
||||
const SkOpPtT* over1s, const SkOpPtT* over1e) const;
|
||||
bool checkOverlap(SkCoincidentSpans* check,
|
||||
const SkOpSegment* coinSeg, const SkOpSegment* oppSeg,
|
||||
double coinTs, double coinTe, double oppTs, double oppTe,
|
||||
SkTDArray<SkCoincidentSpans*>* overlaps) const;
|
||||
bool contains(const SkOpSegment* seg, const SkOpSegment* opp, double oppT) const;
|
||||
bool contains(const SkCoincidentSpans* coin, const SkOpSegment* seg,
|
||||
const SkOpSegment* opp, double oppT) const;
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugAddIfMissing(const SkCoincidentSpans* outer, const SkOpPtT* over1s,
|
||||
const SkOpPtT* over1e, const char* id, SkPathOpsDebug::GlitchLog*) const;
|
||||
void debugAddIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
|
||||
const SkOpPtT* over2s, const SkOpPtT* over2e,
|
||||
double tStart, double tEnd,
|
||||
SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd) const;
|
||||
const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
const SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
|
||||
const char* id, SkPathOpsDebug::GlitchLog*) const;
|
||||
#endif
|
||||
void fixUp(SkCoincidentSpans* coin, SkOpPtT* deleted, const SkOpPtT* kept);
|
||||
void markCollapsed(SkCoincidentSpans* head, SkOpPtT* test);
|
||||
bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
|
||||
const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
|
||||
double* overS, double* overE) const;
|
||||
|
||||
bool release(SkCoincidentSpans* coin, SkCoincidentSpans* );
|
||||
void restoreHead();
|
||||
bool testForCoincidence(const SkCoincidentSpans* outer, const SkOpPtT* testS,
|
||||
const SkOpPtT* testE) const;
|
||||
static void TRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart,
|
||||
double tEnd, const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
|
||||
double* coinTs, double* coinTe);
|
||||
|
||||
SkCoincidentSpans* fHead;
|
||||
SkCoincidentSpans* fTop;
|
||||
SkDEBUGCODE_(SkOpGlobalState* fDebugState);
|
||||
SkOpGlobalState* fGlobalState;
|
||||
bool fContinue;
|
||||
bool fSpanDeleted;
|
||||
bool fPtAllocated;
|
||||
bool fCoinExtended;
|
||||
bool fSpanMerged;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -10,18 +10,18 @@
|
||||
#include "SkReduceOrder.h"
|
||||
#include "SkTSort.h"
|
||||
|
||||
SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
|
||||
SkChunkAlloc* allocator) {
|
||||
SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
|
||||
SkChunkAlloc* allocator = this->globalState()->allocator();
|
||||
switch (verb) {
|
||||
case SkPath::kLine_Verb: {
|
||||
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
|
||||
memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
|
||||
return appendSegment(allocator).addLine(ptStorage, this);
|
||||
return appendSegment().addLine(ptStorage, this);
|
||||
} break;
|
||||
case SkPath::kQuad_Verb: {
|
||||
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
|
||||
memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
|
||||
return appendSegment(allocator).addQuad(ptStorage, this);
|
||||
return appendSegment().addQuad(ptStorage, this);
|
||||
} break;
|
||||
case SkPath::kConic_Verb: {
|
||||
SkASSERT(0); // the original curve is a cubic, which will never reduce to a conic
|
||||
@ -29,7 +29,7 @@ SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
|
||||
case SkPath::kCubic_Verb: {
|
||||
SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
|
||||
memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
|
||||
return appendSegment(allocator).addCubic(ptStorage, this);
|
||||
return appendSegment().addCubic(ptStorage, this);
|
||||
} break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTSort.h"
|
||||
|
||||
class SkChunkAlloc;
|
||||
enum class SkOpRayDir;
|
||||
struct SkOpRayHit;
|
||||
class SkPathWriter;
|
||||
@ -30,47 +29,31 @@ public:
|
||||
|
||||
bool operator<(const SkOpContour& rh) const {
|
||||
return fBounds.fTop == rh.fBounds.fTop
|
||||
? fBounds.fLeft < rh.fBounds.fLeft
|
||||
: fBounds.fTop < rh.fBounds.fTop;
|
||||
? fBounds.fLeft < rh.fBounds.fLeft
|
||||
: fBounds.fTop < rh.fBounds.fTop;
|
||||
}
|
||||
|
||||
void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
|
||||
SkASSERT(fCount > 0);
|
||||
SkOpSegment* segment = &fHead;
|
||||
do {
|
||||
segment->addAlignIntersections(contourList, allocator);
|
||||
} while ((segment = segment->next()));
|
||||
void addConic(SkPoint pts[3], SkScalar weight) {
|
||||
appendSegment().addConic(pts, weight, this);
|
||||
}
|
||||
|
||||
void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) {
|
||||
appendSegment(allocator).addConic(pts, weight, this);
|
||||
void addCubic(SkPoint pts[4]) {
|
||||
appendSegment().addCubic(pts, this);
|
||||
}
|
||||
|
||||
void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
|
||||
appendSegment(allocator).addCubic(pts, this);
|
||||
SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4]);
|
||||
|
||||
SkOpSegment* addLine(SkPoint pts[2]) {
|
||||
return appendSegment().addLine(pts, this);
|
||||
}
|
||||
|
||||
SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
|
||||
|
||||
void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
|
||||
appendSegment(allocator).addLine(pts, this);
|
||||
void addQuad(SkPoint pts[3]) {
|
||||
appendSegment().addQuad(pts, this);
|
||||
}
|
||||
|
||||
void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
|
||||
appendSegment(allocator).addQuad(pts, this);
|
||||
}
|
||||
|
||||
void align() {
|
||||
SkASSERT(fCount > 0);
|
||||
SkOpSegment* segment = &fHead;
|
||||
do {
|
||||
segment->align();
|
||||
} while ((segment = segment->next()));
|
||||
}
|
||||
|
||||
SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
|
||||
SkOpSegment& appendSegment() {
|
||||
SkOpSegment* result = fCount++
|
||||
? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
|
||||
? SkOpTAllocator<SkOpSegment>::Allocate(this->globalState()->allocator()) : &fHead;
|
||||
result->setPrev(fTail);
|
||||
if (fTail) {
|
||||
fTail->setNext(result);
|
||||
@ -79,8 +62,8 @@ public:
|
||||
return *result;
|
||||
}
|
||||
|
||||
SkOpContour* appendContour(SkChunkAlloc* allocator) {
|
||||
SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
|
||||
SkOpContour* appendContour() {
|
||||
SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
|
||||
contour->setNext(nullptr);
|
||||
SkOpContour* prev = this;
|
||||
SkOpContour* next;
|
||||
@ -95,11 +78,11 @@ public:
|
||||
return fBounds;
|
||||
}
|
||||
|
||||
void calcAngles(SkChunkAlloc* allocator) {
|
||||
void calcAngles() {
|
||||
SkASSERT(fCount > 0);
|
||||
SkOpSegment* segment = &fHead;
|
||||
do {
|
||||
segment->calcAngles(allocator);
|
||||
segment->calcAngles();
|
||||
} while ((segment = segment->next()));
|
||||
}
|
||||
|
||||
@ -132,14 +115,21 @@ public:
|
||||
return SkDEBUGRELEASE(this->globalState()->debugAngle(id), nullptr);
|
||||
}
|
||||
|
||||
const SkOpCoincidence* debugCoincidence() const {
|
||||
return this->globalState()->coincidence();
|
||||
}
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
#endif
|
||||
|
||||
SkOpContour* debugContour(int id) {
|
||||
return SkDEBUGRELEASE(this->globalState()->debugContour(id), nullptr);
|
||||
}
|
||||
|
||||
void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log,
|
||||
const SkOpCoincidence* coincidence) const;
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* log) const;
|
||||
#endif
|
||||
|
||||
const SkOpPtT* debugPtT(int id) const {
|
||||
return SkDEBUGRELEASE(this->globalState()->debugPtT(id), nullptr);
|
||||
@ -197,15 +187,6 @@ public:
|
||||
return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
|
||||
}
|
||||
|
||||
bool findCollapsed() {
|
||||
SkASSERT(fCount > 0);
|
||||
SkOpSegment* segment = &fHead;
|
||||
do {
|
||||
segment->findCollapsed();
|
||||
} while ((segment = segment->next()));
|
||||
return true;
|
||||
}
|
||||
|
||||
SkOpSpan* findSortableTop(SkOpContour* );
|
||||
|
||||
SkOpSegment* first() {
|
||||
@ -237,14 +218,15 @@ public:
|
||||
return fXor;
|
||||
}
|
||||
|
||||
void markDone() {
|
||||
void markAllDone() {
|
||||
SkOpSegment* segment = &fHead;
|
||||
do {
|
||||
segment->markAllDone();
|
||||
} while ((segment = segment->next()));
|
||||
}
|
||||
|
||||
bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
|
||||
// Please keep this aligned with debugMissingCoincidence()
|
||||
bool missingCoincidence() {
|
||||
SkASSERT(fCount > 0);
|
||||
SkOpSegment* segment = &fHead;
|
||||
bool result = false;
|
||||
@ -253,7 +235,7 @@ public:
|
||||
#if DEBUG_ANGLE
|
||||
segment->debugCheckAngleCoin();
|
||||
#endif
|
||||
} else if (segment->missingCoincidence(coincidences, allocator)) {
|
||||
} else if (segment->missingCoincidence()) {
|
||||
result = true;
|
||||
// FIXME: trying again loops forever in issue3651_6
|
||||
// The continue below is speculative -- once there's an actual case that requires it,
|
||||
|
@ -36,9 +36,9 @@ int SkOpEdgeBuilder::count() const {
|
||||
return count;
|
||||
}
|
||||
|
||||
bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
|
||||
bool SkOpEdgeBuilder::finish() {
|
||||
fOperand = false;
|
||||
if (fUnparseable || !walk(allocator)) {
|
||||
if (fUnparseable || !walk()) {
|
||||
return false;
|
||||
}
|
||||
complete();
|
||||
@ -162,7 +162,7 @@ bool SkOpEdgeBuilder::close() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
|
||||
bool SkOpEdgeBuilder::walk() {
|
||||
uint8_t* verbPtr = fPathVerbs.begin();
|
||||
uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
|
||||
SkPoint* pointsPtr = fPathPts.begin() - 1;
|
||||
@ -183,20 +183,20 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
|
||||
}
|
||||
}
|
||||
if (!fCurrentContour) {
|
||||
fCurrentContour = fContoursHead->appendContour(allocator);
|
||||
fCurrentContour = fContoursHead->appendContour();
|
||||
}
|
||||
fCurrentContour->init(fGlobalState, fOperand,
|
||||
fXorMask[fOperand] == kEvenOdd_PathOpsMask);
|
||||
pointsPtr += 1;
|
||||
continue;
|
||||
case SkPath::kLine_Verb:
|
||||
fCurrentContour->addLine(pointsPtr, fAllocator);
|
||||
fCurrentContour->addLine(pointsPtr);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
fCurrentContour->addQuad(pointsPtr, fAllocator);
|
||||
fCurrentContour->addQuad(pointsPtr);
|
||||
break;
|
||||
case SkPath::kConic_Verb:
|
||||
fCurrentContour->addConic(pointsPtr, *weightPtr++, fAllocator);
|
||||
fCurrentContour->addConic(pointsPtr, *weightPtr++);
|
||||
break;
|
||||
case SkPath::kCubic_Verb: {
|
||||
// Split complex cubics (such as self-intersecting curves or
|
||||
@ -221,13 +221,13 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
|
||||
for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) {
|
||||
force_small_to_zero(&curve2[index]);
|
||||
}
|
||||
fCurrentContour->addCurve(v1, curve1, fAllocator);
|
||||
fCurrentContour->addCurve(v2, curve2, fAllocator);
|
||||
fCurrentContour->addCurve(v1, curve1);
|
||||
fCurrentContour->addCurve(v2, curve2);
|
||||
} else {
|
||||
fCurrentContour->addCubic(pointsPtr, fAllocator);
|
||||
fCurrentContour->addCubic(pointsPtr);
|
||||
}
|
||||
} else {
|
||||
fCurrentContour->addCubic(pointsPtr, fAllocator);
|
||||
fCurrentContour->addCubic(pointsPtr);
|
||||
}
|
||||
} break;
|
||||
case SkPath::kClose_Verb:
|
||||
|
@ -12,20 +12,16 @@
|
||||
|
||||
class SkOpEdgeBuilder {
|
||||
public:
|
||||
SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
|
||||
SkOpGlobalState* globalState)
|
||||
: fAllocator(allocator) // FIXME: replace with const, tune this
|
||||
, fGlobalState(globalState)
|
||||
SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkOpGlobalState* globalState)
|
||||
: fGlobalState(globalState)
|
||||
, fPath(path.nativePath())
|
||||
, fContoursHead(contours2)
|
||||
, fAllowOpenContours(true) {
|
||||
init();
|
||||
}
|
||||
|
||||
SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
|
||||
SkOpGlobalState* globalState)
|
||||
: fAllocator(allocator)
|
||||
, fGlobalState(globalState)
|
||||
SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
|
||||
: fGlobalState(globalState)
|
||||
, fPath(&path)
|
||||
, fContoursHead(contours2)
|
||||
, fAllowOpenContours(false) {
|
||||
@ -42,7 +38,7 @@ public:
|
||||
}
|
||||
|
||||
int count() const;
|
||||
bool finish(SkChunkAlloc* );
|
||||
bool finish();
|
||||
|
||||
const SkOpContour* head() const {
|
||||
return fContoursHead;
|
||||
@ -56,9 +52,8 @@ private:
|
||||
void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
|
||||
bool close();
|
||||
int preFetch();
|
||||
bool walk(SkChunkAlloc* );
|
||||
bool walk();
|
||||
|
||||
SkChunkAlloc* fAllocator;
|
||||
SkOpGlobalState* fGlobalState;
|
||||
const SkPath* fPath;
|
||||
SkTDArray<SkPoint> fPathPts;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "SkOpSegment.h"
|
||||
#include "SkPathWriter.h"
|
||||
|
||||
#define FAIL_IF(cond) do { if (cond) return false; } while (false)
|
||||
|
||||
/*
|
||||
After computing raw intersections, post process all segments to:
|
||||
- find small collections of points that can be collapsed to a single point
|
||||
@ -159,90 +161,10 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
|
||||
return result;
|
||||
}
|
||||
|
||||
void SkOpSegment::addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
|
||||
SkOpContourHead* contourList, SkChunkAlloc* allocator) {
|
||||
const SkPoint& newPt = endPtT.fPt;
|
||||
if (newPt == oldPt) {
|
||||
return;
|
||||
}
|
||||
SkPoint line[2] = { newPt, oldPt };
|
||||
SkPathOpsBounds lineBounds;
|
||||
lineBounds.setBounds(line, 2);
|
||||
SkDLine aLine;
|
||||
aLine.set(line);
|
||||
SkOpContour* current = contourList;
|
||||
do {
|
||||
if (!SkPathOpsBounds::Intersects(current->bounds(), lineBounds)) {
|
||||
continue;
|
||||
}
|
||||
SkOpSegment* segment = current->first();
|
||||
do {
|
||||
if (!SkPathOpsBounds::Intersects(segment->bounds(), lineBounds)) {
|
||||
continue;
|
||||
}
|
||||
if (newPt == segment->fPts[0]) {
|
||||
continue;
|
||||
}
|
||||
if (newPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
|
||||
continue;
|
||||
}
|
||||
if (oldPt == segment->fPts[0]) {
|
||||
continue;
|
||||
}
|
||||
if (oldPt == segment->fPts[SkPathOpsVerbToPoints(segment->fVerb)]) {
|
||||
continue;
|
||||
}
|
||||
if (endPtT.contains(segment)) {
|
||||
continue;
|
||||
}
|
||||
SkIntersections i;
|
||||
switch (segment->fVerb) {
|
||||
case SkPath::kLine_Verb: {
|
||||
SkDLine bLine;
|
||||
bLine.set(segment->fPts);
|
||||
i.intersect(bLine, aLine);
|
||||
} break;
|
||||
case SkPath::kQuad_Verb: {
|
||||
SkDQuad bQuad;
|
||||
bQuad.set(segment->fPts);
|
||||
i.intersect(bQuad, aLine);
|
||||
} break;
|
||||
case SkPath::kConic_Verb: {
|
||||
SkDConic bConic;
|
||||
bConic.set(segment->fPts, segment->fWeight);
|
||||
i.intersect(bConic, aLine);
|
||||
} break;
|
||||
case SkPath::kCubic_Verb: {
|
||||
SkDCubic bCubic;
|
||||
bCubic.set(segment->fPts);
|
||||
i.intersect(bCubic, aLine);
|
||||
} break;
|
||||
default:
|
||||
SkASSERT(0);
|
||||
}
|
||||
if (i.used()) {
|
||||
SkASSERT(i.used() == 1);
|
||||
SkASSERT(!zero_or_one(i[0][0]));
|
||||
SkOpSpanBase* checkSpan = fHead.next();
|
||||
while (!checkSpan->final()) {
|
||||
if (checkSpan->contains(segment)) {
|
||||
goto nextSegment;
|
||||
}
|
||||
checkSpan = checkSpan->upCast()->next();
|
||||
}
|
||||
SkOpPtT* ptT = segment->addT(i[0][0], SkOpSegment::kAllowAlias, allocator);
|
||||
ptT->fPt = newPt;
|
||||
endPtT.addOpp(ptT);
|
||||
}
|
||||
nextSegment:
|
||||
;
|
||||
} while ((segment = segment->next()));
|
||||
} while ((current = current->next()));
|
||||
}
|
||||
|
||||
bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
|
||||
SkPathWriter* path) const {
|
||||
if (start->starter(end)->alreadyAdded()) {
|
||||
SkDEBUGF(("same curve added twice aborted pathops\n"));
|
||||
return false;
|
||||
}
|
||||
SkOpCurve edge;
|
||||
@ -298,29 +220,57 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
|
||||
return true;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
|
||||
SkOpSpanBase* existing = nullptr;
|
||||
SkOpSpanBase* test = &fHead;
|
||||
double testT;
|
||||
const SkOpPtT* SkOpSegment::existing(double t, const SkOpSegment* opp) const {
|
||||
const SkOpSpanBase* test = &fHead;
|
||||
const SkOpPtT* testPtT;
|
||||
SkPoint pt = this->ptAtT(t);
|
||||
do {
|
||||
if ((testT = test->ptT()->fT) >= t) {
|
||||
if (testT == t) {
|
||||
existing = test;
|
||||
}
|
||||
testPtT = test->ptT();
|
||||
if (testPtT->fT == t) {
|
||||
break;
|
||||
}
|
||||
if (!this->match(testPtT, this, t, pt, opp ? kAllowAliasMatch : kNoAliasMatch)) {
|
||||
if (t < testPtT->fT) {
|
||||
return nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!opp) {
|
||||
return testPtT;
|
||||
}
|
||||
const SkOpPtT* loop = testPtT->next();
|
||||
while (loop != testPtT) {
|
||||
if (loop->segment() == this && loop->fT == t && loop->fPt == pt) {
|
||||
goto foundMatch;
|
||||
}
|
||||
loop = loop->next();
|
||||
}
|
||||
return nullptr;
|
||||
} while ((test = test->upCast()->next()));
|
||||
SkOpPtT* result;
|
||||
if (existing && existing->contains(opp)) {
|
||||
result = existing->ptT();
|
||||
} else {
|
||||
result = this->addT(t, SkOpSegment::kNoAlias, allocator);
|
||||
}
|
||||
SkASSERT(result);
|
||||
return result;
|
||||
foundMatch:
|
||||
return opp && !test->contains(opp) ? nullptr : testPtT;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
|
||||
// break the span so that the coincident part does not change the angle of the remainder
|
||||
bool SkOpSegment::addExpanded(double newT, const SkOpSpanBase* test, bool* startOver) {
|
||||
if (this->contains(newT)) {
|
||||
return true;
|
||||
}
|
||||
SkOpPtT* newPtT = this->addT(newT, kAllowAliasMatch, startOver);
|
||||
if (!newPtT) {
|
||||
return false;
|
||||
}
|
||||
newPtT->fPt = this->ptAtT(newT);
|
||||
// const cast away to change linked list; pt/t values stays unchanged
|
||||
SkOpSpanBase* writableTest = const_cast<SkOpSpanBase*>(test);
|
||||
if (writableTest->ptT()->addOpp(newPtT)) {
|
||||
writableTest->checkForCollapsedCoincidence();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugAddT()
|
||||
SkOpPtT* SkOpSegment::addT(double t, AliasMatch allowAlias, bool* allocated) {
|
||||
debugValidate();
|
||||
SkPoint pt = this->ptAtT(t);
|
||||
SkOpSpanBase* span = &fHead;
|
||||
@ -331,9 +281,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
if (t == result->fT) {
|
||||
goto bumpSpan;
|
||||
}
|
||||
if (this->match(result, this, t, pt)) {
|
||||
if (this->match(result, this, t, pt, allowAlias)) {
|
||||
// see if any existing alias matches segment, pt, and t
|
||||
loop = result->next();
|
||||
loop = result->next();
|
||||
duplicatePt = false;
|
||||
while (loop != result) {
|
||||
bool ptMatch = loop->fPt == pt;
|
||||
@ -343,12 +293,12 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
duplicatePt |= ptMatch;
|
||||
loop = loop->next();
|
||||
}
|
||||
if (kNoAlias == allowAlias) {
|
||||
if (kNoAliasMatch == allowAlias) {
|
||||
bumpSpan:
|
||||
span->bumpSpanAdds();
|
||||
return result;
|
||||
}
|
||||
SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
|
||||
SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(this->globalState()->allocator());
|
||||
alias->init(result->span(), t, pt, duplicatePt);
|
||||
result->insert(alias);
|
||||
result->span()->unaligned();
|
||||
@ -358,6 +308,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
alias->segment()->debugID(), alias->span()->debugID());
|
||||
#endif
|
||||
span->bumpSpanAdds();
|
||||
if (allocated) {
|
||||
*allocated = true;
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
if (t < result->fT) {
|
||||
@ -365,7 +318,7 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
if (!prev) {
|
||||
return nullptr;
|
||||
}
|
||||
SkOpSpan* span = insert(prev, allocator);
|
||||
SkOpSpan* span = insert(prev);
|
||||
span->init(this, prev, t, pt);
|
||||
this->debugValidate();
|
||||
#if DEBUG_ADD_T
|
||||
@ -373,6 +326,9 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
span->segment()->debugID(), span->debugID());
|
||||
#endif
|
||||
span->bumpSpanAdds();
|
||||
if (allocated) {
|
||||
*allocated = true;
|
||||
}
|
||||
return span->ptT();
|
||||
}
|
||||
SkASSERT(span != &fTail);
|
||||
@ -381,43 +337,17 @@ SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* alloca
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// choose a solitary t and pt value; remove aliases; align the opposite ends
|
||||
void SkOpSegment::align() {
|
||||
debugValidate();
|
||||
SkOpSpanBase* span = &fHead;
|
||||
if (!span->aligned()) {
|
||||
span->alignEnd(0, fPts[0]);
|
||||
}
|
||||
while ((span = span->upCast()->next())) {
|
||||
if (span == &fTail) {
|
||||
break;
|
||||
}
|
||||
span->align();
|
||||
}
|
||||
if (!span->aligned()) {
|
||||
span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
|
||||
}
|
||||
if (this->collapsed()) {
|
||||
SkOpSpan* span = &fHead;
|
||||
do {
|
||||
span->setWindValue(0);
|
||||
span->setOppValue(0);
|
||||
this->markDone(span);
|
||||
} while ((span = span->next()->upCastable()));
|
||||
}
|
||||
debugValidate();
|
||||
}
|
||||
|
||||
void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
|
||||
void SkOpSegment::calcAngles() {
|
||||
bool activePrior = !fHead.isCanceled();
|
||||
if (activePrior && !fHead.simple()) {
|
||||
addStartSpan(allocator);
|
||||
addStartSpan();
|
||||
}
|
||||
SkOpSpan* prior = &fHead;
|
||||
SkOpSpanBase* spanBase = fHead.next();
|
||||
while (spanBase != &fTail) {
|
||||
if (activePrior) {
|
||||
SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
|
||||
SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(
|
||||
this->globalState()->allocator());
|
||||
priorAngle->set(spanBase, prior);
|
||||
spanBase->setFromAngle(priorAngle);
|
||||
}
|
||||
@ -425,7 +355,8 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
|
||||
bool active = !span->isCanceled();
|
||||
SkOpSpanBase* next = span->next();
|
||||
if (active) {
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(
|
||||
this->globalState()->allocator());
|
||||
angle->set(span, next);
|
||||
span->setToAngle(angle);
|
||||
}
|
||||
@ -434,12 +365,32 @@ void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
|
||||
spanBase = next;
|
||||
}
|
||||
if (activePrior && !fTail.simple()) {
|
||||
addEndSpan(allocator);
|
||||
addEndSpan();
|
||||
}
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugClearAll()
|
||||
void SkOpSegment::clearAll() {
|
||||
SkOpSpan* span = &fHead;
|
||||
do {
|
||||
this->clearOne(span);
|
||||
} while ((span = span->next()->upCastable()));
|
||||
this->globalState()->coincidence()->release(this);
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugClearOne()
|
||||
void SkOpSegment::clearOne(SkOpSpan* span) {
|
||||
span->setWindValue(0);
|
||||
span->setOppValue(0);
|
||||
this->markDone(span);
|
||||
}
|
||||
|
||||
// Quads and conics collapse if the end points are the same, because
|
||||
// the curve doesn't enclose an area.
|
||||
bool SkOpSegment::collapsed() const {
|
||||
return fVerb == SkPath::kLine_Verb && fHead.pt() == fTail.pt();
|
||||
// FIXME: cubics can have also collapsed -- need to check if the
|
||||
// control points are on a line with the end points
|
||||
return fVerb < SkPath::kCubic_Verb && fHead.pt() == fTail.pt();
|
||||
}
|
||||
|
||||
void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
|
||||
@ -571,12 +522,26 @@ int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
|
||||
return start->starter(end)->windSum();
|
||||
}
|
||||
|
||||
bool SkOpSegment::contains(double newT) const {
|
||||
const SkOpSpanBase* spanBase = &fHead;
|
||||
do {
|
||||
if (spanBase->ptT()->contains(this, newT)) {
|
||||
return true;
|
||||
}
|
||||
if (spanBase == &fTail) {
|
||||
break;
|
||||
}
|
||||
spanBase = spanBase->upCast()->next();
|
||||
} while (true);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkOpSegment::release(const SkOpSpan* span) {
|
||||
if (span->done()) {
|
||||
--fDoneCount;
|
||||
}
|
||||
--fCount;
|
||||
SkASSERT(fCount >= fDoneCount);
|
||||
SkASSERT(this->globalState()->debugSkipAssert() || fCount >= fDoneCount);
|
||||
}
|
||||
|
||||
double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
|
||||
@ -601,15 +566,6 @@ double SkOpSegment::distSq(double t, const SkOpAngle* oppAngle) const {
|
||||
return closestDistSq;
|
||||
}
|
||||
|
||||
void SkOpSegment::findCollapsed() {
|
||||
if (fHead.contains(&fTail)) {
|
||||
markAllDone();
|
||||
// move start and end to the same point
|
||||
fHead.alignEnd(0, fHead.pt());
|
||||
fTail.setAligned();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The M and S variable name parts stand for the operators.
|
||||
Mi stands for Minuend (see wiki subtraction, analogous to difference)
|
||||
@ -893,8 +849,6 @@ SkOpGlobalState* SkOpSegment::globalState() const {
|
||||
void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
|
||||
fContour = contour;
|
||||
fNext = nullptr;
|
||||
fOriginal[0] = pts[0];
|
||||
fOriginal[1] = pts[SkPathOpsVerbToPoints(verb)];
|
||||
fPts = pts;
|
||||
fWeight = weight;
|
||||
fVerb = verb;
|
||||
@ -1095,15 +1049,17 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
|
||||
}
|
||||
|
||||
bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
|
||||
const SkPoint& testPt) const {
|
||||
const SkOpSegment* baseParent = base->segment();
|
||||
if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
|
||||
return true;
|
||||
const SkPoint& testPt, AliasMatch aliasMatch) const {
|
||||
SkASSERT(this == base->segment());
|
||||
if (this == testParent) {
|
||||
if (precisely_equal(base->fT, testT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
|
||||
return false;
|
||||
}
|
||||
return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
|
||||
return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
|
||||
}
|
||||
|
||||
static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
|
||||
@ -1178,7 +1134,8 @@ SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpS
|
||||
return other;
|
||||
}
|
||||
|
||||
static void clear_visited(SkOpSpanBase* span) {
|
||||
// Please keep this in sync with DebugClearVisited()
|
||||
void SkOpSegment::ClearVisited(SkOpSpanBase* span) {
|
||||
// reset visited flag back to false
|
||||
do {
|
||||
SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
|
||||
@ -1189,20 +1146,23 @@ static void clear_visited(SkOpSpanBase* span) {
|
||||
} while (!span->final() && (span = span->upCast()->next()));
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugMissingCoincidence()
|
||||
// look for pairs of undetected coincident curves
|
||||
// assumes that segments going in have visited flag clear
|
||||
// curve/curve intersection should now do a pretty good job of finding coincident runs so
|
||||
// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
|
||||
// the opp is not a line
|
||||
bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
|
||||
if (this->verb() != SkPath::kLine_Verb) {
|
||||
return false;
|
||||
}
|
||||
// Even though pairs of curves correct detect coincident runs, a run may be missed
|
||||
// if the coincidence is a product of multiple intersections. For instance, given
|
||||
// curves A, B, and C:
|
||||
// A-B intersect at a point 1; A-C and B-C intersect at point 2, so near
|
||||
// the end of C that the intersection is replaced with the end of C.
|
||||
// Even though A-B correctly do not detect an intersection at point 2,
|
||||
// the resulting run from point 1 to point 2 is coincident on A and B.
|
||||
bool SkOpSegment::missingCoincidence() {
|
||||
if (this->done()) {
|
||||
return false;
|
||||
}
|
||||
SkOpSpan* prior = nullptr;
|
||||
SkOpSpanBase* spanBase = &fHead;
|
||||
bool result = false;
|
||||
do {
|
||||
SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
|
||||
SkASSERT(ptT->span() == spanBase);
|
||||
@ -1211,9 +1171,6 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
|
||||
continue;
|
||||
}
|
||||
SkOpSegment* opp = ptT->span()->segment();
|
||||
// if (opp->verb() == SkPath::kLine_Verb) {
|
||||
// continue;
|
||||
// }
|
||||
if (opp->done()) {
|
||||
continue;
|
||||
}
|
||||
@ -1224,15 +1181,15 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
|
||||
if (spanBase == &fHead) {
|
||||
continue;
|
||||
}
|
||||
if (ptT->segment() == this) {
|
||||
continue;
|
||||
}
|
||||
SkOpSpan* span = spanBase->upCastable();
|
||||
// FIXME?: this assumes that if the opposite segment is coincident then no more
|
||||
// coincidence needs to be detected. This may not be true.
|
||||
if (span && span->containsCoincidence(opp)) {
|
||||
continue;
|
||||
}
|
||||
if (spanBase->segment() == opp) {
|
||||
continue;
|
||||
}
|
||||
if (spanBase->containsCoinEnd(opp)) {
|
||||
continue;
|
||||
}
|
||||
@ -1268,28 +1225,28 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
|
||||
SkTSwap(priorPtT, ptT);
|
||||
SkTSwap(oppStart, oppEnd);
|
||||
}
|
||||
bool flipped = oppStart->fT > oppEnd->fT;
|
||||
bool coincident = false;
|
||||
if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
|
||||
SkOpCoincidence* coincidences = this->globalState()->coincidence();
|
||||
SkOpPtT* rootPriorPtT = priorPtT->span()->ptT();
|
||||
SkOpPtT* rootPtT = ptT->span()->ptT();
|
||||
SkOpPtT* rootOppStart = oppStart->span()->ptT();
|
||||
SkOpPtT* rootOppEnd = oppEnd->span()->ptT();
|
||||
if (coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
|
||||
goto swapBack;
|
||||
}
|
||||
if (opp->verb() == SkPath::kLine_Verb) {
|
||||
coincident = (SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppStart->fPt) ||
|
||||
SkDPoint::ApproximatelyEqual(priorPtT->fPt, oppEnd->fPt)) &&
|
||||
(SkDPoint::ApproximatelyEqual(ptT->fPt, oppStart->fPt) ||
|
||||
SkDPoint::ApproximatelyEqual(ptT->fPt, oppEnd->fPt));
|
||||
}
|
||||
if (!coincident) {
|
||||
coincident = testForCoincidence(priorPtT, ptT, prior, spanBase, opp, 5000);
|
||||
}
|
||||
if (coincident) {
|
||||
if (this->testForCoincidence(rootPriorPtT, rootPtT, prior, spanBase, opp)) {
|
||||
// mark coincidence
|
||||
if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd)
|
||||
&& !coincidences->extend(oppStart, oppEnd, priorPtT, ptT)) {
|
||||
coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
SkDebugf("%s coinSpan=%d endSpan=%d oppSpan=%d oppEndSpan=%d\n", __FUNCTION__,
|
||||
rootPriorPtT->debugID(), rootPtT->debugID(), rootOppStart->debugID(),
|
||||
rootOppEnd->debugID());
|
||||
#endif
|
||||
if (!coincidences->extend(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd)) {
|
||||
coincidences->add(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd);
|
||||
}
|
||||
clear_visited(&fHead);
|
||||
return true;
|
||||
#if DEBUG_COINCIDENCE
|
||||
SkASSERT(coincidences->contains(rootPriorPtT, rootPtT, rootOppStart, rootOppEnd));
|
||||
#endif
|
||||
result = true;
|
||||
}
|
||||
swapBack:
|
||||
if (swapped) {
|
||||
@ -1297,19 +1254,18 @@ bool SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc
|
||||
}
|
||||
}
|
||||
} while ((spanBase = spanBase->final() ? nullptr : spanBase->upCast()->next()));
|
||||
clear_visited(&fHead);
|
||||
return false;
|
||||
ClearVisited(&fHead);
|
||||
return result;
|
||||
}
|
||||
|
||||
// please keep this in sync with debugMoveMultiples()
|
||||
// if a span has more than one intersection, merge the other segments' span as needed
|
||||
bool SkOpSegment::moveMultiples() {
|
||||
debugValidate();
|
||||
SkOpSpanBase* test = &fHead;
|
||||
do {
|
||||
int addCount = test->spanAddsCount();
|
||||
if (addCount < 1) {
|
||||
return false;
|
||||
}
|
||||
FAIL_IF(addCount < 1);
|
||||
if (addCount == 1) {
|
||||
continue;
|
||||
}
|
||||
@ -1403,55 +1359,115 @@ checkNextSpan:
|
||||
return true;
|
||||
}
|
||||
|
||||
// adjacent spans may have points close by
|
||||
bool SkOpSegment::spansNearby(const SkOpSpanBase* refSpan, const SkOpSpanBase* checkSpan) const {
|
||||
const SkOpPtT* refHead = refSpan->ptT();
|
||||
const SkOpPtT* checkHead = checkSpan->ptT();
|
||||
// if the first pt pair from adjacent spans are far apart, assume that all are far enough apart
|
||||
if (!SkDPoint::RoughlyEqual(refHead->fPt, checkHead->fPt)) {
|
||||
#if DEBUG_COINCIDENCE
|
||||
// verify that no combination of points are close
|
||||
const SkOpPtT* dBugRef = refHead;
|
||||
do {
|
||||
const SkOpPtT* dBugCheck = checkHead;
|
||||
do {
|
||||
SkASSERT(!SkDPoint::ApproximatelyEqual(dBugRef->fPt, dBugCheck->fPt));
|
||||
dBugCheck = dBugCheck->next();
|
||||
} while (dBugCheck != checkHead);
|
||||
dBugRef = dBugRef->next();
|
||||
} while (dBugRef != refHead);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
// check only unique points
|
||||
SkScalar distSqBest = SK_ScalarMax;
|
||||
const SkOpPtT* refBest = nullptr;
|
||||
const SkOpPtT* checkBest = nullptr;
|
||||
const SkOpPtT* ref = refHead;
|
||||
do {
|
||||
if (ref->deleted()) {
|
||||
continue;
|
||||
}
|
||||
while (ref->ptAlreadySeen(refHead)) {
|
||||
ref = ref->next();
|
||||
if (ref == refHead) {
|
||||
goto doneCheckingDistance;
|
||||
}
|
||||
}
|
||||
const SkOpPtT* check = checkHead;
|
||||
const SkOpSegment* refSeg = ref->segment();
|
||||
do {
|
||||
if (check->deleted()) {
|
||||
continue;
|
||||
}
|
||||
while (check->ptAlreadySeen(checkHead)) {
|
||||
check = check->next();
|
||||
if (check == checkHead) {
|
||||
goto nextRef;
|
||||
}
|
||||
}
|
||||
SkScalar distSq = ref->fPt.distanceToSqd(check->fPt);
|
||||
if (distSqBest > distSq && (refSeg != check->segment()
|
||||
|| !refSeg->ptsDisjoint(*ref, *check))) {
|
||||
distSqBest = distSq;
|
||||
refBest = ref;
|
||||
checkBest = check;
|
||||
}
|
||||
} while ((check = check->next()) != checkHead);
|
||||
nextRef:
|
||||
;
|
||||
} while ((ref = ref->next()) != refHead);
|
||||
doneCheckingDistance:
|
||||
return checkBest && refBest->segment()->match(refBest, checkBest->segment(), checkBest->fT,
|
||||
checkBest->fPt, kAllowAliasMatch);
|
||||
}
|
||||
|
||||
// Please keep this function in sync with debugMoveNearby()
|
||||
// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
|
||||
void SkOpSegment::moveNearby() {
|
||||
debugValidate();
|
||||
SkOpSpanBase* spanS = &fHead;
|
||||
// release undeleted spans pointing to this seg that are linked to the primary span
|
||||
SkOpSpanBase* spanBase = &fHead;
|
||||
do {
|
||||
SkOpSpanBase* test = spanS->upCast()->next();
|
||||
SkOpSpanBase* next;
|
||||
if (spanS->contains(test)) {
|
||||
if (!test->final()) {
|
||||
test->upCast()->release(spanS->ptT());
|
||||
continue;
|
||||
} else if (spanS != &fHead) {
|
||||
spanS->upCast()->release(test->ptT());
|
||||
spanS = test;
|
||||
continue;
|
||||
SkOpPtT* ptT = spanBase->ptT();
|
||||
const SkOpPtT* headPtT = ptT;
|
||||
while ((ptT = ptT->next()) != headPtT) {
|
||||
SkOpSpanBase* test = ptT->span();
|
||||
if (ptT->segment() == this && !ptT->deleted() && test != spanBase
|
||||
&& test->ptT() == ptT) {
|
||||
if (test->final()) {
|
||||
if (spanBase == &fHead) {
|
||||
this->clearAll();
|
||||
return;
|
||||
}
|
||||
spanBase->upCast()->release(ptT);
|
||||
} else if (test->prev()) {
|
||||
test->upCast()->release(headPtT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
do { // iterate through all spans associated with start
|
||||
SkOpPtT* startBase = spanS->ptT();
|
||||
next = test->final() ? nullptr : test->upCast()->next();
|
||||
do {
|
||||
SkOpPtT* testBase = test->ptT();
|
||||
do {
|
||||
if (startBase == testBase) {
|
||||
goto checkNextSpan;
|
||||
}
|
||||
if (testBase->duplicate()) {
|
||||
continue;
|
||||
}
|
||||
if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
|
||||
if (test == &this->fTail) {
|
||||
if (spanS == &fHead) {
|
||||
debugValidate();
|
||||
return; // if this span has collapsed, remove it from parent
|
||||
}
|
||||
this->fTail.merge(spanS->upCast());
|
||||
debugValidate();
|
||||
return;
|
||||
}
|
||||
spanS->merge(test->upCast());
|
||||
goto checkNextSpan;
|
||||
}
|
||||
} while ((testBase = testBase->next()) != test->ptT());
|
||||
} while ((startBase = startBase->next()) != spanS->ptT());
|
||||
checkNextSpan:
|
||||
;
|
||||
} while ((test = next));
|
||||
spanS = spanS->upCast()->next();
|
||||
} while (!spanS->final());
|
||||
spanBase = spanBase->upCast()->next();
|
||||
} while (!spanBase->final());
|
||||
|
||||
// This loop looks for adjacent spans which are near by
|
||||
spanBase = &fHead;
|
||||
do { // iterate through all spans associated with start
|
||||
SkOpSpanBase* test = spanBase->upCast()->next();
|
||||
if (this->spansNearby(spanBase, test)) {
|
||||
if (test->final()) {
|
||||
if (spanBase->prev()) {
|
||||
test->merge(spanBase->upCast());
|
||||
} else {
|
||||
this->clearAll();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
spanBase->merge(test->upCast());
|
||||
}
|
||||
}
|
||||
spanBase = test;
|
||||
} while (!spanBase->final());
|
||||
debugValidate();
|
||||
}
|
||||
|
||||
@ -1679,8 +1695,7 @@ bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
|
||||
}
|
||||
|
||||
bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT,
|
||||
const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp,
|
||||
SkScalar flatnessLimit) const {
|
||||
const SkOpSpanBase* prior, const SkOpSpanBase* spanBase, const SkOpSegment* opp) const {
|
||||
// average t, find mid pt
|
||||
double midT = (prior->t() + spanBase->t()) / 2;
|
||||
SkPoint midPt = this->ptAtT(midT);
|
||||
@ -1688,22 +1703,28 @@ bool SkOpSegment::testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT
|
||||
// if the mid pt is not near either end pt, project perpendicular through opp seg
|
||||
if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
|
||||
&& !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
|
||||
if (priorPtT->span() == ptT->span()) {
|
||||
return false;
|
||||
}
|
||||
coincident = false;
|
||||
SkIntersections i;
|
||||
SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
|
||||
SkDLine ray = {{{midPt.fX, midPt.fY},
|
||||
{(double) midPt.fX + dxdy.fY, (double) midPt.fY - dxdy.fX}}};
|
||||
(*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
|
||||
SkDCurve curvePart;
|
||||
this->subDivide(prior, spanBase, &curvePart);
|
||||
SkDVector dxdy = (*CurveDDSlopeAtT[fVerb])(curvePart, 0.5f);
|
||||
SkDPoint partMidPt = (*CurveDDPointAtT[fVerb])(curvePart, 0.5f);
|
||||
SkDLine ray = {{{midPt.fX, midPt.fY}, {partMidPt.fX + dxdy.fY, partMidPt.fY - dxdy.fX}}};
|
||||
SkDCurve oppPart;
|
||||
opp->subDivide(priorPtT->span(), ptT->span(), &oppPart);
|
||||
(*CurveDIntersectRay[opp->verb()])(oppPart, ray, &i);
|
||||
// measure distance and see if it's small enough to denote coincidence
|
||||
for (int index = 0; index < i.used(); ++index) {
|
||||
if (!between(0, i[0][index], 1)) {
|
||||
continue;
|
||||
}
|
||||
SkDPoint oppPt = i.pt(index);
|
||||
if (oppPt.approximatelyEqual(midPt)) {
|
||||
SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
|
||||
opp->weight(), i[index][0]);
|
||||
oppDxdy.normalize();
|
||||
dxdy.normalize();
|
||||
SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
|
||||
coincident |= flatness < flatnessLimit;
|
||||
// the coincidence can occur at almost any angle
|
||||
coincident = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ class SkPathWriter;
|
||||
|
||||
class SkOpSegment {
|
||||
public:
|
||||
enum AllowAlias {
|
||||
kAllowAlias,
|
||||
kNoAlias
|
||||
enum AliasMatch {
|
||||
kNoAliasMatch,
|
||||
kAllowAliasMatch,
|
||||
};
|
||||
|
||||
bool operator<(const SkOpSegment& rh) const {
|
||||
@ -45,13 +45,6 @@ public:
|
||||
|
||||
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
|
||||
bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
|
||||
void addAlignIntersection(SkOpPtT& endPtT, SkPoint& oldPt,
|
||||
SkOpContourHead* contourList, SkChunkAlloc* allocator);
|
||||
|
||||
void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
|
||||
this->addAlignIntersection(*fHead.ptT(), fOriginal[0], contourList, allocator);
|
||||
this->addAlignIntersection(*fTail.ptT(), fOriginal[1], contourList, allocator);
|
||||
}
|
||||
|
||||
SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) {
|
||||
init(pts, weight, parent, SkPath::kConic_Verb);
|
||||
@ -71,23 +64,25 @@ public:
|
||||
|
||||
bool addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const;
|
||||
|
||||
SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
|
||||
SkOpAngle* addEndSpan() {
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
|
||||
angle->set(&fTail, fTail.prev());
|
||||
fTail.setFromAngle(angle);
|
||||
return angle;
|
||||
}
|
||||
|
||||
bool addExpanded(double newT, const SkOpSpanBase* test, bool* startOver);
|
||||
|
||||
SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) {
|
||||
init(pts, 1, parent, SkPath::kLine_Verb);
|
||||
fBounds.set(pts, 2);
|
||||
return this;
|
||||
}
|
||||
|
||||
SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
|
||||
SkOpPtT* addMissing(double t, SkOpSegment* opp, bool* allExist);
|
||||
|
||||
SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
|
||||
SkOpAngle* addStartSpan() {
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
|
||||
angle->set(&fHead, fHead.next());
|
||||
fHead.setToAngle(angle);
|
||||
return angle;
|
||||
@ -101,9 +96,11 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
|
||||
SkOpPtT* addT(double t, AliasMatch, bool* allocated);
|
||||
|
||||
void align();
|
||||
template<typename T> T* allocateArray(int count) {
|
||||
return SkOpTAllocator<T>::AllocateArray(this->globalState()->allocator(), count);
|
||||
}
|
||||
|
||||
const SkPathOpsBounds& bounds() const {
|
||||
return fBounds;
|
||||
@ -113,7 +110,7 @@ public:
|
||||
++fCount;
|
||||
}
|
||||
|
||||
void calcAngles(SkChunkAlloc*);
|
||||
void calcAngles();
|
||||
bool collapsed() const;
|
||||
static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
|
||||
SkOpAngle::IncludeType );
|
||||
@ -121,6 +118,11 @@ public:
|
||||
SkOpAngle::IncludeType );
|
||||
int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
|
||||
|
||||
void clearAll();
|
||||
void clearOne(SkOpSpan* span);
|
||||
static void ClearVisited(SkOpSpanBase* span);
|
||||
bool contains(double t) const;
|
||||
|
||||
SkOpContour* contour() const {
|
||||
return fContour;
|
||||
}
|
||||
@ -129,36 +131,30 @@ public:
|
||||
return fCount;
|
||||
}
|
||||
|
||||
void debugAddAngle(double startT, double endT, SkChunkAlloc*);
|
||||
void debugAddAlignIntersection(const char* id, SkPathOpsDebug::GlitchLog* glitches,
|
||||
const SkOpPtT& endPtT, const SkPoint& oldPt,
|
||||
const SkOpContourHead* ) const;
|
||||
|
||||
void debugAddAlignIntersections(const char* id, SkPathOpsDebug::GlitchLog* glitches,
|
||||
SkOpContourHead* contourList) const {
|
||||
this->debugAddAlignIntersection(id, glitches, *fHead.ptT(), fOriginal[0], contourList);
|
||||
this->debugAddAlignIntersection(id, glitches, *fTail.ptT(), fOriginal[1], contourList);
|
||||
}
|
||||
|
||||
bool debugAddMissing(double t, const SkOpSegment* opp) const;
|
||||
void debugAlign(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
void debugAddAngle(double startT, double endT);
|
||||
const SkOpPtT* debugAddT(double t, AliasMatch , bool* allocated) const;
|
||||
const SkOpAngle* debugAngle(int id) const;
|
||||
#if DEBUG_ANGLE
|
||||
void debugCheckAngleCoin() const;
|
||||
#endif
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugCheckHealth(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
void debugClearAll(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
void debugClearOne(const SkOpSpan* span, const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
#endif
|
||||
const SkOpCoincidence* debugCoincidence() const;
|
||||
SkOpContour* debugContour(int id);
|
||||
void debugFindCollapsed(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
|
||||
int debugID() const {
|
||||
return SkDEBUGRELEASE(fID, -1);
|
||||
}
|
||||
|
||||
SkOpAngle* debugLastAngle();
|
||||
void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches,
|
||||
const SkOpCoincidence* coincidences) const;
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugMissingCoincidence(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
void debugMoveMultiples(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
void debugMoveNearby(const char* id, SkPathOpsDebug::GlitchLog* glitches) const;
|
||||
#endif
|
||||
const SkOpPtT* debugPtT(int id) const;
|
||||
void debugReset();
|
||||
const SkOpSegment* debugSegment(int id) const;
|
||||
@ -173,11 +169,24 @@ public:
|
||||
|
||||
const SkOpSpanBase* debugSpan(int id) const;
|
||||
void debugValidate() const;
|
||||
|
||||
#if DEBUG_COINCIDENCE
|
||||
static void SkOpSegment::DebugClearVisited(const SkOpSpanBase* span);
|
||||
|
||||
bool debugVisited() const {
|
||||
if (!fDebugVisited) {
|
||||
fDebugVisited = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void release(const SkOpSpan* );
|
||||
double distSq(double t, const SkOpAngle* opp) const;
|
||||
|
||||
bool done() const {
|
||||
SkASSERT(fDoneCount <= fCount);
|
||||
SkASSERT(this->globalState()->debugSkipAssert() || fDoneCount <= fCount);
|
||||
return fDoneCount == fCount;
|
||||
}
|
||||
|
||||
@ -200,7 +209,7 @@ public:
|
||||
void dumpPts(const char* prefix = "seg") const;
|
||||
void dumpPtsInner(const char* prefix = "seg") const;
|
||||
|
||||
void findCollapsed();
|
||||
const SkOpPtT* existing(double t, const SkOpSegment* opp) const;
|
||||
SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
|
||||
SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
|
||||
int xorMiMask, int xorSuMask);
|
||||
@ -220,8 +229,8 @@ public:
|
||||
|
||||
void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb);
|
||||
|
||||
SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
|
||||
SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
|
||||
SkOpSpan* insert(SkOpSpan* prev) {
|
||||
SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(this->globalState()->allocator());
|
||||
SkOpSpanBase* next = prev->next();
|
||||
result->setPrev(prev);
|
||||
prev->setNext(result);
|
||||
@ -269,8 +278,9 @@ public:
|
||||
void markDone(SkOpSpan* );
|
||||
bool markWinding(SkOpSpan* , int winding);
|
||||
bool markWinding(SkOpSpan* , int winding, int oppWinding);
|
||||
bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
|
||||
bool missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
|
||||
bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt,
|
||||
AliasMatch ) const;
|
||||
bool missingCoincidence();
|
||||
bool moveMultiples();
|
||||
void moveNearby();
|
||||
|
||||
@ -302,17 +312,31 @@ public:
|
||||
}
|
||||
|
||||
bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
|
||||
SkASSERT(this == span.segment());
|
||||
SkASSERT(this == test.segment());
|
||||
return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
|
||||
}
|
||||
|
||||
bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
|
||||
SkASSERT(this == span.segment());
|
||||
return ptsDisjoint(span.fT, span.fPt, t, pt);
|
||||
}
|
||||
|
||||
bool ptsDisjoint(const SkOpSpanBase* span, const SkOpSpanBase* test) const {
|
||||
SkASSERT(this == span->segment());
|
||||
SkASSERT(this == test->segment());
|
||||
return ptsDisjoint(span->t(), span->pt(), test->t(), test->pt());
|
||||
}
|
||||
|
||||
bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
|
||||
|
||||
void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
|
||||
SkChunkAlloc* allocator);
|
||||
void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc*);
|
||||
|
||||
#if DEBUG_COINCIDENCE
|
||||
void resetDebugVisited() const {
|
||||
fDebugVisited = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void resetVisited() {
|
||||
fVisited = false;
|
||||
@ -330,10 +354,6 @@ public:
|
||||
fPrev = prev;
|
||||
}
|
||||
|
||||
void setVisited() {
|
||||
fVisited = true;
|
||||
}
|
||||
|
||||
void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
|
||||
int deltaSum = SpanSign(start, end);
|
||||
*maxWinding = *sumWinding;
|
||||
@ -348,6 +368,7 @@ public:
|
||||
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
|
||||
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
|
||||
void sortAngles();
|
||||
bool spansNearby(const SkOpSpanBase* ref, const SkOpSpanBase* check) const;
|
||||
|
||||
static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
|
||||
int result = start->t() < end->t() ? -start->upCast()->windValue()
|
||||
@ -372,9 +393,10 @@ public:
|
||||
}
|
||||
|
||||
bool testForCoincidence(const SkOpPtT* priorPtT, const SkOpPtT* ptT, const SkOpSpanBase* prior,
|
||||
const SkOpSpanBase* spanBase, const SkOpSegment* opp, SkScalar flatnessLimit) const;
|
||||
const SkOpSpanBase* spanBase, const SkOpSegment* opp) const;
|
||||
|
||||
void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
|
||||
bool uniqueT(double t, AliasMatch allowAlias) const;
|
||||
int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
|
||||
int updateOppWinding(const SkOpAngle* angle) const;
|
||||
int updateOppWindingReverse(const SkOpAngle* angle) const;
|
||||
@ -404,17 +426,12 @@ public:
|
||||
SkOpSpan* windingSpanAtT(double tHit);
|
||||
int windSum(const SkOpAngle* angle) const;
|
||||
|
||||
SkPoint* writablePt(bool end) {
|
||||
return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
|
||||
}
|
||||
|
||||
private:
|
||||
SkOpSpan fHead; // the head span always has its t set to zero
|
||||
SkOpSpanBase fTail; // the tail span always has its t set to one
|
||||
SkOpContour* fContour;
|
||||
SkOpSegment* fNext; // forward-only linked list used by contour to walk the segments
|
||||
const SkOpSegment* fPrev;
|
||||
SkPoint fOriginal[2]; // if aligned, the original unaligned points are here
|
||||
SkPoint* fPts; // pointer into array of points owned by edge builder that may be tweaked
|
||||
SkPathOpsBounds fBounds; // tight bounds
|
||||
SkScalar fWeight;
|
||||
@ -422,6 +439,9 @@ private:
|
||||
int fDoneCount; // number of processed spans (zero initially)
|
||||
SkPath::Verb fVerb;
|
||||
bool fVisited; // used by missing coincidence check
|
||||
#if DEBUG_COINCIDENCE
|
||||
mutable bool fDebugVisited; // used by debug missing coincidence check
|
||||
#endif
|
||||
SkDEBUGCODE(int fID);
|
||||
};
|
||||
|
||||
|
@ -35,12 +35,35 @@ bool SkOpPtT::contains(const SkOpPtT* check) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) {
|
||||
SkASSERT(this->segment() != check);
|
||||
SkOpPtT* ptT = this;
|
||||
bool SkOpPtT::contains(const SkOpSegment* segment, const SkPoint& pt) const {
|
||||
SkASSERT(this->segment() != segment);
|
||||
const SkOpPtT* ptT = this;
|
||||
const SkOpPtT* stopPtT = ptT;
|
||||
while ((ptT = ptT->next()) != stopPtT) {
|
||||
if (ptT->segment() == check) {
|
||||
if (ptT->fPt == pt && ptT->segment() == segment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkOpPtT::contains(const SkOpSegment* segment, double t) const {
|
||||
const SkOpPtT* ptT = this;
|
||||
const SkOpPtT* stopPtT = ptT;
|
||||
while ((ptT = ptT->next()) != stopPtT) {
|
||||
if (ptT->fT == t && ptT->segment() == segment) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const SkOpPtT* SkOpPtT::contains(const SkOpSegment* check) const {
|
||||
SkASSERT(this->segment() != check);
|
||||
const SkOpPtT* ptT = this;
|
||||
const SkOpPtT* stopPtT = ptT;
|
||||
while ((ptT = ptT->next()) != stopPtT) {
|
||||
if (ptT->segment() == check && !ptT->deleted()) {
|
||||
return ptT;
|
||||
}
|
||||
}
|
||||
@ -51,33 +74,16 @@ SkOpContour* SkOpPtT::contour() const {
|
||||
return segment()->contour();
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::doppelganger() {
|
||||
SkASSERT(fDeleted);
|
||||
SkOpPtT* ptT = fNext;
|
||||
while (ptT->fDeleted) {
|
||||
ptT = ptT->fNext;
|
||||
}
|
||||
const SkOpPtT* SkOpPtT::find(const SkOpSegment* segment) const {
|
||||
const SkOpPtT* ptT = this;
|
||||
const SkOpPtT* stopPtT = ptT;
|
||||
do {
|
||||
if (ptT->fSpan == fSpan) {
|
||||
if (ptT->segment() == segment && !ptT->deleted()) {
|
||||
return ptT;
|
||||
}
|
||||
ptT = ptT->fNext;
|
||||
} while (stopPtT != ptT);
|
||||
SkASSERT(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::find(SkOpSegment* segment) {
|
||||
SkOpPtT* ptT = this;
|
||||
const SkOpPtT* stopPtT = ptT;
|
||||
do {
|
||||
if (ptT->segment() == segment) {
|
||||
return ptT;
|
||||
}
|
||||
ptT = ptT->fNext;
|
||||
} while (stopPtT != ptT);
|
||||
SkASSERT(0);
|
||||
// SkASSERT(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -92,6 +98,7 @@ void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplica
|
||||
fNext = this;
|
||||
fDuplicatePt = duplicate;
|
||||
fDeleted = false;
|
||||
fCoincident = false;
|
||||
SkDEBUGCODE(fID = span->globalState()->nextPtTID());
|
||||
}
|
||||
|
||||
@ -104,6 +111,16 @@ bool SkOpPtT::onEnd() const {
|
||||
return span == segment->head() || span == segment->tail();
|
||||
}
|
||||
|
||||
bool SkOpPtT::ptAlreadySeen(const SkOpPtT* check) const {
|
||||
while (this != check) {
|
||||
if (this->fPt == check->fPt) {
|
||||
return true;
|
||||
}
|
||||
check = check->fNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::prev() {
|
||||
SkOpPtT* result = this;
|
||||
SkOpPtT* next = this;
|
||||
@ -114,12 +131,12 @@ SkOpPtT* SkOpPtT::prev() {
|
||||
return result;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpPtT::remove() {
|
||||
SkOpPtT* SkOpPtT::remove(const SkOpPtT* kept) {
|
||||
SkOpPtT* prev = this;
|
||||
do {
|
||||
SkOpPtT* next = prev->fNext;
|
||||
if (next == this) {
|
||||
prev->removeNext(this);
|
||||
prev->removeNext(kept);
|
||||
SkASSERT(prev->fNext != prev);
|
||||
fDeleted = true;
|
||||
return prev;
|
||||
@ -130,12 +147,16 @@ SkOpPtT* SkOpPtT::remove() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SkOpPtT::removeNext(SkOpPtT* kept) {
|
||||
void SkOpPtT::removeNext(const SkOpPtT* kept) {
|
||||
SkASSERT(this->fNext);
|
||||
SkOpPtT* next = this->fNext;
|
||||
SkASSERT(this != next->fNext);
|
||||
this->fNext = next->fNext;
|
||||
SkOpSpanBase* span = next->span();
|
||||
SkOpCoincidence* coincidence = span->globalState()->coincidence();
|
||||
if (coincidence) {
|
||||
coincidence->fixUp(next, kept);
|
||||
}
|
||||
next->setDeleted();
|
||||
if (span->ptT() == next) {
|
||||
span->upCast()->release(kept);
|
||||
@ -150,85 +171,68 @@ SkOpSegment* SkOpPtT::segment() {
|
||||
return span()->segment();
|
||||
}
|
||||
|
||||
void SkOpSpanBase::align() {
|
||||
if (this->fAligned) {
|
||||
void SkOpPtT::setDeleted() {
|
||||
SkASSERT(this->span()->debugDeleted() || this->span()->ptT() != this);
|
||||
SkASSERT(this->globalState()->debugSkipAssert() || !fDeleted);
|
||||
fDeleted = true;
|
||||
}
|
||||
|
||||
// please keep this in sync with debugAddOppAndMerge
|
||||
// If the added points envelop adjacent spans, merge them in.
|
||||
void SkOpSpanBase::addOppAndMerge(SkOpSpanBase* opp) {
|
||||
if (this->ptT()->addOpp(opp->ptT())) {
|
||||
this->checkForCollapsedCoincidence();
|
||||
}
|
||||
// compute bounds of points in span
|
||||
SkPathOpsBounds bounds;
|
||||
bounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin);
|
||||
const SkOpPtT* head = this->ptT();
|
||||
const SkOpPtT* nextPt = head;
|
||||
do {
|
||||
bounds.add(nextPt->fPt);
|
||||
} while ((nextPt = nextPt->next()) != head);
|
||||
if (!bounds.width() && !bounds.height()) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(!zero_or_one(this->fPtT.fT));
|
||||
SkASSERT(this->fPtT.next());
|
||||
// if a linked pt/t pair has a t of zero or one, use it as the base for alignment
|
||||
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
|
||||
while ((ptT = ptT->next()) != stopPtT) {
|
||||
if (zero_or_one(ptT->fT)) {
|
||||
SkOpSegment* segment = ptT->segment();
|
||||
SkASSERT(this->segment() != segment);
|
||||
SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
|
||||
if (ptT->fT) {
|
||||
segment->tail()->alignEnd(1, segment->lastPt());
|
||||
} else {
|
||||
segment->head()->alignEnd(0, segment->pts()[0]);
|
||||
}
|
||||
this->mergeContained(bounds);
|
||||
opp->mergeContained(bounds);
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugMergeContained()
|
||||
void SkOpSpanBase::mergeContained(const SkPathOpsBounds& bounds) {
|
||||
// while adjacent spans' points are contained by the bounds, merge them
|
||||
SkOpSpanBase* prev = this;
|
||||
SkOpSegment* seg = this->segment();
|
||||
while ((prev = prev->prev()) && bounds.contains(prev->pt()) && !seg->ptsDisjoint(prev, this)) {
|
||||
if (prev->prev()) {
|
||||
this->merge(prev->upCast());
|
||||
prev = this;
|
||||
} else if (this->final()) {
|
||||
seg->clearAll();
|
||||
return;
|
||||
} else {
|
||||
prev->merge(this->upCast());
|
||||
}
|
||||
}
|
||||
SkOpSpanBase* current = this;
|
||||
SkOpSpanBase* next = this;
|
||||
while (next->upCastable() && (next = next->upCast()->next())
|
||||
&& bounds.contains(next->pt()) && !seg->ptsDisjoint(this, next)) {
|
||||
if (!current->prev() && next->final()) {
|
||||
seg->clearAll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
alignInner();
|
||||
this->fAligned = true;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: delete spans that collapse
|
||||
// delete segments that collapse
|
||||
// delete contours that collapse
|
||||
void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
|
||||
SkASSERT(zero_or_one(t));
|
||||
SkOpSegment* segment = this->segment();
|
||||
SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
|
||||
alignInner();
|
||||
*segment->writablePt(!!t) = pt;
|
||||
SkOpPtT* ptT = &this->fPtT;
|
||||
SkASSERT(t == ptT->fT);
|
||||
SkASSERT(pt == ptT->fPt);
|
||||
SkOpPtT* test = ptT, * stopPtT = ptT;
|
||||
while ((test = test->next()) != stopPtT) {
|
||||
SkOpSegment* other = test->segment();
|
||||
if (other == this->segment()) {
|
||||
continue;
|
||||
if (current->prev()) {
|
||||
next->merge(current->upCast());
|
||||
current = next;
|
||||
} else {
|
||||
current->merge(next->upCast());
|
||||
// extra line in debug version
|
||||
}
|
||||
if (!zero_or_one(test->fT)) {
|
||||
continue;
|
||||
}
|
||||
*other->writablePt(!!test->fT) = pt;
|
||||
}
|
||||
this->fAligned = true;
|
||||
}
|
||||
|
||||
void SkOpSpanBase::alignInner() {
|
||||
// force the spans to share points and t values
|
||||
SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
|
||||
const SkPoint& pt = ptT->fPt;
|
||||
do {
|
||||
ptT->fPt = pt;
|
||||
const SkOpSpanBase* span = ptT->span();
|
||||
SkOpPtT* test = ptT;
|
||||
do {
|
||||
SkOpPtT* prev = test;
|
||||
if ((test = test->next()) == stopPtT) {
|
||||
break;
|
||||
}
|
||||
if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
|
||||
// omit aliases that alignment makes redundant
|
||||
if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
|
||||
SkASSERT(test->alias());
|
||||
prev->removeNext(ptT);
|
||||
test = prev;
|
||||
} else {
|
||||
SkASSERT(ptT->alias());
|
||||
stopPtT = ptT = ptT->remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
} while ((ptT = ptT->next()) != stopPtT);
|
||||
#if DEBUG_COINCIDENCE
|
||||
this->globalState()->coincidence()->debugValidate();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
|
||||
@ -244,11 +248,14 @@ bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
|
||||
SkOpPtT* start = &fPtT;
|
||||
SkOpPtT* walk = start;
|
||||
const SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) const {
|
||||
const SkOpPtT* start = &fPtT;
|
||||
const SkOpPtT* walk = start;
|
||||
while ((walk = walk->next()) != start) {
|
||||
if (walk->segment() == segment) {
|
||||
if (walk->deleted()) {
|
||||
continue;
|
||||
}
|
||||
if (walk->segment() == segment && walk->span()->ptT() == walk) {
|
||||
return walk;
|
||||
}
|
||||
}
|
||||
@ -285,6 +292,7 @@ void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, cons
|
||||
fChased = false;
|
||||
SkDEBUGCODE(fCount = 1);
|
||||
SkDEBUGCODE(fID = globalState()->nextSpanID());
|
||||
SkDEBUGCODE(fDeleted = false);
|
||||
}
|
||||
|
||||
// this pair of spans share a common t value or point; merge them and eliminate duplicates
|
||||
@ -294,8 +302,11 @@ void SkOpSpanBase::merge(SkOpSpan* span) {
|
||||
SkASSERT(this->t() != spanPtT->fT);
|
||||
SkASSERT(!zero_or_one(spanPtT->fT));
|
||||
span->release(this->ptT());
|
||||
if (this->contains(span)) {
|
||||
return; // merge is already in the ptT loop
|
||||
}
|
||||
SkOpPtT* remainder = spanPtT->next();
|
||||
ptT()->insert(spanPtT);
|
||||
this->ptT()->insert(spanPtT);
|
||||
while (remainder != spanPtT) {
|
||||
SkOpPtT* next = remainder->next();
|
||||
SkOpPtT* compare = spanPtT->next();
|
||||
@ -311,6 +322,26 @@ tryNextRemainder:
|
||||
remainder = next;
|
||||
}
|
||||
fSpanAdds += span->fSpanAdds;
|
||||
this->checkForCollapsedCoincidence();
|
||||
}
|
||||
|
||||
// please keep in sync with debugCheckForCollapsedCoincidence()
|
||||
void SkOpSpanBase::checkForCollapsedCoincidence() {
|
||||
SkOpCoincidence* coins = this->globalState()->coincidence();
|
||||
if (coins->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// the insert above may have put both ends of a coincident run in the same span
|
||||
// for each coincident ptT in loop; see if its opposite in is also in the loop
|
||||
// this implementation is the motivation for marking that a ptT is referenced by a coincident span
|
||||
SkOpPtT* head = this->ptT();
|
||||
SkOpPtT* test = head;
|
||||
do {
|
||||
if (!test->coincident()) {
|
||||
continue;
|
||||
}
|
||||
coins->markCollapsed(test);
|
||||
} while ((test = test->next()) != head);
|
||||
}
|
||||
|
||||
int SkOpSpan::computeWindSum() {
|
||||
@ -334,7 +365,45 @@ bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkOpSpan::release(SkOpPtT* kept) {
|
||||
void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
|
||||
SkASSERT(t != 1);
|
||||
initBase(segment, prev, t, pt);
|
||||
fCoincident = this;
|
||||
fToAngle = nullptr;
|
||||
fWindSum = fOppSum = SK_MinS32;
|
||||
fWindValue = 1;
|
||||
fOppValue = 0;
|
||||
fTopTTry = 0;
|
||||
fChased = fDone = false;
|
||||
segment->bumpCount();
|
||||
fAlreadyAdded = false;
|
||||
}
|
||||
|
||||
// Please keep this in sync with debugInsertCoincidence()
|
||||
bool SkOpSpan::insertCoincidence(const SkOpSegment* segment, bool flipped) {
|
||||
if (this->containsCoincidence(segment)) {
|
||||
return true;
|
||||
}
|
||||
SkOpPtT* next = &fPtT;
|
||||
while ((next = next->next()) != &fPtT) {
|
||||
if (next->segment() == segment) {
|
||||
SkOpSpan* span = flipped ? next->span()->prev() : next->span()->upCast();
|
||||
if (!span) {
|
||||
return false;
|
||||
}
|
||||
this->insertCoincidence(span);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#if DEBUG_COINCIDENCE
|
||||
SkASSERT(0); // FIXME? if we get here, the span is missing its opposite segment...
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkOpSpan::release(const SkOpPtT* kept) {
|
||||
SkDEBUGCODE(fDeleted = true);
|
||||
SkASSERT(kept->span() != this);
|
||||
SkASSERT(!final());
|
||||
SkOpSpan* prev = this->prev();
|
||||
SkASSERT(prev);
|
||||
@ -348,20 +417,14 @@ void SkOpSpan::release(SkOpPtT* kept) {
|
||||
coincidence->fixUp(this->ptT(), kept);
|
||||
}
|
||||
this->ptT()->setDeleted();
|
||||
}
|
||||
|
||||
void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
|
||||
SkASSERT(t != 1);
|
||||
initBase(segment, prev, t, pt);
|
||||
fCoincident = this;
|
||||
fToAngle = nullptr;
|
||||
fWindSum = fOppSum = SK_MinS32;
|
||||
fWindValue = 1;
|
||||
fOppValue = 0;
|
||||
fTopTTry = 0;
|
||||
fChased = fDone = false;
|
||||
segment->bumpCount();
|
||||
fAlreadyAdded = false;
|
||||
SkOpPtT* stopPtT = this->ptT();
|
||||
SkOpPtT* testPtT = stopPtT;
|
||||
const SkOpSpanBase* keptSpan = kept->span();
|
||||
do {
|
||||
if (this == testPtT->span()) {
|
||||
testPtT->setSpan(keptSpan);
|
||||
}
|
||||
} while ((testPtT = testPtT->next()) != stopPtT);
|
||||
}
|
||||
|
||||
void SkOpSpan::setOppSum(int oppSum) {
|
||||
|
@ -12,12 +12,13 @@
|
||||
#include "SkPoint.h"
|
||||
|
||||
class SkChunkAlloc;
|
||||
struct SkOpAngle;
|
||||
class SkOpAngle;
|
||||
class SkOpContour;
|
||||
class SkOpGlobalState;
|
||||
class SkOpSegment;
|
||||
class SkOpSpanBase;
|
||||
class SkOpSpan;
|
||||
struct SkPathOpsBounds;
|
||||
|
||||
// subset of op span used by terminal span (when t is equal to one)
|
||||
class SkOpPtT {
|
||||
@ -27,37 +28,43 @@ public:
|
||||
kIsDuplicate = 1
|
||||
};
|
||||
|
||||
void addOpp(SkOpPtT* opp) {
|
||||
// please keep in sync with debugAddOpp()
|
||||
bool addOpp(SkOpPtT* opp) {
|
||||
// find the fOpp ptr to opp
|
||||
SkOpPtT* oppPrev = opp->fNext;
|
||||
if (oppPrev == this) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
while (oppPrev->fNext != opp) {
|
||||
oppPrev = oppPrev->fNext;
|
||||
if (oppPrev == this) {
|
||||
return;
|
||||
}
|
||||
if (oppPrev == this) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SkOpPtT* oldNext = this->fNext;
|
||||
SkASSERT(this != opp);
|
||||
this->fNext = opp;
|
||||
SkASSERT(oppPrev != oldNext);
|
||||
oppPrev->fNext = oldNext;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool alias() const;
|
||||
bool coincident() const { return fCoincident; }
|
||||
bool collapsed(const SkOpPtT* ) const;
|
||||
bool contains(const SkOpPtT* ) const;
|
||||
SkOpPtT* contains(const SkOpSegment* );
|
||||
bool contains(const SkOpSegment*, const SkPoint& ) const;
|
||||
bool contains(const SkOpSegment*, double t) const;
|
||||
const SkOpPtT* contains(const SkOpSegment* ) const;
|
||||
SkOpContour* contour() const;
|
||||
|
||||
int debugID() const {
|
||||
return SkDEBUGRELEASE(fID, -1);
|
||||
}
|
||||
|
||||
bool debugAddOpp(const SkOpPtT* opp) const;
|
||||
const SkOpAngle* debugAngle(int id) const;
|
||||
const SkOpCoincidence* debugCoincidence() const;
|
||||
bool debugContains(const SkOpPtT* ) const;
|
||||
const SkOpPtT* debugContains(const SkOpSegment* check) const;
|
||||
SkOpContour* debugContour(int id);
|
||||
@ -72,8 +79,6 @@ public:
|
||||
return fDeleted;
|
||||
}
|
||||
|
||||
SkOpPtT* doppelganger();
|
||||
|
||||
bool duplicate() const {
|
||||
return fDuplicatePt;
|
||||
}
|
||||
@ -82,7 +87,7 @@ public:
|
||||
void dumpAll() const;
|
||||
void dumpBase() const;
|
||||
|
||||
SkOpPtT* find(SkOpSegment* );
|
||||
const SkOpPtT* find(const SkOpSegment* ) const;
|
||||
SkOpGlobalState* globalState() const;
|
||||
void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
|
||||
|
||||
@ -102,14 +107,14 @@ public:
|
||||
|
||||
bool onEnd() const;
|
||||
|
||||
static bool Overlaps(SkOpPtT* s1, SkOpPtT* e1, SkOpPtT* s2, SkOpPtT* e2,
|
||||
SkOpPtT** sOut, SkOpPtT** eOut) {
|
||||
SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
|
||||
SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
|
||||
static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
|
||||
const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
|
||||
const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
|
||||
const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
|
||||
*sOut = between(s1->fT, start2->fT, e1->fT) ? start2
|
||||
: between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr;
|
||||
SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
|
||||
SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
|
||||
const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
|
||||
const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
|
||||
*eOut = between(s1->fT, end2->fT, e1->fT) ? end2
|
||||
: between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr;
|
||||
if (*sOut == *eOut) {
|
||||
@ -120,16 +125,23 @@ public:
|
||||
return *sOut && *eOut;
|
||||
}
|
||||
|
||||
bool ptAlreadySeen(const SkOpPtT* head) const;
|
||||
SkOpPtT* prev();
|
||||
SkOpPtT* remove();
|
||||
void removeNext(SkOpPtT* kept);
|
||||
SkOpPtT* remove(const SkOpPtT* kept);
|
||||
void removeNext(const SkOpPtT* kept);
|
||||
|
||||
const SkOpSegment* segment() const;
|
||||
SkOpSegment* segment();
|
||||
|
||||
void setDeleted() {
|
||||
void setCoincident() const {
|
||||
SkASSERT(!fDeleted);
|
||||
fDeleted = true;
|
||||
fCoincident = true;
|
||||
}
|
||||
|
||||
void setDeleted();
|
||||
|
||||
void setSpan(const SkOpSpanBase* span) {
|
||||
fSpan = const_cast<SkOpSpanBase*>(span);
|
||||
}
|
||||
|
||||
const SkOpSpanBase* span() const {
|
||||
@ -151,18 +163,14 @@ protected:
|
||||
SkOpPtT* fNext; // intersection on opposite curve or alias on this curve
|
||||
bool fDeleted; // set if removed from span list
|
||||
bool fDuplicatePt; // set if identical pt is somewhere in the next loop
|
||||
// below mutable since referrer is otherwise always const
|
||||
mutable bool fCoincident; // set if at some point a coincident span pointed here
|
||||
SkDEBUGCODE(int fID);
|
||||
};
|
||||
|
||||
class SkOpSpanBase {
|
||||
public:
|
||||
void align();
|
||||
|
||||
bool aligned() const {
|
||||
return fAligned;
|
||||
}
|
||||
|
||||
void alignEnd(double t, const SkPoint& pt);
|
||||
void addOppAndMerge(SkOpSpanBase* );
|
||||
|
||||
void bumpSpanAdds() {
|
||||
++fSpanAdds;
|
||||
@ -172,17 +180,14 @@ public:
|
||||
return fChased;
|
||||
}
|
||||
|
||||
void clearCoinEnd() {
|
||||
SkASSERT(fCoinEnd != this);
|
||||
fCoinEnd = this;
|
||||
}
|
||||
void checkForCollapsedCoincidence();
|
||||
|
||||
const SkOpSpanBase* coinEnd() const {
|
||||
return fCoinEnd;
|
||||
}
|
||||
|
||||
bool contains(const SkOpSpanBase* ) const;
|
||||
SkOpPtT* contains(const SkOpSegment* );
|
||||
const SkOpPtT* contains(const SkOpSegment* ) const;
|
||||
|
||||
bool containsCoinEnd(const SkOpSpanBase* coin) const {
|
||||
SkASSERT(this != coin);
|
||||
@ -198,6 +203,11 @@ public:
|
||||
bool containsCoinEnd(const SkOpSegment* ) const;
|
||||
SkOpContour* contour() const;
|
||||
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugAddOppAndMerge(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* ,
|
||||
bool* del1, bool* del2) const;
|
||||
#endif
|
||||
|
||||
int debugBumpCount() {
|
||||
return SkDEBUGRELEASE(++fCount, -1);
|
||||
}
|
||||
@ -209,9 +219,21 @@ public:
|
||||
bool debugAlignedEnd(double t, const SkPoint& pt) const;
|
||||
bool debugAlignedInner() const;
|
||||
const SkOpAngle* debugAngle(int id) const;
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* ) const;
|
||||
#endif
|
||||
const SkOpCoincidence* debugCoincidence() const;
|
||||
bool debugCoinEndLoopCheck() const;
|
||||
bool debugContains(const SkOpSegment* ) const;
|
||||
SkOpContour* debugContour(int id);
|
||||
#ifdef SK_DEBUG
|
||||
bool debugDeleted() const { return fDeleted; }
|
||||
#endif
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* ,
|
||||
const SkOpSpanBase* ) const;
|
||||
void debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* ,
|
||||
const SkPathOpsBounds& bounds, bool* deleted) const;
|
||||
#endif
|
||||
const SkOpPtT* debugPtT(int id) const;
|
||||
const SkOpSegment* debugSegment(int id) const;
|
||||
const SkOpSpanBase* debugSpan(int id) const;
|
||||
@ -227,6 +249,7 @@ public:
|
||||
void dumpCoin() const;
|
||||
void dumpAll() const;
|
||||
void dumpBase() const;
|
||||
void dumpHead() const;
|
||||
|
||||
bool final() const {
|
||||
return fPtT.fT == 1;
|
||||
@ -238,6 +261,7 @@ public:
|
||||
|
||||
void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
|
||||
|
||||
// Please keep this in sync with debugInsertCoinEnd()
|
||||
void insertCoinEnd(SkOpSpanBase* coin) {
|
||||
if (containsCoinEnd(coin)) {
|
||||
SkASSERT(coin->containsCoinEnd(this));
|
||||
@ -252,8 +276,13 @@ public:
|
||||
}
|
||||
|
||||
void merge(SkOpSpan* span);
|
||||
void mergeContained(const SkPathOpsBounds& bounds);
|
||||
|
||||
SkOpSpan* prev() const {
|
||||
const SkOpSpan* prev() const {
|
||||
return fPrev;
|
||||
}
|
||||
|
||||
SkOpSpan* prev() {
|
||||
return fPrev;
|
||||
}
|
||||
|
||||
@ -281,8 +310,6 @@ public:
|
||||
fChased = chased;
|
||||
}
|
||||
|
||||
SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
|
||||
|
||||
void setFromAngle(SkOpAngle* angle) {
|
||||
fFromAngle = angle;
|
||||
}
|
||||
@ -368,6 +395,7 @@ protected: // no direct access to internals to avoid treating a span base as a
|
||||
bool fChased; // set after span has been added to chase array
|
||||
SkDEBUGCODE(int fCount); // number of pt/t pairs added
|
||||
SkDEBUGCODE(int fID);
|
||||
SkDEBUGCODE(bool fDeleted); // set when span was merged with another span
|
||||
};
|
||||
|
||||
class SkOpSpan : public SkOpSpanBase {
|
||||
@ -404,7 +432,12 @@ public:
|
||||
}
|
||||
|
||||
bool debugCoinLoopCheck() const;
|
||||
void release(SkOpPtT* );
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const;
|
||||
void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* ,
|
||||
const SkOpSegment* , bool flipped) const;
|
||||
#endif
|
||||
void release(const SkOpPtT* );
|
||||
|
||||
bool done() const {
|
||||
SkASSERT(!final());
|
||||
@ -414,7 +447,9 @@ public:
|
||||
void dumpCoin() const;
|
||||
bool dumpSpan() const;
|
||||
void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
|
||||
bool insertCoincidence(const SkOpSegment* , bool flipped);
|
||||
|
||||
// Please keep this in sync with debugInsertCoincidence()
|
||||
void insertCoincidence(SkOpSpan* coin) {
|
||||
if (containsCoincidence(coin)) {
|
||||
SkASSERT(coin->containsCoincidence(this));
|
||||
@ -470,6 +505,7 @@ public:
|
||||
void setOppValue(int oppValue) {
|
||||
SkASSERT(!final());
|
||||
SkASSERT(fOppSum == SK_MinS32);
|
||||
SkASSERT(!oppValue || !fDone);
|
||||
fOppValue = oppValue;
|
||||
}
|
||||
|
||||
@ -484,6 +520,7 @@ public:
|
||||
SkASSERT(!final());
|
||||
SkASSERT(windValue >= 0);
|
||||
SkASSERT(fWindSum == SK_MinS32);
|
||||
SkASSERT(!windValue || !fDone);
|
||||
fWindValue = windValue;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,13 @@ struct SkPathOpsBounds : public SkRect {
|
||||
add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
|
||||
}
|
||||
|
||||
void add(const SkPoint& pt) {
|
||||
if (pt.fX < fLeft) fLeft = pt.fX;
|
||||
if (pt.fY < fTop) fTop = pt.fY;
|
||||
if (pt.fX > fRight) fRight = pt.fX;
|
||||
if (pt.fY > fBottom) fBottom = pt.fY;
|
||||
}
|
||||
|
||||
void add(const SkDPoint& pt) {
|
||||
if (pt.fX < fLeft) fLeft = SkDoubleToScalar(pt.fX);
|
||||
if (pt.fY < fTop) fTop = SkDoubleToScalar(pt.fY);
|
||||
@ -40,13 +47,18 @@ struct SkPathOpsBounds : public SkRect {
|
||||
if (pt.fY > fBottom) fBottom = SkDoubleToScalar(pt.fY);
|
||||
}
|
||||
|
||||
bool almostContains(const SkPoint& pt) {
|
||||
bool almostContains(const SkPoint& pt) const {
|
||||
return AlmostLessOrEqualUlps(fLeft, pt.fX)
|
||||
&& AlmostLessOrEqualUlps(pt.fX, fRight)
|
||||
&& AlmostLessOrEqualUlps(fTop, pt.fY)
|
||||
&& AlmostLessOrEqualUlps(pt.fY, fBottom);
|
||||
}
|
||||
|
||||
bool contains(const SkPoint& pt) const {
|
||||
return fLeft <= pt.fX && fTop <= pt.fY &&
|
||||
fRight >= pt.fX && fBottom >= pt.fY;
|
||||
}
|
||||
|
||||
typedef SkRect INHERITED;
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,28 @@
|
||||
#include "SkPathWriter.h"
|
||||
#include "SkTSort.h"
|
||||
|
||||
SkScalar ScaleFactor(const SkPath& path) {
|
||||
static const SkScalar twoTo10 = 1024.f;
|
||||
SkScalar largest = 0;
|
||||
const SkScalar* oneBounds = &path.getBounds().fLeft;
|
||||
for (int index = 0; index < 4; ++index) {
|
||||
largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
|
||||
}
|
||||
SkScalar scale = twoTo10;
|
||||
SkScalar next;
|
||||
while ((next = scale * twoTo10) < largest) {
|
||||
scale = next;
|
||||
}
|
||||
return scale == twoTo10 ? SK_Scalar1 : scale;
|
||||
}
|
||||
|
||||
void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
|
||||
SkMatrix matrix;
|
||||
matrix.setScale(scale, scale);
|
||||
*scaled = path;
|
||||
scaled->transform(matrix);
|
||||
}
|
||||
|
||||
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
|
||||
bool* sortablePtr) {
|
||||
// find first angle, initialize winding to computed fWindSum
|
||||
@ -144,15 +166,6 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
void DebugShowActiveSpans(SkOpContourHead* contourList) {
|
||||
SkOpContour* contour = contourList;
|
||||
do {
|
||||
contour->debugShowActiveSpans();
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
|
||||
SkTDArray<SkOpContour* > list;
|
||||
SkOpContour* contour = *contourList;
|
||||
@ -201,7 +214,7 @@ public:
|
||||
void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
|
||||
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
|
||||
SkOpContourHead contour;
|
||||
SkOpGlobalState globalState(nullptr, &contour SkDEBUGPARAMS(false)
|
||||
SkOpGlobalState globalState(&contour, &allocator SkDEBUGPARAMS(false)
|
||||
SkDEBUGPARAMS(nullptr));
|
||||
#if DEBUG_SHOW_TEST_NAME
|
||||
SkDebugf("</div>\n");
|
||||
@ -209,8 +222,8 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
|
||||
#if DEBUG_PATH_CONSTRUCTION
|
||||
SkDebugf("%s\n", __FUNCTION__);
|
||||
#endif
|
||||
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
|
||||
builder.finish(&allocator);
|
||||
SkOpEdgeBuilder builder(path, &contour, &globalState);
|
||||
builder.finish();
|
||||
SkTDArray<const SkOpContour* > runs; // indices of partial contours
|
||||
const SkOpContour* eContour = builder.head();
|
||||
do {
|
||||
@ -391,40 +404,18 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void align(SkOpContourHead* contourList) {
|
||||
static void calcAngles(SkOpContourHead* contourList) {
|
||||
SkOpContour* contour = contourList;
|
||||
do {
|
||||
contour->align();
|
||||
contour->calcAngles();
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
|
||||
static void addAlignIntersections(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
|
||||
SkOpContour* contour = contourList;
|
||||
do {
|
||||
contour->addAlignIntersections(contourList, allocator);
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
|
||||
static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
|
||||
SkOpContour* contour = contourList;
|
||||
do {
|
||||
contour->calcAngles(allocator);
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
|
||||
static void findCollapsed(SkOpContourHead* contourList) {
|
||||
SkOpContour* contour = contourList;
|
||||
do {
|
||||
contour->findCollapsed();
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
|
||||
static bool missingCoincidence(SkOpContourHead* contourList,
|
||||
SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
|
||||
static bool missingCoincidence(SkOpContourHead* contourList) {
|
||||
SkOpContour* contour = contourList;
|
||||
bool result = false;
|
||||
do {
|
||||
result |= contour->missingCoincidence(coincidence, allocator);
|
||||
result |= contour->missingCoincidence();
|
||||
} while ((contour = contour->next()));
|
||||
return result;
|
||||
}
|
||||
@ -453,72 +444,116 @@ static void sortAngles(SkOpContourHead* contourList) {
|
||||
} while ((contour = contour->next()));
|
||||
}
|
||||
|
||||
bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence,
|
||||
SkChunkAlloc* allocator) {
|
||||
bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
|
||||
SkOpGlobalState* globalState = contourList->globalState();
|
||||
// combine t values when multiple intersections occur on some segments but not others
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "start");
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kIntersecting);
|
||||
#endif
|
||||
|
||||
// match up points within the coincident runs
|
||||
if (!coincidence->addExpanded()) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded");
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kWalking);
|
||||
#endif
|
||||
// combine t values when multiple intersections occur on some segments but not others
|
||||
if (!moveMultiples(contourList)) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples");
|
||||
findCollapsed(contourList);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "findCollapsed");
|
||||
// move t values and points together to eliminate small/tiny gaps
|
||||
moveNearby(contourList);
|
||||
(void) moveNearby(contourList);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
|
||||
align(contourList); // give all span members common values
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "align");
|
||||
if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned");
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kIntersecting);
|
||||
#endif
|
||||
// look for intersections on line segments formed by moving end points
|
||||
addAlignIntersections(contourList, allocator);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addAlignIntersections");
|
||||
if (coincidence->addMissing(allocator)) {
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
|
||||
moveNearby(contourList);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
|
||||
align(contourList); // give all span members common values
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "align2");
|
||||
if (!coincidence->fixAligned()) { // aligning may have marked a coincidence pt-t deleted
|
||||
// add coincidence formed by pairing on curve points and endpoints
|
||||
coincidence->correctEnds();
|
||||
if (!coincidence->addEndMovedSpans()) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addEndMovedSpans");
|
||||
|
||||
const int SAFETY_COUNT = 100; // FIXME: tune
|
||||
int safetyHatch = SAFETY_COUNT;
|
||||
// look for coincidence present in A-B and A-C but missing in B-C
|
||||
while (coincidence->addMissing()) {
|
||||
if (!--safetyHatch) {
|
||||
SkASSERT(0); // FIXME: take this out after verifying std tests don't trigger
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "fixAligned2");
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing");
|
||||
moveNearby(contourList);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby");
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
|
||||
// FIXME: only call this if addMissing modified something when returning false
|
||||
moveNearby(contourList);
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveNearby2");
|
||||
// check to see if, loosely, coincident ranges may be expanded
|
||||
if (coincidence->expand()) {
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "expand1");
|
||||
coincidence->addMissing();
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addMissing2");
|
||||
if (!coincidence->addExpanded()) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
|
||||
if (!moveMultiples(contourList)) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "moveMultiples2");
|
||||
moveNearby(contourList);
|
||||
}
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kWalking);
|
||||
#endif
|
||||
// check to see if, loosely, coincident ranges may be expanded
|
||||
if (coincidence->expand()) {
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "expand1");
|
||||
if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "expand2");
|
||||
// the expanded ranges may not align -- add the missing spans
|
||||
SkAssertResult(coincidence->addExpanded());
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
|
||||
coincidence->correctEnds();
|
||||
if (!coincidence->mark()) { // mark spans of coincident segments as coincident
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "mark1");
|
||||
// look for coincidence missed earlier
|
||||
if (missingCoincidence(contourList, coincidence, allocator)) {
|
||||
// look for coincidence lines and curves undetected by intersection
|
||||
if (missingCoincidence(contourList)) {
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kIntersecting);
|
||||
#endif
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence1");
|
||||
(void) coincidence->expand();
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "expand3");
|
||||
if (!coincidence->addExpanded(allocator PATH_OPS_DEBUG_VALIDATE_PARAMS(globalState))) {
|
||||
if (!coincidence->addExpanded()) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded2");
|
||||
coincidence->mark();
|
||||
#if DEBUG_VALIDATE
|
||||
globalState->setPhase(SkOpGlobalState::kWalking);
|
||||
#endif
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "addExpanded3");
|
||||
if (!coincidence->mark()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
|
||||
(void) coincidence->expand();
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence2");
|
||||
SkOpCoincidence overlaps;
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "missingCoincidence3");
|
||||
|
||||
(void) coincidence->expand();
|
||||
|
||||
#if 0 // under development
|
||||
// coincident runs may cross two or more spans, but the opposite spans may be out of order
|
||||
if (!coincidence->reorder()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "coincidence.reorder");
|
||||
SkOpCoincidence overlaps(globalState);
|
||||
do {
|
||||
SkOpCoincidence* pairs = overlaps.isEmpty() ? coincidence : &overlaps;
|
||||
if (!pairs->apply()) { // adjust the winding value to account for coincident edges
|
||||
@ -527,22 +562,25 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->apply");
|
||||
// For each coincident pair that overlaps another, when the receivers (the 1st of the pair)
|
||||
// are different, construct a new pair to resolve their mutual span
|
||||
if (!pairs->findOverlaps(&overlaps, allocator)) {
|
||||
if (!pairs->findOverlaps(&overlaps)) {
|
||||
return false;
|
||||
}
|
||||
DEBUG_COINCIDENCE_HEALTH(contourList, "pairs->findOverlaps");
|
||||
} while (!overlaps.isEmpty());
|
||||
calcAngles(contourList, allocator);
|
||||
calcAngles(contourList);
|
||||
sortAngles(contourList);
|
||||
if (globalState->angleCoincidence()) {
|
||||
(void) missingCoincidence(contourList, coincidence, allocator);
|
||||
(void) missingCoincidence(contourList);
|
||||
if (!coincidence->apply()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
#if DEBUG_COINCIDENCE_VERBOSE
|
||||
coincidence->debugShowCoincidence();
|
||||
DebugShowActiveSpans(contourList);
|
||||
#endif
|
||||
#if DEBUG_COINCIDENCE
|
||||
coincidence->debugValidate();
|
||||
#endif
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
return true;
|
||||
}
|
||||
|
@ -24,12 +24,11 @@ SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
|
||||
SkOpSpanBase** endPtr);
|
||||
bool FixWinding(SkPath* path);
|
||||
bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
|
||||
bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
|
||||
bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* );
|
||||
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
SkDEBUGPARAMS(bool skipAssert)
|
||||
SkDEBUGPARAMS(const char* testName));
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
void DebugShowActiveSpans(SkOpContourHead* );
|
||||
#endif
|
||||
SkScalar ScaleFactor(const SkPath& path);
|
||||
void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled);
|
||||
|
||||
#endif
|
||||
|
@ -277,7 +277,7 @@ bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
|
||||
for (int index = 0; index < roots; ++index) {
|
||||
if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
|
||||
*t = maxCurvature[index];
|
||||
return true;
|
||||
return *t > 0 && *t < 1;
|
||||
}
|
||||
}
|
||||
} else if (infTCount == 1) {
|
||||
|
@ -8,6 +8,57 @@
|
||||
#include "SkPathOpsRect.h"
|
||||
#include "SkPathOpsCurve.h"
|
||||
|
||||
// this cheats and assumes that the perpendicular to the point is the closest ray to the curve
|
||||
// this case (where the line and the curve are nearly coincident) may be the only case that counts
|
||||
double SkDCurve::nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const {
|
||||
int count = SkPathOpsVerbToPoints(verb);
|
||||
double minX = fCubic.fPts[0].fX;
|
||||
double maxX = minX;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
minX = SkTMin(minX, fCubic.fPts[index].fX);
|
||||
maxX = SkTMax(maxX, fCubic.fPts[index].fX);
|
||||
}
|
||||
if (!AlmostBetweenUlps(minX, xy.fX, maxX)) {
|
||||
return -1;
|
||||
}
|
||||
double minY = fCubic.fPts[0].fY;
|
||||
double maxY = minY;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
minY = SkTMin(minY, fCubic.fPts[index].fY);
|
||||
maxY = SkTMax(maxY, fCubic.fPts[index].fY);
|
||||
}
|
||||
if (!AlmostBetweenUlps(minY, xy.fY, maxY)) {
|
||||
return -1;
|
||||
}
|
||||
SkIntersections i;
|
||||
SkDLine perp = {{ xy, { xy.fX + opp.fY - xy.fY, xy.fY + xy.fX - opp.fX }}};
|
||||
(*CurveDIntersectRay[verb])(*this, perp, &i);
|
||||
int minIndex = -1;
|
||||
double minDist = FLT_MAX;
|
||||
for (int index = 0; index < i.used(); ++index) {
|
||||
double dist = xy.distance(i.pt(index));
|
||||
if (minDist > dist) {
|
||||
minDist = dist;
|
||||
minIndex = index;
|
||||
}
|
||||
}
|
||||
if (minIndex < 0) {
|
||||
return -1;
|
||||
}
|
||||
double largest = SkTMax(SkTMax(maxX, maxY), -SkTMin(minX, minY));
|
||||
if (!AlmostEqualUlps_Pin(largest, largest + minDist)) { // is distance within ULPS tolerance?
|
||||
return -1;
|
||||
}
|
||||
return SkPinT(i[0][minIndex]);
|
||||
}
|
||||
|
||||
void SkDCurve::offset(SkPath::Verb verb, const SkDVector& off) {
|
||||
int count = SkPathOpsVerbToPoints(verb);
|
||||
for (int index = 0; index < count; ++index) {
|
||||
fCubic.fPts[index] += off;
|
||||
}
|
||||
}
|
||||
|
||||
void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
|
||||
double tStart, double tEnd, SkPathOpsBounds* bounds) {
|
||||
SkDConic dCurve;
|
||||
|
@ -67,8 +67,11 @@ struct SkDCurve {
|
||||
SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight,
|
||||
double s, double e, double* topT);
|
||||
SkDPoint cubicTop(const SkPoint curve[4], SkScalar , double s, double e, double* topT);
|
||||
void dump() const;
|
||||
void dumpID(int ) const;
|
||||
SkDPoint lineTop(const SkPoint[2], SkScalar , double , double , double* topT);
|
||||
double nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const;
|
||||
void offset(SkPath::Verb verb, const SkDVector& );
|
||||
SkDPoint quadTop(const SkPoint curve[3], SkScalar , double s, double e, double* topT);
|
||||
|
||||
void setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
|
||||
@ -115,6 +118,30 @@ static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], SkScalar , double )
|
||||
dcubic_xy_at_t
|
||||
};
|
||||
|
||||
static SkDPoint ddline_xy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fLine.ptAtT(t);
|
||||
}
|
||||
|
||||
static SkDPoint ddquad_xy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fQuad.ptAtT(t);
|
||||
}
|
||||
|
||||
static SkDPoint ddconic_xy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fConic.ptAtT(t);
|
||||
}
|
||||
|
||||
static SkDPoint ddcubic_xy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fCubic.ptAtT(t);
|
||||
}
|
||||
|
||||
static SkDPoint (* const CurveDDPointAtT[])(const SkDCurve& , double ) = {
|
||||
nullptr,
|
||||
ddline_xy_at_t,
|
||||
ddquad_xy_at_t,
|
||||
ddconic_xy_at_t,
|
||||
ddcubic_xy_at_t
|
||||
};
|
||||
|
||||
static SkPoint fline_xy_at_t(const SkPoint a[2], SkScalar weight, double t) {
|
||||
return dline_xy_at_t(a, weight, t).asSkPoint();
|
||||
}
|
||||
@ -171,6 +198,30 @@ static SkDVector (* const CurveDSlopeAtT[])(const SkPoint[], SkScalar , double )
|
||||
dcubic_dxdy_at_t
|
||||
};
|
||||
|
||||
static SkDVector ddline_dxdy_at_t(const SkDCurve& c, double ) {
|
||||
return c.fLine.fPts[1] - c.fLine.fPts[0];
|
||||
}
|
||||
|
||||
static SkDVector ddquad_dxdy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fQuad.dxdyAtT(t);
|
||||
}
|
||||
|
||||
static SkDVector ddconic_dxdy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fConic.dxdyAtT(t);
|
||||
}
|
||||
|
||||
static SkDVector ddcubic_dxdy_at_t(const SkDCurve& c, double t) {
|
||||
return c.fCubic.dxdyAtT(t);
|
||||
}
|
||||
|
||||
static SkDVector (* const CurveDDSlopeAtT[])(const SkDCurve& , double ) = {
|
||||
nullptr,
|
||||
ddline_dxdy_at_t,
|
||||
ddquad_dxdy_at_t,
|
||||
ddconic_dxdy_at_t,
|
||||
ddcubic_dxdy_at_t
|
||||
};
|
||||
|
||||
static SkVector fline_dxdy_at_t(const SkPoint a[2], SkScalar , double ) {
|
||||
return a[1] - a[0];
|
||||
}
|
||||
@ -269,6 +320,30 @@ static void (* const CurveIntersectRay[])(const SkPoint[] , SkScalar , const SkD
|
||||
cubic_intersect_ray
|
||||
};
|
||||
|
||||
static void dline_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
|
||||
i->intersectRay(c.fLine, ray);
|
||||
}
|
||||
|
||||
static void dquad_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
|
||||
i->intersectRay(c.fQuad, ray);
|
||||
}
|
||||
|
||||
static void dconic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
|
||||
i->intersectRay(c.fConic, ray);
|
||||
}
|
||||
|
||||
static void dcubic_intersect_ray(const SkDCurve& c, const SkDLine& ray, SkIntersections* i) {
|
||||
i->intersectRay(c.fCubic, ray);
|
||||
}
|
||||
|
||||
static void (* const CurveDIntersectRay[])(const SkDCurve& , const SkDLine& , SkIntersections* ) = {
|
||||
nullptr,
|
||||
dline_intersect_ray,
|
||||
dquad_intersect_ray,
|
||||
dconic_intersect_ray,
|
||||
dcubic_intersect_ray
|
||||
};
|
||||
|
||||
static int line_intercept_h(const SkPoint a[2], SkScalar , SkScalar y, double* roots) {
|
||||
SkDLine line;
|
||||
roots[0] = SkIntersections::HorizontalIntercept(line.set(a), y);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,8 @@
|
||||
if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
|
||||
else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
|
||||
|
||||
#define DEBUG_UNDER_DEVELOPMENT 01
|
||||
|
||||
#if FORCE_RELEASE
|
||||
|
||||
#define DEBUG_ACTIVE_OP 0
|
||||
@ -47,6 +49,7 @@
|
||||
#define DEBUG_ANGLE 0
|
||||
#define DEBUG_ASSEMBLE 0
|
||||
#define DEBUG_COINCIDENCE 0
|
||||
#define DEBUG_COINCIDENCE_VERBOSE 0
|
||||
#define DEBUG_CUBIC_BINARY_SEARCH 0
|
||||
#define DEBUG_CUBIC_SPLIT 0
|
||||
#define DEBUG_DUMP_SEGMENTS 0
|
||||
@ -74,12 +77,13 @@
|
||||
#define DEBUG_ALIGNMENT 0
|
||||
#define DEBUG_ANGLE 1
|
||||
#define DEBUG_ASSEMBLE 1
|
||||
#define DEBUG_COINCIDENCE 0
|
||||
#define DEBUG_COINCIDENCE 01
|
||||
#define DEBUG_COINCIDENCE_VERBOSE 01
|
||||
#define DEBUG_CUBIC_BINARY_SEARCH 0
|
||||
#define DEBUG_CUBIC_SPLIT 1
|
||||
#define DEBUG_DUMP_SEGMENTS 1
|
||||
#define DEBUG_FLOW 1
|
||||
#define DEBUG_LIMIT_WIND_SUM 5
|
||||
#define DEBUG_LIMIT_WIND_SUM 15
|
||||
#define DEBUG_MARK_DONE 1
|
||||
#define DEBUG_PATH_CONSTRUCTION 1
|
||||
#define DEBUG_PERP 1
|
||||
@ -186,6 +190,7 @@ public:
|
||||
static void BumpTestName(char* );
|
||||
#endif
|
||||
static const char* OpStr(SkPathOp );
|
||||
static void ShowActiveSpans(SkOpContourHead* contourList);
|
||||
static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
|
||||
static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
|
||||
|
||||
@ -193,37 +198,37 @@ public:
|
||||
|
||||
static void CheckHealth(class SkOpContourHead* contourList, const char* id);
|
||||
|
||||
static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
|
||||
static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
|
||||
static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
|
||||
static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
|
||||
static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
|
||||
static const class SkOpAngle* DebugAngleAngle(const class SkOpAngle*, int id);
|
||||
static class SkOpContour* DebugAngleContour(class SkOpAngle*, int id);
|
||||
static const class SkOpPtT* DebugAnglePtT(const class SkOpAngle*, int id);
|
||||
static const class SkOpSegment* DebugAngleSegment(const class SkOpAngle*, int id);
|
||||
static const class SkOpSpanBase* DebugAngleSpan(const class SkOpAngle*, int id);
|
||||
|
||||
static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
|
||||
static const class SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
|
||||
static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
|
||||
static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
|
||||
static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
|
||||
static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
|
||||
|
||||
static const struct SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
|
||||
static const class SkOpAngle* DebugCoincidenceAngle(class SkOpCoincidence*, int id);
|
||||
static class SkOpContour* DebugCoincidenceContour(class SkOpCoincidence*, int id);
|
||||
static const class SkOpPtT* DebugCoincidencePtT(class SkOpCoincidence*, int id);
|
||||
static const class SkOpSegment* DebugCoincidenceSegment(class SkOpCoincidence*, int id);
|
||||
static const class SkOpSpanBase* DebugCoincidenceSpan(class SkOpCoincidence*, int id);
|
||||
|
||||
static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
|
||||
static const class SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
|
||||
static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
|
||||
static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
|
||||
static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
|
||||
static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
|
||||
|
||||
static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
|
||||
static const class SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
|
||||
static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
|
||||
static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
|
||||
static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
|
||||
static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
|
||||
|
||||
static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
|
||||
static const class SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
|
||||
static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
|
||||
static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
|
||||
static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
|
||||
|
@ -88,7 +88,7 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
|
||||
}
|
||||
|
||||
static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
|
||||
const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
|
||||
const int xorMask, const int xorOpMask, SkPathWriter* simple) {
|
||||
bool unsortable = false;
|
||||
do {
|
||||
SkOpSpan* span = FindSortableTop(contourList);
|
||||
@ -117,11 +117,9 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
|
||||
if (!current->addCurveTo(start, end, simple)) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
if (!simple->isClosed()) {
|
||||
DebugShowActiveSpans(contourList);
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -163,9 +161,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
|
||||
}
|
||||
}
|
||||
current = findChaseOp(chase, &start, &end);
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
DebugShowActiveSpans(contourList);
|
||||
#endif
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
@ -253,40 +249,42 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
|
||||
SkOpContour contour;
|
||||
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
|
||||
SkOpCoincidence coincidence;
|
||||
SkOpGlobalState globalState(&coincidence, contourList
|
||||
SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
|
||||
SkOpGlobalState globalState(contourList, &allocator
|
||||
SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
|
||||
SkOpCoincidence coincidence(&globalState);
|
||||
#if DEBUGGING_PATHOPS_FROM_HOST
|
||||
dump_op(one, two, op);
|
||||
#endif
|
||||
#if 0 && DEBUG_SHOW_TEST_NAME
|
||||
char* debugName = DEBUG_FILENAME_STRING;
|
||||
if (debugName && debugName[0]) {
|
||||
SkPathOpsDebug::BumpTestName(debugName);
|
||||
SkPathOpsDebug::ShowPath(one, two, op, debugName);
|
||||
}
|
||||
#endif
|
||||
op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
|
||||
SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
|
||||
? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
|
||||
const SkPath* minuend = &one;
|
||||
const SkPath* subtrahend = &two;
|
||||
SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
|
||||
SkPath scaledOne, scaledTwo;
|
||||
const SkPath* minuend, * subtrahend;
|
||||
if (scaleFactor > SK_Scalar1) {
|
||||
ScalePath(one, 1.f / scaleFactor, &scaledOne);
|
||||
minuend = &scaledOne;
|
||||
ScalePath(two, 1.f / scaleFactor, &scaledTwo);
|
||||
subtrahend = &scaledTwo;
|
||||
} else {
|
||||
minuend = &one;
|
||||
subtrahend = &two;
|
||||
}
|
||||
if (op == kReverseDifference_SkPathOp) {
|
||||
minuend = &two;
|
||||
subtrahend = &one;
|
||||
SkTSwap(minuend, subtrahend);
|
||||
op = kDifference_SkPathOp;
|
||||
}
|
||||
#if DEBUG_SORT
|
||||
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
|
||||
#endif
|
||||
// turn path into list of segments
|
||||
SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
|
||||
SkOpEdgeBuilder builder(*minuend, &contour, &globalState);
|
||||
if (builder.unparseable()) {
|
||||
return false;
|
||||
}
|
||||
const int xorMask = builder.xorMask();
|
||||
builder.addOperand(*subtrahend);
|
||||
if (!builder.finish(&allocator)) {
|
||||
if (!builder.finish()) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_DUMP_SEGMENTS
|
||||
@ -304,14 +302,14 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
SkOpContour* current = contourList;
|
||||
do {
|
||||
SkOpContour* next = current;
|
||||
while (AddIntersectTs(current, next, &coincidence, &allocator)
|
||||
while (AddIntersectTs(current, next, &coincidence)
|
||||
&& (next = next->next()))
|
||||
;
|
||||
} while ((current = current->next()));
|
||||
#if DEBUG_VALIDATE
|
||||
globalState.setPhase(SkOpGlobalState::kWalking);
|
||||
#endif
|
||||
if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
|
||||
if (!HandleCoincidence(contourList, &coincidence)) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_ALIGNMENT
|
||||
@ -321,7 +319,7 @@ 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, &allocator);
|
||||
bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
|
||||
{ // if some edges could not be resolved, assemble remaining fragments
|
||||
SkPath temp;
|
||||
temp.setFillType(fillType);
|
||||
@ -339,6 +337,9 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
debugWorstState.debugDoYourWorst(&globalState);
|
||||
}
|
||||
#endif
|
||||
if (scaleFactor > 1) {
|
||||
ScalePath(*result, scaleFactor, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -458,6 +459,6 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
@ -58,12 +58,20 @@ struct SkDVector {
|
||||
}
|
||||
|
||||
// similar to cross, this bastardization considers nearly coincident to be zero
|
||||
// uses ulps epsilon == 16
|
||||
double crossCheck(const SkDVector& a) const {
|
||||
double xy = fX * a.fY;
|
||||
double yx = fY * a.fX;
|
||||
return AlmostEqualUlps(xy, yx) ? 0 : xy - yx;
|
||||
}
|
||||
|
||||
// allow tinier numbers
|
||||
double crossNoNormalCheck(const SkDVector& a) const {
|
||||
double xy = fX * a.fY;
|
||||
double yx = fY * a.fX;
|
||||
return AlmostEqualUlpsNoNormalCheck(xy, yx) ? 0 : xy - yx;
|
||||
}
|
||||
|
||||
double dot(const SkDVector& a) const {
|
||||
return fX * a.fX + fY * a.fY;
|
||||
}
|
||||
@ -75,6 +83,12 @@ struct SkDVector {
|
||||
double lengthSquared() const {
|
||||
return fX * fX + fY * fY;
|
||||
}
|
||||
|
||||
void normalize() {
|
||||
double inverseLength = 1 / this->length();
|
||||
fX *= inverseLength;
|
||||
fY *= inverseLength;
|
||||
}
|
||||
};
|
||||
|
||||
struct SkDPoint {
|
||||
@ -164,7 +178,7 @@ struct SkDPoint {
|
||||
float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
|
||||
float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
|
||||
largest = SkTMax(largest, -tiniest);
|
||||
return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
|
||||
return AlmostDequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
|
||||
}
|
||||
|
||||
// only used by testing
|
||||
|
@ -191,6 +191,12 @@ SkDPoint SkDQuad::ptAtT(double t) const {
|
||||
}
|
||||
|
||||
static double interp_quad_coords(const double* src, double t) {
|
||||
if (0 == t) {
|
||||
return src[0];
|
||||
}
|
||||
if (1 == t) {
|
||||
return src[4];
|
||||
}
|
||||
double ab = SkDInterp(src[0], src[2], t);
|
||||
double bc = SkDInterp(src[2], src[4], t);
|
||||
double abc = SkDInterp(ab, bc, t);
|
||||
@ -228,8 +234,11 @@ Group the known values on one side:
|
||||
B = D*2 - A/2 - C/2
|
||||
*/
|
||||
|
||||
// OPTIMIZE : special case either or both of t1 = 0, t2 = 1
|
||||
// OPTIMIZE? : special case t1 = 1 && t2 = 0
|
||||
SkDQuad SkDQuad::subDivide(double t1, double t2) const {
|
||||
if (0 == t1 && 1 == t2) {
|
||||
return *this;
|
||||
}
|
||||
SkDQuad dst;
|
||||
double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
|
||||
double ay = dst[0].fY = interp_quad_coords(&fPts[0].fY, t1);
|
||||
@ -263,7 +272,7 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou
|
||||
b = i.pt(0);
|
||||
} else {
|
||||
SkASSERT(i.used() <= 2);
|
||||
b = SkDPoint::Mid(b0[1], b1[1]);
|
||||
return SkDPoint::Mid(b0[1], b1[1]);
|
||||
}
|
||||
if (t1 == 0 || t2 == 0) {
|
||||
align(0, &b);
|
||||
|
@ -10,8 +10,7 @@
|
||||
#include "SkPathOpsCommon.h"
|
||||
#include "SkPathWriter.h"
|
||||
|
||||
static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
SkChunkAlloc* allocator, bool* closable) {
|
||||
static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
|
||||
bool unsortable = false;
|
||||
do {
|
||||
SkOpSpan* span = FindSortableTop(contourList);
|
||||
@ -40,11 +39,9 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
if (!current->addCurveTo(start, end, simple)) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
if (!simple->isClosed()) {
|
||||
DebugShowActiveSpans(contourList);
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -86,9 +83,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
}
|
||||
}
|
||||
current = FindChase(&chase, &start, &end);
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
DebugShowActiveSpans(contourList);
|
||||
#endif
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
@ -99,8 +94,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
}
|
||||
|
||||
// returns true if all edges were processed
|
||||
static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
SkChunkAlloc* allocator, bool* closable) {
|
||||
static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
|
||||
SkOpSegment* current;
|
||||
SkOpSpanBase* start;
|
||||
SkOpSpanBase* end;
|
||||
@ -108,11 +102,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
*closable = true;
|
||||
while ((current = FindUndone(contourList, &start, &end))) {
|
||||
do {
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
if (!unsortable && current->done()) {
|
||||
DebugShowActiveSpans(contourList);
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
}
|
||||
#endif
|
||||
SkASSERT(unsortable || !current->done());
|
||||
SkOpSpanBase* nextStart = start;
|
||||
SkOpSpanBase* nextEnd = end;
|
||||
@ -124,11 +116,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
if (!current->addCurveTo(start, end, simple)) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
if (!simple->isClosed()) {
|
||||
DebugShowActiveSpans(contourList);
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -156,16 +146,14 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
|
||||
*closable = false;
|
||||
}
|
||||
simple->close();
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
DebugShowActiveSpans(contourList);
|
||||
#endif
|
||||
SkPathOpsDebug::ShowActiveSpans(contourList);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME : add this as a member of SkPath
|
||||
bool Simplify(const SkPath& path, SkPath* result) {
|
||||
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
|
||||
bool SimplifyDebug(const SkPath& path, SkPath* result
|
||||
SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
|
||||
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
|
||||
SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
|
||||
: SkPath::kEvenOdd_FillType;
|
||||
@ -177,16 +165,26 @@ bool Simplify(const SkPath& path, SkPath* result) {
|
||||
return true;
|
||||
}
|
||||
// turn path into list of segments
|
||||
SkOpCoincidence coincidence;
|
||||
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
|
||||
SkOpContour contour;
|
||||
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
|
||||
SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(false)
|
||||
SkDEBUGPARAMS(nullptr));
|
||||
SkOpGlobalState globalState(contourList, &allocator
|
||||
SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
|
||||
SkOpCoincidence coincidence(&globalState);
|
||||
SkScalar scaleFactor = ScaleFactor(path);
|
||||
SkPath scaledPath;
|
||||
const SkPath* workingPath;
|
||||
if (scaleFactor > SK_Scalar1) {
|
||||
ScalePath(path, 1.f / scaleFactor, &scaledPath);
|
||||
workingPath = &scaledPath;
|
||||
} else {
|
||||
workingPath = &path;
|
||||
}
|
||||
#if DEBUG_SORT
|
||||
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
|
||||
#endif
|
||||
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
|
||||
if (!builder.finish(&allocator)) {
|
||||
SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
|
||||
if (!builder.finish()) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_DUMP_SEGMENTS
|
||||
@ -201,13 +199,13 @@ bool Simplify(const SkPath& path, SkPath* result) {
|
||||
SkOpContour* current = contourList;
|
||||
do {
|
||||
SkOpContour* next = current;
|
||||
while (AddIntersectTs(current, next, &coincidence, &allocator)
|
||||
while (AddIntersectTs(current, next, &coincidence)
|
||||
&& (next = next->next()));
|
||||
} while ((current = current->next()));
|
||||
#if DEBUG_VALIDATE
|
||||
globalState.setPhase(SkOpGlobalState::kWalking);
|
||||
#endif
|
||||
if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
|
||||
if (!HandleCoincidence(contourList, &coincidence)) {
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_DUMP_ALIGNMENT
|
||||
@ -219,8 +217,8 @@ bool Simplify(const SkPath& path, SkPath* result) {
|
||||
SkPathWriter wrapper(*result);
|
||||
bool closable SK_INIT_TO_AVOID_WARNING;
|
||||
if (builder.xorMask() == kWinding_PathOpsMask
|
||||
? !bridgeWinding(contourList, &wrapper, &allocator, &closable)
|
||||
: !bridgeXor(contourList, &wrapper, &allocator, &closable)) {
|
||||
? !bridgeWinding(contourList, &wrapper, &closable)
|
||||
: !bridgeXor(contourList, &wrapper, &closable)) {
|
||||
return false;
|
||||
}
|
||||
if (!closable)
|
||||
@ -232,6 +230,12 @@ bool Simplify(const SkPath& path, SkPath* result) {
|
||||
*result = *assembled.nativePath();
|
||||
result->setFillType(fillType);
|
||||
}
|
||||
if (scaleFactor > 1) {
|
||||
ScalePath(*result, scaleFactor, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Simplify(const SkPath& path, SkPath* result) {
|
||||
return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
|
||||
}
|
||||
|
@ -969,6 +969,9 @@ void SkTSect<TCurve, OppCurve>::coincidentCheck(SkTSect<OppCurve, TCurve>* sect2
|
||||
do {
|
||||
coinStart = this->extractCoincident(sect2, coinStart, last);
|
||||
} while (coinStart && !last->fDeleted);
|
||||
if (!fHead || !sect2->fHead) {
|
||||
break;
|
||||
}
|
||||
} while ((first = next));
|
||||
}
|
||||
|
||||
@ -1208,7 +1211,7 @@ SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::extractCoincident(
|
||||
}
|
||||
this->validate();
|
||||
sect2->validate();
|
||||
return last && !last->fDeleted ? last : nullptr;
|
||||
return last && !last->fDeleted && fHead && sect2->fHead ? last : nullptr;
|
||||
}
|
||||
|
||||
template<typename TCurve, typename OppCurve>
|
||||
|
@ -11,11 +11,20 @@ bool TightBounds(const SkPath& path, SkRect* result) {
|
||||
SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
|
||||
SkOpContour contour;
|
||||
SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
|
||||
SkOpGlobalState globalState(nullptr, contourList SkDEBUGPARAMS(false)
|
||||
SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false)
|
||||
SkDEBUGPARAMS(nullptr));
|
||||
// turn path into list of segments
|
||||
SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
|
||||
if (!builder.finish(&allocator)) {
|
||||
SkScalar scaleFactor = ScaleFactor(path);
|
||||
SkPath scaledPath;
|
||||
const SkPath* workingPath;
|
||||
if (scaleFactor > SK_Scalar1) {
|
||||
ScalePath(path, 1.f / scaleFactor, &scaledPath);
|
||||
workingPath = &scaledPath;
|
||||
} else {
|
||||
workingPath = &path;
|
||||
}
|
||||
SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
|
||||
if (!builder.finish()) {
|
||||
return false;
|
||||
}
|
||||
if (!SortContourList(&contourList, false, false)) {
|
||||
|
@ -25,6 +25,13 @@ static bool equal_ulps(float a, float b, int epsilon, int depsilon) {
|
||||
return aBits < bBits + epsilon && bBits < aBits + epsilon;
|
||||
}
|
||||
|
||||
static bool equal_ulps_no_normal_check(float a, float b, int epsilon, int depsilon) {
|
||||
int aBits = SkFloatAs2sCompliment(a);
|
||||
int bBits = SkFloatAs2sCompliment(b);
|
||||
// Find the difference in ULPs.
|
||||
return aBits < bBits + epsilon && bBits < aBits + epsilon;
|
||||
}
|
||||
|
||||
static bool equal_ulps_pin(float a, float b, int epsilon, int depsilon) {
|
||||
if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
|
||||
return false;
|
||||
@ -120,6 +127,11 @@ bool AlmostEqualUlps(float a, float b) {
|
||||
return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon);
|
||||
}
|
||||
|
||||
bool AlmostEqualUlpsNoNormalCheck(float a, float b) {
|
||||
const int UlpsEpsilon = 16;
|
||||
return equal_ulps_no_normal_check(a, b, UlpsEpsilon, UlpsEpsilon);
|
||||
}
|
||||
|
||||
bool AlmostEqualUlps_Pin(float a, float b) {
|
||||
const int UlpsEpsilon = 16;
|
||||
return equal_ulps_pin(a, b, UlpsEpsilon, UlpsEpsilon);
|
||||
@ -212,10 +224,12 @@ double SkDCubeRoot(double x) {
|
||||
return result;
|
||||
}
|
||||
|
||||
SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
|
||||
SkOpGlobalState::SkOpGlobalState(SkOpContourHead* head,
|
||||
SkChunkAlloc* allocator
|
||||
SkDEBUGPARAMS(bool debugSkipAssert)
|
||||
SkDEBUGPARAMS(const char* testName))
|
||||
: fCoincidence(coincidence)
|
||||
: fAllocator(allocator)
|
||||
, fCoincidence(nullptr)
|
||||
, fContourHead(head)
|
||||
, fNested(0)
|
||||
, fWindingFailed(false)
|
||||
@ -227,13 +241,9 @@ SkOpGlobalState::SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead*
|
||||
SkDEBUGPARAMS(fContourID(0))
|
||||
SkDEBUGPARAMS(fPtTID(0))
|
||||
SkDEBUGPARAMS(fSegmentID(0))
|
||||
SkDEBUGPARAMS(fSpanID(0))
|
||||
SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
|
||||
if (coincidence) {
|
||||
coincidence->debugSetGlobalState(this);
|
||||
}
|
||||
SkDEBUGPARAMS(fSpanID(0))
|
||||
SkDEBUGPARAMS(fDebugSkipAssert(debugSkipAssert)) {
|
||||
#if DEBUG_T_SECT_LOOP_COUNT
|
||||
debugResetLoopCounts();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ enum SkPathOpsMask {
|
||||
kEvenOdd_PathOpsMask = 1
|
||||
};
|
||||
|
||||
class SkChunkAlloc;
|
||||
class SkOpCoincidence;
|
||||
class SkOpContour;
|
||||
class SkOpContourHead;
|
||||
@ -30,8 +31,8 @@ class SkIntersectionHelper;
|
||||
|
||||
class SkOpGlobalState {
|
||||
public:
|
||||
SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head
|
||||
SkDEBUGPARAMS(bool debugSkipAssert)
|
||||
SkOpGlobalState(SkOpContourHead* head,
|
||||
SkChunkAlloc* allocator SkDEBUGPARAMS(bool debugSkipAssert)
|
||||
SkDEBUGPARAMS(const char* testName));
|
||||
|
||||
enum Phase {
|
||||
@ -44,6 +45,10 @@ public:
|
||||
kMaxWindingTries = 10
|
||||
};
|
||||
|
||||
SkChunkAlloc* allocator() {
|
||||
return fAllocator;
|
||||
}
|
||||
|
||||
bool angleCoincidence() const {
|
||||
return fAngleCoincidence;
|
||||
}
|
||||
@ -65,7 +70,8 @@ public:
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
const struct SkOpAngle* debugAngle(int id) const;
|
||||
const class SkOpAngle* debugAngle(int id) const;
|
||||
const SkOpCoincidence* debugCoincidence() const;
|
||||
SkOpContour* debugContour(int id);
|
||||
const class SkOpPtT* debugPtT(int id) const;
|
||||
bool debugRunFail() const;
|
||||
@ -83,6 +89,11 @@ public:
|
||||
void debugResetLoopCounts();
|
||||
#endif
|
||||
|
||||
#if DEBUG_COINCIDENCE
|
||||
void debugSetCheckHealth(bool check) { fDebugCheckHealth = check; }
|
||||
bool debugCheckHealth() const { return fDebugCheckHealth; }
|
||||
#endif
|
||||
|
||||
int nested() const {
|
||||
return fNested;
|
||||
}
|
||||
@ -121,6 +132,10 @@ public:
|
||||
fAngleCoincidence = true;
|
||||
}
|
||||
|
||||
void setCoincidence(SkOpCoincidence* coincidence) {
|
||||
fCoincidence = coincidence;
|
||||
}
|
||||
|
||||
void setContourHead(SkOpContourHead* contourHead) {
|
||||
fContourHead = contourHead;
|
||||
}
|
||||
@ -140,6 +155,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
SkChunkAlloc* fAllocator;
|
||||
SkOpCoincidence* fCoincidence;
|
||||
SkOpContourHead* fContourHead;
|
||||
int fNested;
|
||||
@ -162,6 +178,9 @@ private:
|
||||
SkPoint fDebugWorstPts[24];
|
||||
float fDebugWorstWeight[6];
|
||||
#endif
|
||||
#if DEBUG_COINCIDENCE
|
||||
bool fDebugCheckHealth;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
|
||||
@ -170,6 +189,11 @@ inline bool AlmostEqualUlps(double a, double b) {
|
||||
return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
|
||||
}
|
||||
|
||||
bool AlmostEqualUlpsNoNormalCheck(float a, float b);
|
||||
inline bool AlmostEqualUlpsNoNormalCheck(double a, double b) {
|
||||
return AlmostEqualUlpsNoNormalCheck(SkDoubleToScalar(a), SkDoubleToScalar(b));
|
||||
}
|
||||
|
||||
bool AlmostEqualUlps_Pin(float a, float b);
|
||||
inline bool AlmostEqualUlps_Pin(double a, double b) {
|
||||
return AlmostEqualUlps_Pin(SkDoubleToScalar(a), SkDoubleToScalar(b));
|
||||
@ -246,6 +270,8 @@ const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
|
||||
const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
|
||||
const double BUMP_EPSILON = FLT_EPSILON * 4096;
|
||||
|
||||
const SkScalar INVERSE_NUMBER_RANGE = FLT_EPSILON_ORDERABLE_ERR;
|
||||
|
||||
inline bool zero_or_one(double x) {
|
||||
return x == 0 || x == 1;
|
||||
}
|
||||
@ -298,7 +324,6 @@ inline bool approximately_zero_inverse(double x) {
|
||||
return fabs(x) > FLT_EPSILON_INVERSE;
|
||||
}
|
||||
|
||||
// OPTIMIZATION: if called multiple times with the same denom, we want to pass 1/y instead
|
||||
inline bool approximately_zero_when_compared_to(double x, double y) {
|
||||
return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
|
||||
}
|
||||
@ -307,6 +332,10 @@ inline bool precisely_zero_when_compared_to(double x, double y) {
|
||||
return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
|
||||
}
|
||||
|
||||
inline bool roughly_zero_when_compared_to(double x, double y) {
|
||||
return x == 0 || fabs(x) < fabs(y * ROUGH_EPSILON);
|
||||
}
|
||||
|
||||
// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
|
||||
// AlmostEqualUlps instead.
|
||||
inline bool approximately_equal(double x, double y) {
|
||||
|
@ -243,6 +243,10 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
|
||||
}
|
||||
SkOpRayHit* hitHead = &hitBase;
|
||||
dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
|
||||
if (hitBase.fSpan && hitBase.fSpan->segment()->verb() > SkPath::kLine_Verb
|
||||
&& !pt_yx(hitBase.fSlope.asSkVector(), dir)) {
|
||||
return false;
|
||||
}
|
||||
SkOpContour* contour = contourHead;
|
||||
do {
|
||||
contour->rayCheck(hitBase, dir, &hitHead, &allocator);
|
||||
@ -378,15 +382,20 @@ SkOpSpan* SkOpSegment::findSortableTop(SkOpContour* contourHead) {
|
||||
|
||||
SkOpSpan* SkOpContour::findSortableTop(SkOpContour* contourHead) {
|
||||
SkOpSegment* testSegment = &fHead;
|
||||
bool allDone = true;
|
||||
do {
|
||||
if (testSegment->done()) {
|
||||
continue;
|
||||
}
|
||||
allDone = false;
|
||||
SkOpSpan* result = testSegment->findSortableTop(contourHead);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} while ((testSegment = testSegment->next()));
|
||||
if (allDone) {
|
||||
fDone = true;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -78,10 +78,12 @@ int SkReduceOrder::reduce(const SkDQuad& quad) {
|
||||
minYSet |= 1 << index;
|
||||
}
|
||||
}
|
||||
if ((minXSet & 0x05) == 0x5 && (minYSet & 0x05) == 0x5) { // test for degenerate
|
||||
// this quad starts and ends at the same place, so never contributes
|
||||
// to the fill
|
||||
return coincident_line(quad, fQuad);
|
||||
}
|
||||
if (minXSet == 0x7) { // test for vertical line
|
||||
if (minYSet == 0x7) { // return 1 if all three are coincident
|
||||
return coincident_line(quad, fQuad);
|
||||
}
|
||||
return vertical_line(quad, fQuad);
|
||||
}
|
||||
if (minYSet == 0x7) { // test for horizontal line
|
||||
|
@ -406,12 +406,11 @@ static bool bruteForceCheck(skiatest::Reporter* reporter, const SkDQuad& quad1,
|
||||
return ccw == upperRange.ccw;
|
||||
}
|
||||
|
||||
static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3],
|
||||
SkChunkAlloc* allocator) {
|
||||
static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3]) {
|
||||
shortQuad[0] = quad[0].asSkPoint();
|
||||
shortQuad[1] = quad[1].asSkPoint();
|
||||
shortQuad[2] = quad[2].asSkPoint();
|
||||
contour->addQuad(shortQuad, allocator);
|
||||
contour->addQuad(shortQuad);
|
||||
}
|
||||
|
||||
static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
|
||||
@ -419,14 +418,14 @@ static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, c
|
||||
SkPoint shortQuads[2][3];
|
||||
|
||||
SkOpContourHead contour;
|
||||
SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
SkOpGlobalState state(&contour, allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
contour.init(&state, false, false);
|
||||
makeSegment(&contour, quad1, shortQuads[0], allocator);
|
||||
makeSegment(&contour, quad1, shortQuads[1], allocator);
|
||||
makeSegment(&contour, quad1, shortQuads[0]);
|
||||
makeSegment(&contour, quad1, shortQuads[1]);
|
||||
SkOpSegment* seg1 = contour.first();
|
||||
seg1->debugAddAngle(0, 1, allocator);
|
||||
seg1->debugAddAngle(0, 1);
|
||||
SkOpSegment* seg2 = seg1->next();
|
||||
seg2->debugAddAngle(0, 1, allocator);
|
||||
seg2->debugAddAngle(0, 1);
|
||||
int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
|
||||
*seg2->debugLastAngle());
|
||||
const SkDPoint& origin = quad1[0];
|
||||
|
@ -194,6 +194,10 @@ public:
|
||||
return lh.after(&rh);
|
||||
}
|
||||
|
||||
static int AllOnOneSide(SkOpAngle& lh, SkOpAngle& rh) {
|
||||
return lh.allOnOneSide(&rh);
|
||||
}
|
||||
|
||||
static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
|
||||
return lh.convexHullOverlaps(&rh);
|
||||
}
|
||||
@ -235,7 +239,7 @@ static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
|
||||
DEF_TEST(PathOpsAngleCircle, reporter) {
|
||||
SkChunkAlloc allocator(4096);
|
||||
SkOpContourHead contour;
|
||||
SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
contour.init(&state, false, false);
|
||||
for (int index = 0; index < circleDataSetSize; ++index) {
|
||||
CircleData& data = circleDataSet[index];
|
||||
@ -244,20 +248,20 @@ DEF_TEST(PathOpsAngleCircle, reporter) {
|
||||
}
|
||||
switch (data.fPtCount) {
|
||||
case 2:
|
||||
contour.addLine(data.fShortPts, &allocator);
|
||||
contour.addLine(data.fShortPts);
|
||||
break;
|
||||
case 3:
|
||||
contour.addQuad(data.fShortPts, &allocator);
|
||||
contour.addQuad(data.fShortPts);
|
||||
break;
|
||||
case 4:
|
||||
contour.addCubic(data.fShortPts, &allocator);
|
||||
contour.addCubic(data.fShortPts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SkOpSegment* first = contour.first();
|
||||
first->debugAddAngle(0, 1, &allocator);
|
||||
first->debugAddAngle(0, 1);
|
||||
SkOpSegment* next = first->next();
|
||||
next->debugAddAngle(0, 1, &allocator);
|
||||
next->debugAddAngle(0, 1);
|
||||
PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
|
||||
}
|
||||
|
||||
@ -427,7 +431,7 @@ struct FourPoints {
|
||||
DEF_TEST(PathOpsAngleAfter, reporter) {
|
||||
SkChunkAlloc allocator(4096);
|
||||
SkOpContourHead contour;
|
||||
SkOpGlobalState state(nullptr, &contour SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
contour.init(&state, false, false);
|
||||
for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
|
||||
IntersectData* dataArray = intersectDataSets[index];
|
||||
@ -443,22 +447,22 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
|
||||
}
|
||||
switch (data.fPtCount) {
|
||||
case 2: {
|
||||
contour.addLine(temp, &allocator);
|
||||
contour.addLine(temp);
|
||||
} break;
|
||||
case 3: {
|
||||
contour.addQuad(temp, &allocator);
|
||||
contour.addQuad(temp);
|
||||
} break;
|
||||
case 4: {
|
||||
contour.addCubic(temp, &allocator);
|
||||
contour.addCubic(temp);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
SkOpSegment* seg1 = contour.first();
|
||||
seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd, &allocator);
|
||||
seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd);
|
||||
SkOpSegment* seg2 = seg1->next();
|
||||
seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd, &allocator);
|
||||
seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd);
|
||||
SkOpSegment* seg3 = seg2->next();
|
||||
seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd, &allocator);
|
||||
seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd);
|
||||
SkOpAngle& angle1 = *seg1->debugLastAngle();
|
||||
SkOpAngle& angle2 = *seg2->debugLastAngle();
|
||||
SkOpAngle& angle3 = *seg3->debugLastAngle();
|
||||
@ -472,12 +476,12 @@ DEF_TEST(PathOpsAngleAfter, reporter) {
|
||||
}
|
||||
}
|
||||
|
||||
void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* allocator) {
|
||||
void SkOpSegment::debugAddAngle(double startT, double endT) {
|
||||
SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
|
||||
: this->addT(startT, kNoAlias, allocator);
|
||||
: this->addT(startT, kNoAliasMatch, nullptr);
|
||||
SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
|
||||
: this->addT(endT, kNoAlias, allocator);
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
|
||||
: this->addT(endT, kNoAliasMatch, nullptr);
|
||||
SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(this->globalState()->allocator());
|
||||
SkOpSpanBase* startSpan = &fHead;
|
||||
while (startSpan->ptT() != startPtT) {
|
||||
startSpan = startSpan->upCast()->next();
|
||||
@ -495,3 +499,29 @@ void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* alloca
|
||||
startSpan->setFromAngle(angle);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(PathOpsAngleAllOnOneSide, reporter) {
|
||||
SkChunkAlloc allocator(4096);
|
||||
SkOpContourHead contour;
|
||||
SkOpGlobalState state(&contour, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
|
||||
contour.init(&state, false, false);
|
||||
SkPoint conicPts[3] = {{494.37100219726562f, 224.66200256347656f},
|
||||
{494.37360910682298f, 224.6729026561527f},
|
||||
{494.37600708007813f, 224.68400573730469f}};
|
||||
SkPoint linePts[2] = {{494.371002f, 224.662003f}, {494.375000f, 224.675995f}};
|
||||
for (int i = 10; i >= 0; --i) {
|
||||
SkPoint modLinePts[2] = { linePts[0], linePts[1] };
|
||||
modLinePts[1].fX += i * .1f;
|
||||
contour.addLine(modLinePts);
|
||||
contour.addQuad(conicPts);
|
||||
// contour.addConic(conicPts, 0.999935746f, &allocator);
|
||||
SkOpSegment* first = contour.first();
|
||||
first->debugAddAngle(0, 1);
|
||||
SkOpSegment* next = first->next();
|
||||
next->debugAddAngle(0, 1);
|
||||
/* int result = */
|
||||
PathOpsAngleTester::AllOnOneSide(*first->debugLastAngle(), *next->debugLastAngle());
|
||||
// SkDebugf("i=%d result=%d\n", i , result);
|
||||
// SkDebugf("");
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,14 @@ void SkPathOpsDebug::WindingPrintf(int wind) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static void DumpID(int id) {
|
||||
SkDebugf("} ");
|
||||
if (id >= 0) {
|
||||
SkDebugf("id=%d", id);
|
||||
}
|
||||
SkDebugf("\n");
|
||||
}
|
||||
|
||||
void SkDConic::dump() const {
|
||||
dumpInner();
|
||||
SkDebugf("},\n");
|
||||
@ -57,7 +65,7 @@ void SkDConic::dump() const {
|
||||
|
||||
void SkDConic::dumpID(int id) const {
|
||||
dumpInner();
|
||||
SkDebugf("} id=%d\n", id);
|
||||
DumpID(id);
|
||||
}
|
||||
|
||||
void SkDConic::dumpInner() const {
|
||||
@ -73,7 +81,8 @@ void SkDCubic::dump() const {
|
||||
|
||||
void SkDCubic::dumpID(int id) const {
|
||||
this->dumpInner();
|
||||
SkDebugf("}} id=%d\n", id);
|
||||
SkDebugf("}");
|
||||
DumpID(id);
|
||||
}
|
||||
|
||||
static inline bool double_is_NaN(double x) { return x != x; }
|
||||
@ -97,6 +106,10 @@ void SkDCubic::dumpInner() const {
|
||||
fPts[index].dump();
|
||||
}
|
||||
|
||||
void SkDCurve::dump() const {
|
||||
dumpID(-1);
|
||||
}
|
||||
|
||||
void SkDCurve::dumpID(int id) const {
|
||||
#ifndef SK_RELEASE
|
||||
switch(fVerb) {
|
||||
@ -127,7 +140,8 @@ void SkDLine::dump() const {
|
||||
|
||||
void SkDLine::dumpID(int id) const {
|
||||
this->dumpInner();
|
||||
SkDebugf("}} id=%d\n", id);
|
||||
SkDebugf("}");
|
||||
DumpID(id);
|
||||
}
|
||||
|
||||
void SkDLine::dumpInner() const {
|
||||
@ -168,7 +182,8 @@ void SkDQuad::dump() const {
|
||||
|
||||
void SkDQuad::dumpID(int id) const {
|
||||
dumpInner();
|
||||
SkDebugf("}} id=%d\n", id);
|
||||
SkDebugf("}");
|
||||
DumpID(id);
|
||||
}
|
||||
|
||||
void SkDQuad::dumpInner() const {
|
||||
@ -787,6 +802,10 @@ const SkOpAngle* SkOpAngle::debugAngle(int id) const {
|
||||
return this->segment()->debugAngle(id);
|
||||
}
|
||||
|
||||
const SkOpCoincidence* SkOpAngle::debugCoincidence() const {
|
||||
return this->segment()->debugCoincidence();
|
||||
}
|
||||
|
||||
SkOpContour* SkOpAngle::debugContour(int id) {
|
||||
return this->segment()->debugContour(id);
|
||||
}
|
||||
@ -925,6 +944,10 @@ SkOpContour* SkOpPtT::debugContour(int id) {
|
||||
return this->span()->debugContour(id);
|
||||
}
|
||||
|
||||
const SkOpCoincidence* SkOpPtT::debugCoincidence() const {
|
||||
return this->span()->debugCoincidence();
|
||||
}
|
||||
|
||||
const SkOpPtT* SkOpPtT::debugPtT(int id) const {
|
||||
return this->span()->debugPtT(id);
|
||||
}
|
||||
@ -964,7 +987,8 @@ void SkOpPtT::dumpAll() const {
|
||||
}
|
||||
|
||||
void SkOpPtT::dumpBase() const {
|
||||
SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s", this->fT, this->fPt.fX, this->fPt.fY,
|
||||
SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s%s", this->fT, this->fPt.fX, this->fPt.fY,
|
||||
this->fCoincident ? " coin" : "",
|
||||
this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : "");
|
||||
}
|
||||
|
||||
@ -972,6 +996,10 @@ const SkOpAngle* SkOpSpanBase::debugAngle(int id) const {
|
||||
return this->segment()->debugAngle(id);
|
||||
}
|
||||
|
||||
const SkOpCoincidence* SkOpSpanBase::debugCoincidence() const {
|
||||
return this->segment()->debugCoincidence();
|
||||
}
|
||||
|
||||
SkOpContour* SkOpSpanBase::debugContour(int id) {
|
||||
return this->segment()->debugContour(id);
|
||||
}
|
||||
@ -989,15 +1017,19 @@ const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const {
|
||||
}
|
||||
|
||||
void SkOpSpanBase::dump() const {
|
||||
this->dumpAll();
|
||||
SkDebugf("\n");
|
||||
this->dumpHead();
|
||||
this->fPtT.dump();
|
||||
}
|
||||
|
||||
void SkOpSpanBase::dumpAll() const {
|
||||
void SkOpSpanBase::dumpHead() const {
|
||||
SkDebugf("%.*s", contour()->debugIndent(), " ");
|
||||
SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID());
|
||||
this->dumpBase();
|
||||
SkDebugf("\n");
|
||||
}
|
||||
|
||||
void SkOpSpanBase::dumpAll() const {
|
||||
this->dumpHead();
|
||||
this->fPtT.dumpAll();
|
||||
}
|
||||
|
||||
@ -1008,6 +1040,11 @@ void SkOpSpanBase::dumpBase() const {
|
||||
if (this->fChased) {
|
||||
SkDebugf(" chased");
|
||||
}
|
||||
#ifdef SK_DEBUG
|
||||
if (this->fDeleted) {
|
||||
SkDebugf(" deleted");
|
||||
}
|
||||
#endif
|
||||
if (!this->final()) {
|
||||
this->upCast()->dumpSpan();
|
||||
}
|
||||
@ -1069,6 +1106,11 @@ const SkOpAngle* SkOpSegment::debugAngle(int id) const {
|
||||
return this->contour()->debugAngle(id);
|
||||
}
|
||||
|
||||
|
||||
const SkOpCoincidence* SkOpSegment::debugCoincidence() const {
|
||||
return this->contour()->debugCoincidence();
|
||||
}
|
||||
|
||||
SkOpContour* SkOpSegment::debugContour(int id) {
|
||||
return this->contour()->debugContour(id);
|
||||
}
|
||||
@ -1188,20 +1230,26 @@ void SkOpCoincidence::dump() const {
|
||||
SkCoincidentSpans* span = fHead;
|
||||
while (span) {
|
||||
span->dump();
|
||||
span = span->fNext;
|
||||
span = span->next();
|
||||
}
|
||||
if (!fTop || fHead == fTop) {
|
||||
return;
|
||||
}
|
||||
SkDebugf("top:\n");
|
||||
span = fTop;
|
||||
if (fHead) {
|
||||
span->dump();
|
||||
return;
|
||||
}
|
||||
int count = 0;
|
||||
while (span) {
|
||||
span->dump();
|
||||
span = span->fNext;
|
||||
span = span->next();
|
||||
SkCoincidentSpans* check = fTop;
|
||||
++count;
|
||||
for (int index = 0; index < count; ++index) {
|
||||
if (span == check) {
|
||||
SkDebugf("(loops to #%d)\n", index);
|
||||
return;
|
||||
}
|
||||
check = check->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,15 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
SkDEBUGPARAMS(bool skipAssert)
|
||||
SkDEBUGPARAMS(const char* testName));
|
||||
|
||||
bool SimplifyDebug(const SkPath& one, SkPath* result
|
||||
SkDEBUGPARAMS(bool skipAssert)
|
||||
SkDEBUGPARAMS(const char* testName));
|
||||
|
||||
|
||||
__SK_FORCE_IMAGE_DECODER_LINKING;
|
||||
|
||||
DEFINE_bool2(runFail, f, false, "run tests known to fail.");
|
||||
@ -38,7 +47,7 @@ static const char* opStrs[] = {
|
||||
"kDifference_SkPathOp",
|
||||
"kIntersect_SkPathOp",
|
||||
"kUnion_SkPathOp",
|
||||
"kXor_PathOp",
|
||||
"kXOR_PathOp",
|
||||
"kReverseDifference_SkPathOp",
|
||||
};
|
||||
|
||||
@ -47,6 +56,7 @@ static const char* opSuffixes[] = {
|
||||
"i",
|
||||
"u",
|
||||
"o",
|
||||
"r",
|
||||
};
|
||||
|
||||
#if DEBUG_SHOW_TEST_NAME
|
||||
@ -443,20 +453,43 @@ bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& st
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
enum class ExpectSuccess {
|
||||
kNo,
|
||||
kYes
|
||||
};
|
||||
|
||||
enum class SkipAssert {
|
||||
kNo,
|
||||
kYes
|
||||
};
|
||||
|
||||
enum class ExpectMatch {
|
||||
kNo,
|
||||
kYes
|
||||
};
|
||||
|
||||
static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
|
||||
bool checkFail) {
|
||||
ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) {
|
||||
#if 0 && DEBUG_SHOW_TEST_NAME
|
||||
showPathData(path);
|
||||
#endif
|
||||
SkPath out;
|
||||
if (!Simplify(path, &out)) {
|
||||
SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
if (!SimplifyDebug(path, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
|
||||
SkDEBUGPARAMS(testName))) {
|
||||
if (ExpectSuccess::kYes == expectSuccess) {
|
||||
SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (ExpectSuccess::kNo == expectSuccess) {
|
||||
SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
}
|
||||
}
|
||||
SkBitmap bitmap;
|
||||
int errors = comparePaths(reporter, filename, path, out, bitmap);
|
||||
if (!checkFail) {
|
||||
if (ExpectMatch::kNo == expectMatch) {
|
||||
if (!errors) {
|
||||
SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
@ -470,12 +503,19 @@ static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, con
|
||||
}
|
||||
|
||||
bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
|
||||
return inner_simplify(reporter, path, filename, true);
|
||||
return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo,
|
||||
ExpectMatch::kYes);
|
||||
}
|
||||
|
||||
bool testSimplifyFailSkipAssert(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
|
||||
return inner_simplify(reporter, path, filename, ExpectSuccess::kNo, SkipAssert::kYes,
|
||||
ExpectMatch::kNo);
|
||||
}
|
||||
|
||||
bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
|
||||
bool checkFail) {
|
||||
return inner_simplify(reporter, path, filename, checkFail);
|
||||
return inner_simplify(reporter, path, filename, checkFail ?
|
||||
ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
|
||||
}
|
||||
|
||||
#if DEBUG_SHOW_TEST_NAME
|
||||
@ -487,23 +527,25 @@ static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
|
||||
SkDEBUGPARAMS(bool skipAssert)
|
||||
SkDEBUGPARAMS(const char* testName));
|
||||
|
||||
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName, bool expectSuccess, bool skipAssert) {
|
||||
const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess,
|
||||
SkipAssert skipAssert, ExpectMatch expectMatch) {
|
||||
#if 0 && DEBUG_SHOW_TEST_NAME
|
||||
showName(a, b, shapeOp);
|
||||
#endif
|
||||
SkPath out;
|
||||
if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(skipAssert)
|
||||
if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
|
||||
SkDEBUGPARAMS(testName))) {
|
||||
if (expectSuccess) {
|
||||
SkDebugf("%s did not expect failure\n", __FUNCTION__);
|
||||
if (ExpectSuccess::kYes == expectSuccess) {
|
||||
SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (ExpectSuccess::kNo == expectSuccess) {
|
||||
SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName);
|
||||
REPORTER_ASSERT(reporter, 0);
|
||||
}
|
||||
}
|
||||
if (!reporter->verbose()) {
|
||||
return true;
|
||||
@ -533,37 +575,42 @@ static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
|
||||
scaledOut.addPath(out, scale);
|
||||
scaledOut.setFillType(out.getFillType());
|
||||
int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
|
||||
a, b, shapeOp, scale, expectSuccess);
|
||||
a, b, shapeOp, scale, ExpectMatch::kYes == expectMatch);
|
||||
reporter->bumpTestCount();
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName) {
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, true, false);
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo,
|
||||
ExpectMatch::kYes);
|
||||
}
|
||||
|
||||
bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName, bool checkFail) {
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, checkFail, false);
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ?
|
||||
ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
|
||||
}
|
||||
|
||||
bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName) {
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, false, false);
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kNo, SkipAssert::kNo,
|
||||
ExpectMatch::kNo);
|
||||
}
|
||||
|
||||
bool testPathSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
bool testPathOpSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName) {
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, true, true);
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kYes,
|
||||
ExpectMatch::kYes);
|
||||
}
|
||||
|
||||
bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
bool testPathOpFailSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName) {
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, false, true);
|
||||
return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kNo, SkipAssert::kYes,
|
||||
ExpectMatch::kNo);
|
||||
}
|
||||
|
||||
bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp shapeOp, const char* testName) {
|
||||
#if DEBUG_SHOW_TEST_NAME
|
||||
showName(a, b, shapeOp);
|
||||
|
@ -40,19 +40,23 @@ extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPa
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName, bool checkFail);
|
||||
extern bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp, const char* testName);
|
||||
extern bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testPathFailSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testPathSkipAssertOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
extern bool testPathOpFailSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testPathOpSkipAssert(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
|
||||
const SkPathOp , const char* testName);
|
||||
extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
|
||||
const char* pathStr);
|
||||
extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
|
||||
extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
|
||||
const char* filename, bool checkFail);
|
||||
extern bool testSimplifyFailSkipAssert(skiatest::Reporter* reporter, const SkPath& path,
|
||||
const char* filename);
|
||||
extern bool testSimplifySkipAssert(skiatest::Reporter* reporter, const SkPath& path,
|
||||
const char* filename);
|
||||
|
||||
void initializeTests(skiatest::Reporter* reporter, const char* testName);
|
||||
void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
|
||||
|
@ -454,7 +454,8 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
// DEBUG_UNDER_DEVELOPMENT fuzz763_378a disable expectation check for now
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
|
||||
@ -495,7 +496,7 @@ path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -932,8 +933,8 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
// FIXME: This should not fail; trading adding this failure for fixing security bug
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
// DEBUG_UNDER_DEVELOPMENT fuzz763_4713 disable expectation check for now
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -1529,7 +1530,7 @@ path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -2093,7 +2094,8 @@ path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x4
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
// DEBUG_UNDER_DEVELOPMENT fuzz763_1026368 disable expectation check for now
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -2396,7 +2398,7 @@ path.close();
|
||||
}
|
||||
|
||||
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_1026368;
|
||||
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
|
||||
static struct TestDesc tests[] = {
|
||||
|
@ -472,7 +472,8 @@ path.close();
|
||||
static void issue3651_1a(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path = path1_a();
|
||||
SkPath pathB = path2_a();
|
||||
testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
|
||||
// DEBUG_UNDER_DEVELOPMENT issue3651_1a disable expectation check for now
|
||||
testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static SkPath path3() {
|
||||
@ -1202,7 +1203,8 @@ path.close();
|
||||
static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path = path1();
|
||||
SkPath pathB = path2();
|
||||
testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
|
||||
// DEBUG_UNDER_DEVELOPMENT issue3651_1 disable expectation check for now
|
||||
testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -1656,7 +1658,7 @@ path.close();
|
||||
|
||||
|
||||
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = issue3651_1;
|
||||
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
|
||||
static struct TestDesc tests[] = {
|
||||
|
@ -1196,7 +1196,7 @@ static void rRect1(skiatest::Reporter* reporter, const char* filename) {
|
||||
path.setFillType(SkPath::kInverseEvenOdd_FillType);
|
||||
for (int index = 0; index < 5; ++index) {
|
||||
testPathOp(reporter, path, paths[index], ops[index], filename);
|
||||
Op(path, paths[index], ops[index], &path);
|
||||
REPORTER_ASSERT(reporter, Op(path, paths[index], ops[index], &path));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3477,27 +3477,27 @@ static void issue2753(skiatest::Reporter* reporter, const char* filename) {
|
||||
static void issue2808(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path1, path2;
|
||||
|
||||
path1.moveTo(509.20300293f, 385.601989746f);
|
||||
path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
|
||||
path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
|
||||
path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
|
||||
path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
|
||||
path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
|
||||
path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
|
||||
path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
|
||||
path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
|
||||
path1.close();
|
||||
path1.moveTo(509.20300293f, 385.601989746f);
|
||||
path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
|
||||
path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
|
||||
path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
|
||||
path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
|
||||
path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
|
||||
path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
|
||||
path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
|
||||
path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
|
||||
path1.close();
|
||||
|
||||
path2.moveTo(449.033996582f, 290.87298584f);
|
||||
path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
|
||||
path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
|
||||
path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
|
||||
path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
|
||||
path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
|
||||
path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
|
||||
path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
|
||||
path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
|
||||
path2.close();
|
||||
path2.moveTo(449.033996582f, 290.87298584f);
|
||||
path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
|
||||
path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
|
||||
path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
|
||||
path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
|
||||
path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
|
||||
path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
|
||||
path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
|
||||
path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
|
||||
path2.close();
|
||||
|
||||
testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
|
||||
}
|
||||
@ -4866,7 +4866,7 @@ static void loops46i(skiatest::Reporter* reporter, const char* filename) {
|
||||
}
|
||||
|
||||
/*
|
||||
FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */
|
||||
FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346 0 */
|
||||
static void loops47i(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
@ -5160,7 +5160,7 @@ static void fuzz38(skiatest::Reporter* reporter, const char* filename) {
|
||||
path.lineTo(100.34f, 310.156f);
|
||||
path.lineTo(100.34f, 303.312f);
|
||||
path.close();
|
||||
testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
|
||||
}
|
||||
|
||||
static void crbug_526025(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5211,13 +5211,176 @@ path.lineTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0x43c61f86)); // -236.484f,
|
||||
testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void dean2(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
|
||||
path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
|
||||
path.cubicTo(SkBits2Float(0x414a835a), SkBits2Float(0x3ec07ba6), SkBits2Float(0x413fcc0d), SkBits2Float(0x3e193319), SkBits2Float(0x4134a02b), SkBits2Float(0x00000000)); // 12.6571f, 0.375943f, 11.9873f, 0.149609f, 11.2891f, 0
|
||||
path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
|
||||
path.close();
|
||||
SkPath path1(path);
|
||||
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
|
||||
path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
|
||||
path.lineTo(SkBits2Float(0x417ab74b), SkBits2Float(0x4154a02b)); // 15.6697f, 13.2891f
|
||||
path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
|
||||
path.close();
|
||||
SkPath path2(path);
|
||||
testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void cubics_d(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(0, 1);
|
||||
path.cubicTo(3, 5, 1, 0, 3, 0);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 1);
|
||||
pathB.cubicTo(0, 3, 1, 0, 5, 3);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void cubics_d2(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(0, 1);
|
||||
path.cubicTo(2, 5, 2, 0, 2, 1);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 2);
|
||||
pathB.cubicTo(1, 2, 1, 0, 5, 2);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i1(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(2, 3);
|
||||
path.cubicTo(0, 4, -0.333333343f, 4.66666651f, 3, 5.83333349f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 4);
|
||||
pathB.cubicTo(-0.333333343f, 4.66666651f, 3, 5.83333349f, 2, 3);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i2(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(2, 4);
|
||||
path.cubicTo(0, 5, -0.333333343f, 5.66666651f, 3, 6.83333302f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 5);
|
||||
pathB.cubicTo(-0.333333343f, 5.66666651f, 3, 6.83333302f, 2, 4);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i3(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(2, 5);
|
||||
path.cubicTo(0, 6, -0.333333343f, 6.66666651f, 3, 7.83333302f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 6);
|
||||
pathB.cubicTo(-0.333333343f, 6.66666651f, 3, 7.83333302f, 2, 5);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i4(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(3, 4);
|
||||
path.cubicTo(1, 5, 0.666666627f, 5.66666651f, 4, 6.83333302f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(1, 5);
|
||||
pathB.cubicTo(0.666666627f, 5.66666651f, 4, 6.83333302f, 3, 4);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i5(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(3, 5);
|
||||
path.cubicTo(1, 6, 0.666666627f, 6.66666651f, 4, 7.83333302f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(1, 6);
|
||||
pathB.cubicTo(0.666666627f, 6.66666651f, 4, 7.83333302f, 3, 5);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void loops_i6(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(4, 5);
|
||||
path.cubicTo(2, 6, 1.66666663f, 6.66666651f, 5, 7.83333302f);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(2, 6);
|
||||
pathB.cubicTo(1.66666663f, 6.66666651f, 5, 7.83333302f, 4, 5);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void cubics_d3(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(3, 4);
|
||||
path.cubicTo(0, 6, 6, 1, 4, 2);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(1, 6);
|
||||
pathB.cubicTo(2, 4, 4, 3, 6, 0);
|
||||
pathB.close();
|
||||
// DEBUG_UNDER_DEVELOPMENT cubics_d3 disable expectation check for now
|
||||
testPathOpCheck(reporter, path, pathB, kDifference_SkPathOp, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void cubics_o(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path, pathB;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.moveTo(1, 4);
|
||||
path.cubicTo(2, 6, 5, 0, 5, 3);
|
||||
path.close();
|
||||
pathB.setFillType(SkPath::kWinding_FillType);
|
||||
pathB.moveTo(0, 5);
|
||||
pathB.cubicTo(3, 5, 4, 1, 6, 2);
|
||||
pathB.close();
|
||||
testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*firstTest)(skiatest::Reporter* , const char* filename) = cubics_d3;
|
||||
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
|
||||
#define TEST(name) { name, #name }
|
||||
|
||||
static struct TestDesc tests[] = {
|
||||
TEST(loops_i1),
|
||||
TEST(loops_i2),
|
||||
TEST(loops_i3),
|
||||
TEST(loops_i4),
|
||||
TEST(loops_i5),
|
||||
TEST(loops_i6),
|
||||
TEST(cubics_d3),
|
||||
TEST(cubics_o),
|
||||
TEST(cubics_d2),
|
||||
TEST(cubics_d),
|
||||
TEST(dean2),
|
||||
TEST(fuzzX_392),
|
||||
TEST(crbug_526025),
|
||||
TEST(fuzz38),
|
||||
@ -5594,7 +5757,7 @@ static void fuzz535151(skiatest::Reporter* reporter, const char* filename) {
|
||||
two.lineTo(0, 50);
|
||||
two.lineTo(4.29497e+09f, 50);
|
||||
SkPath dummy;
|
||||
REPORTER_ASSERT(reporter, !Op(one, two, kIntersect_SkPathOp, &dummy));
|
||||
testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5602,7 +5765,7 @@ static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
|
||||
path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
|
||||
SkPath pathB;
|
||||
pathB.addRect(0,0, 300,16);
|
||||
testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
|
||||
testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
|
||||
}
|
||||
|
||||
// m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
|
||||
@ -5622,7 +5785,7 @@ static void fuzz433(skiatest::Reporter* reporter, const char* filename) {
|
||||
path2.lineTo(-170 + 20,11000000000.0f + 20);
|
||||
path2.close();
|
||||
|
||||
testPathOpCheck(reporter, path1, path2, kIntersect_SkPathOp, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path1, path2, kIntersect_SkPathOp, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5645,7 +5808,7 @@ static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
|
||||
path2.lineTo(190, 60);
|
||||
path2.close();
|
||||
|
||||
testPathOpCheck(reporter, path1, path2, kUnion_SkPathOp, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path1, path2, kUnion_SkPathOp, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5691,7 +5854,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpFailCheck(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5737,7 +5900,7 @@ path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5763,7 +5926,7 @@ path.lineTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
|
||||
testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz1(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5785,7 +5948,7 @@ path.close();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
|
||||
SkPath path2(path);
|
||||
testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
testPathOpFail(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
}
|
||||
|
||||
|
||||
@ -5810,7 +5973,7 @@ path.cubicTo(SkBits2Float(0x42266e32), SkBits2Float(0xcf223cc0), SkBits2Float(0x
|
||||
path.lineTo(SkBits2Float(0x40f8fbe0), SkBits2Float(0xcf223cc0)); // 7.78075f, -2.72189e+09f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
|
||||
}
|
||||
|
||||
static void bug597926_0(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5833,7 +5996,7 @@ path.cubicTo(SkBits2Float(0xc51cd471), SkBits2Float(0xc49d54d0), SkBits2Float(0x
|
||||
path.quadTo(SkBits2Float(0xc51bf7eb), SkBits2Float(0xc49cf010), SkBits2Float(0xc51ba866), SkBits2Float(0xc49cb9e6)); // -2495.49f, -1255.5f, -2490.52f, -1253.81f
|
||||
path.cubicTo(SkBits2Float(0xc51bac0d), SkBits2Float(0xc49cc50e), SkBits2Float(0xc51c29eb), SkBits2Float(0xc49cfb01), SkBits2Float(0xc51c5bca), SkBits2Float(0xc49d1fa6)); // -2490.75f, -1254.16f, -2498.62f, -1255.84f, -2501.74f, -1256.99f
|
||||
SkPath path2(path);
|
||||
testPathFailOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
static void fuzz1450_0(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5852,7 +6015,7 @@ path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000)); // 360, 1024
|
||||
path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000)); // 360, 4140
|
||||
path.close();
|
||||
SkPath path2(path);
|
||||
testPathSkipAssertOp(reporter, path1, path2, kUnion_SkPathOp, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, kUnion_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void fuzz1450_1(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5881,7 +6044,7 @@ path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000)); // 2551, 64
|
||||
path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321
|
||||
path.close();
|
||||
SkPath path2(path);
|
||||
testPathFailOp(reporter, path1, path2, kUnion_SkPathOp, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, kUnion_SkPathOp, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5908,7 +6071,7 @@ path.quadTo(SkBits2Float(0xd912102a), SkBits2Float(0x284f9a28), SkBits2Float(0xb
|
||||
path.lineTo(SkBits2Float(0xc809272a), SkBits2Float(0x29b02829)); // -140445, 7.82294e-14f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathFailOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
|
||||
@ -5942,7 +6105,7 @@ path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a)); // 5.7784e-19f
|
||||
path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x21df212a), SkBits2Float(0x033a8a3a)); // 6.14991e+25f, 6.77381e-19f, 9.33503e-15f, 1.51198e-18f, 5.48192e-37f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathFailSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_3(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -5977,7 +6140,7 @@ path.moveTo(SkBits2Float(0x21081f3f), SkBits2Float(0x9fd4e62a)); // 4.61199e-19
|
||||
path.cubicTo(SkBits2Float(0x3a293a2a), SkBits2Float(0x0e3bf0c5), SkBits2Float(0x3b29d42a), SkBits2Float(0x0f217265), SkBits2Float(0x2d5d2921), SkBits2Float(0x5568295b)); // 0.000645551f, 2.31655e-30f, 0.00259138f, 7.95994e-30f, 1.25715e-11f, 1.5954e+13f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_5(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -6002,7 +6165,7 @@ path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3b21), SkBits2Float(0x2
|
||||
path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d8c55)); // 4.87407e+16f, 4.88495e+16f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 4, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 4, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) {
|
||||
@ -6041,10 +6204,226 @@ path.conicTo(SkBits2Float(0xf86d273b), SkBits2Float(0x27e523e3), SkBits2Float(0x
|
||||
path.cubicTo(SkBits2Float(0x2f273927), SkBits2Float(0xa83a2c21), SkBits2Float(0xd7122121), SkBits2Float(0x21212921), SkBits2Float(0x3be3db3a), SkBits2Float(0xa9deb63b)); // 1.52089e-10f, -1.03346e-14f, -1.60671e+14f, 5.46034e-19f, 0.00695362f, -9.89039e-14f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathSkipAssertOp(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
// crbug.com/626164
|
||||
static void fuzz763_1c(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
|
||||
path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
|
||||
path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
|
||||
path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
|
||||
|
||||
SkPath path2(path);
|
||||
SkPath dummy;
|
||||
testPathOp(reporter, path1, path2, (SkPathOp)4, filename);
|
||||
}
|
||||
|
||||
// crbug.com/626186
|
||||
static void fuzz763_1b(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x0000ff07), SkBits2Float(0xf9f9ff00), SkBits2Float(0xfe0ef9f4), SkBits2Float(0xd9b105fb), SkBits2Float(0x000000f9), SkBits2Float(0xfe11f901)); // 9.14866e-41f, -1.62257e+35f, -4.75121e+37f, -6.22846e+15f, 3.48923e-43f, -4.85077e+37f
|
||||
path.lineTo(SkBits2Float(0xda1905ed), SkBits2Float(0x3c05fbfb)); // -1.0768e+16f, 0.00817775f
|
||||
path.cubicTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0x3c3c3c3c), SkBits2Float(0x253c7f00), SkBits2Float(0xfa00d3fa), SkBits2Float(0x250025fe), SkBits2Float(0x00000006)); // 0.011489f, 0.011489f, 1.63494e-16f, -1.67228e+35f, 1.11151e-16f, 8.40779e-45f
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.quadTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0xfa253c3c), SkBits2Float(0xfefa00d3), SkBits2Float(0x25fad9df)); // 0.011489f, -2.14488e+35f, -1.66156e+38f, 4.35157e-16f
|
||||
path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.lineTo(SkBits2Float(0x8dfefa00), SkBits2Float(0xf0f9fad9)); // -1.57141e-30f, -6.1892e+29f
|
||||
path.cubicTo(SkBits2Float(0x20fe58f9), SkBits2Float(0x0525fbed), SkBits2Float(0x1905ffff), SkBits2Float(0x01f9f9f9), SkBits2Float(0xfbfe0ef9), SkBits2Float(0xfb212fff)); // 4.30882e-19f, 7.80453e-36f, 6.92764e-24f, 9.18268e-38f, -2.63829e+36f, -8.36933e+35f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp)2, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_1a(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x154be880), SkBits2Float(0x80000640), SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0x80045959), SkBits2Float(0x40154be8)); // 4.11789e-26f, -2.24208e-42f, 1.49562e+13f, 7.50652e+15f, -3.99394e-40f, 2.33276f
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.quadTo(SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0xbd595959), SkBits2Float(0x3f3f3f09)); // 1.49562e+13f, 7.50652e+15f, -0.0530637f, 0.747056f
|
||||
path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0x3f3f3f3f)); // 0.747059f, 0.747059f
|
||||
path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
|
||||
path.lineTo(SkBits2Float(0x09090909), SkBits2Float(0x3038d509)); // 1.6495e-33f, 6.72416e-10f
|
||||
path.conicTo(SkBits2Float(0x5947ffff), SkBits2Float(0x40e88004), SkBits2Float(0x00002059), SkBits2Float(0x28555900), SkBits2Float(0x5959d559)); // 3.51844e+15f, 7.26563f, 1.16042e-41f, 1.18432e-14f, 3.83217e+15f
|
||||
path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
|
||||
path.lineTo(SkBits2Float(0x38d57f4b), SkBits2Float(0x59597f4b)); // 0.000101803f, 3.82625e+15f
|
||||
path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f)); // 0.747059f, -2.54211e+38f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x384700ff), SkBits2Float(0x0108804b)); // 4.74462e-05f, 2.50713e-38f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp)0, filename);
|
||||
}
|
||||
|
||||
// crbug.com/627780
|
||||
static void fuzz763_3a(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 1);
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c)); // 1.50606e+13f, 1.43144e-13f
|
||||
path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0)); // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
|
||||
path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
|
||||
path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x2a685568), SkBits2Float(0x68295b2d)); // 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 2.06354e-13f, 3.19905e+24f
|
||||
path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0x7a6a4b77), SkBits2Float(0x3a214726)); // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, 3.04132e+35f, 0.000615226f
|
||||
path.moveTo(SkBits2Float(0x8adf2028), SkBits2Float(0x3a219a3a)); // -2.14862e-32f, 0.000616464f
|
||||
path.quadTo(SkBits2Float(0x3ab38e28), SkBits2Float(0x29283ac2), SkBits2Float(0x2be61d2a), SkBits2Float(0x812a4396)); // 0.0013699f, 3.73545e-14f, 1.63506e-12f, -3.12726e-38f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 1, filename);
|
||||
}
|
||||
|
||||
// crbug.com/627689
|
||||
static void fuzz763_5a(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 1);
|
||||
path.moveTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0
|
||||
path.conicTo(SkBits2Float(0x4183d871), SkBits2Float(0x41fea321), SkBits2Float(0xb700ff00), SkBits2Float(0x4240b8b8), SkBits2Float(0x3b058283)); // 16.4807f, 31.8297f, -7.68877e-06f, 48.1804f, 0.0020372f
|
||||
path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
|
||||
path.conicTo(SkBits2Float(0x3a455ec8), SkBits2Float(0xb8b8b8b3), SkBits2Float(0x38b2418d), SkBits2Float(0xb730d014), SkBits2Float(0x3f7ffff3)); // 0.000752908f, -8.80821e-05f, 8.49991e-05f, -1.05389e-05f, 0.999999f
|
||||
path.quadTo(SkBits2Float(0x3a51246a), SkBits2Float(0xb6da45a3), SkBits2Float(0x38bc5c3c), SkBits2Float(0x00000000)); // 0.000797814f, -6.50501e-06f, 8.98172e-05f, 0
|
||||
path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
|
||||
path.quadTo(SkBits2Float(0x39a32d2d), SkBits2Float(0x00000000), SkBits2Float(0xb8a13a00), SkBits2Float(0x00000000)); // 0.000311234f, 0, -7.68788e-05f, 0
|
||||
path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8)); // 0.000710409f, -8.80821e-05f
|
||||
path.quadTo(SkBits2Float(0x39ba814c), SkBits2Float(0xb838fed2), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0.00035573f, -4.41063e-05f, 0, 0
|
||||
path.lineTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000)); // 9.03719e-05f, 0
|
||||
path.close();
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOp(reporter, path1, path2, (SkPathOp) 4, filename);
|
||||
}
|
||||
|
||||
// crbug.com/627401
|
||||
static void fuzz763_2a(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 1);
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.quadTo(SkBits2Float(0x3e484500), SkBits2Float(0x164f3a30), SkBits2Float(0x49484801), SkBits2Float(0x7d0100c8)); // 0.195576f, 1.67397e-25f, 820352, 1.07172e+37f
|
||||
path.conicTo(SkBits2Float(0xff7f36fd), SkBits2Float(0x3e647d01), SkBits2Float(0x0c00f430), SkBits2Float(0x486b6448), SkBits2Float(0x00484848)); // -3.39239e+38f, 0.223133f, 9.93424e-32f, 241041, 6.63809e-39f
|
||||
path.lineTo(SkBits2Float(0x4f4f557d), SkBits2Float(0x48480112)); // 3.47849e+09f, 204804
|
||||
path.lineTo(SkBits2Float(0xf40c01ff), SkBits2Float(0x45008000)); // -4.43702e+31f, 2056
|
||||
path.moveTo(SkBits2Float(0x4bfffa00), SkBits2Float(0x7d4ac859)); // 3.35514e+07f, 1.68465e+37f
|
||||
path.conicTo(SkBits2Float(0x7d014f3e), SkBits2Float(0x00f4ff01), SkBits2Float(0x6b64480c), SkBits2Float(0x48484848), SkBits2Float(0x557d0100)); // 1.07426e+37f, 2.24993e-38f, 2.75975e+26f, 205089, 1.73863e+13f
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 0, filename);
|
||||
}
|
||||
|
||||
// crbug.com/627761
|
||||
static void fuzz763_2b(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 1);
|
||||
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55)); // 4.76191e+16f, 5.7784e-19f
|
||||
path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
|
||||
path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21)); // 2.50338e-13f, 4.61198e-19f
|
||||
path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x4721ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f9a3a8a)); // 5.63611e+25f, 41453.5f, 9.4495e-15f, 0.000617492f, 5.17506e+09f
|
||||
path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
|
||||
path.cubicTo(SkBits2Float(0x273ac23a), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x29685568)); // 2.5918e-15f, 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 5.15884e-14f
|
||||
path.lineTo(SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321)); // 4.78968e-34f, 1.99397e+36f
|
||||
path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28)); // 9.4495e-15f, 0.000617492f
|
||||
path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003)); // 0.00245715f, 1.64987e+07f
|
||||
path.quadTo(SkBits2Float(0x8a4fc29a), SkBits2Float(0x3ab3283a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6)); // -1.00033e-32f, 0.00136686f, 2.25206e-21f, 300.343f
|
||||
path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
|
||||
path.conicTo(SkBits2Float(0x1e2ab03a), SkBits2Float(0x2920213b), SkBits2Float(0x3b3ac527), SkBits2Float(0xc422333b), SkBits2Float(0x6c2a9f1f)); // 9.03617e-21f, 3.5556e-14f, 0.00284989f, -648.8f, 8.25075e+26f
|
||||
path.quadTo(SkBits2Float(0xc25d2757), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152), SkBits2Float(0x28d91210)); // -55.2884f, 0.000916855f, 1.2818e-13f, 2.40997e-14f
|
||||
path.quadTo(SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21)); // 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f
|
||||
path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
|
||||
path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0xcb7bc003), SkBits2Float(0x47ed7a6a)); // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, -1.64987e+07f, 121589
|
||||
path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
|
||||
path.close();
|
||||
path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829)); // 4.87419e+16f, 3.71718e-14f
|
||||
path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x2d8a3a21), SkBits2Float(0x5b682b68), SkBits2Float(0x5b292d55)); // -1.2117e+19f, 1.57146e-11f, 6.53499e+16f, 4.76191e+16f
|
||||
path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f)); // 1.43144e-13f, 4.7323e-37f
|
||||
path.conicTo(SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x3a21477a)); // 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 0.000615231f
|
||||
path.moveTo(SkBits2Float(0x21df2828), SkBits2Float(0x9a3a8a3a)); // 1.51217e-18f, -3.85756e-23f
|
||||
path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x28273ac2), SkBits2Float(0xe61d2a29), SkBits2Float(0x2a63962b)); // 0.00136978f, 9.2831e-15f, -1.85547e+23f, 2.02138e-13f
|
||||
path.conicTo(SkBits2Float(0x2d29272a), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55)); // 9.61523e-12f, 1.5954e+13f, 4.87407e+16f, 4.88097e+16f, 5.7784e-19f
|
||||
path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f
|
||||
path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3)); // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
|
||||
path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0)); // 4.7323e-37f, 6.14991e+25f
|
||||
path.close();
|
||||
|
||||
SkPath path2(path);
|
||||
testPathOpSkipAssert(reporter, path1, path2, (SkPathOp) 4, filename);
|
||||
}
|
||||
|
||||
static void fuzz763_2c(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.setFillType((SkPath::FillType) 1);
|
||||
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 0, 2.68653e-06f
|
||||
path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x364a4a4a), SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0, 3.01436e-06f, 3.01436e-06f, 0, 0
|
||||
path.lineTo(SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000)); // 3.01436e-06f, 0
|
||||
path.cubicTo(SkBits2Float(0x364a30f0), SkBits2Float(0x344ac7fb), SkBits2Float(0x3656d432), SkBits2Float(0x34cabb48), SkBits2Float(0x367031a9), SkBits2Float(0x351802f1)); // 3.01288e-06f, 1.88855e-07f, 3.2012e-06f, 3.77617e-07f, 3.57917e-06f, 5.66287e-07f
|
||||
path.cubicTo(SkBits2Float(0x36a7b150), SkBits2Float(0x35ab09db), SkBits2Float(0x371874ed), SkBits2Float(0x3604f2c7), SkBits2Float(0x3784e0c7), SkBits2Float(0x36344a51)); // 4.99763e-06f, 1.27434e-06f, 9.08713e-06f, 1.98108e-06f, 1.58403e-05f, 2.68653e-06f
|
||||
path.cubicTo(SkBits2Float(0x3743dc9a), SkBits2Float(0x36344a4f), SkBits2Float(0x36fbef33), SkBits2Float(0x36344a4e), SkBits2Float(0x36604a35), SkBits2Float(0x36344a4c)); // 1.16743e-05f, 2.68653e-06f, 7.50823e-06f, 2.68653e-06f, 3.34218e-06f, 2.68653e-06f
|
||||
path.cubicTo(SkBits2Float(0x36531715), SkBits2Float(0x36344a4c), SkBits2Float(0x3645e3f5), SkBits2Float(0x36344a4b), SkBits2Float(0x3638b0d4), SkBits2Float(0x36344a4b)); // 3.14549e-06f, 2.68653e-06f, 2.9488e-06f, 2.68653e-06f, 2.75211e-06f, 2.68653e-06f
|
||||
path.cubicTo(SkBits2Float(0x35f64120), SkBits2Float(0x36344a4b), SkBits2Float(0x35764124), SkBits2Float(0x36344a4a), SkBits2Float(0x00000000), SkBits2Float(0x36344a4a)); // 1.83474e-06f, 2.68653e-06f, 9.17369e-07f, 2.68653e-06f, 0, 2.68653e-06f
|
||||
path.close();
|
||||
SkPath path1(path);
|
||||
path.reset();
|
||||
path.setFillType((SkPath::FillType) 0);
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
|
||||
path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
|
||||
path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
|
||||
path.cubicTo(SkBits2Float(0x544a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.47532e+12f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
|
||||
SkPath path2(path);
|
||||
testPathOpCheck(reporter, path1, path2, kReverseDifference_SkPathOp, filename, true);
|
||||
}
|
||||
|
||||
static struct TestDesc failTests[] = {
|
||||
TEST(fuzz763_2c),
|
||||
TEST(fuzz763_2b),
|
||||
TEST(fuzz763_2a),
|
||||
TEST(fuzz763_5a),
|
||||
TEST(fuzz763_3a),
|
||||
TEST(fuzz763_1a),
|
||||
TEST(fuzz763_1b),
|
||||
TEST(fuzz763_1c),
|
||||
TEST(fuzz763_2),
|
||||
TEST(fuzz763_5),
|
||||
TEST(fuzz763_3),
|
||||
@ -6072,3 +6451,14 @@ DEF_TEST(PathOpsFailOp, reporter) {
|
||||
#endif
|
||||
RunTestSet(reporter, failTests, failTestCount, nullptr, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
static struct TestDesc repTests[] = {
|
||||
TEST(loops44i),
|
||||
TEST(loops45i),
|
||||
TEST(loops46i),
|
||||
};
|
||||
|
||||
DEF_TEST(PathOpsRepOp, reporter) {
|
||||
for (int index = 0; index < 2; ++index)
|
||||
RunTestSet(reporter, repTests, SK_ARRAY_COUNT(repTests), nullptr, nullptr, nullptr, false);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "PathOpsExtendedTest.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkPathOps.h"
|
||||
#include "SkPoint.h"
|
||||
@ -93,7 +94,44 @@ static void dontFailOne(skiatest::Reporter* reporter, int index) {
|
||||
reporter->bumpTestCount();
|
||||
}
|
||||
|
||||
static void fuzz_59(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419)); // 200, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b)); // 40, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b)); // 240, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419)); // 70, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
|
||||
path.close();
|
||||
testSimplifyCheck(reporter, path, filename, true);
|
||||
}
|
||||
|
||||
static void fuzz_x1(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
|
||||
path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
|
||||
path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
|
||||
path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void fuzz_x2(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
|
||||
path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a)); // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
|
||||
path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000)); // 5.96533e-42f, 9.15715e-24f
|
||||
path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a)); // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
|
||||
path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff)); // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
#define TEST(test) test(reporter, #test)
|
||||
|
||||
DEF_TEST(PathOpsSimplifyFail, reporter) {
|
||||
TEST(fuzz_x2);
|
||||
TEST(fuzz_x1);
|
||||
TEST(fuzz_59);
|
||||
for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) {
|
||||
failOne(reporter, index);
|
||||
}
|
||||
@ -102,6 +140,8 @@ DEF_TEST(PathOpsSimplifyFail, reporter) {
|
||||
}
|
||||
}
|
||||
|
||||
#undef TEST
|
||||
|
||||
DEF_TEST(PathOpsSimplifyFailOne, reporter) {
|
||||
int index = 0;
|
||||
failOne(reporter, index);
|
||||
|
@ -5052,19 +5052,259 @@ path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000)); // 3.35546e+07
|
||||
path.lineTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000)); // 123, 0
|
||||
path.close();
|
||||
|
||||
REPORTER_ASSERT(reporter, !Simplify(path, &path));
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void fuzz_59(skiatest::Reporter* reporter, const char* filename) {
|
||||
static void tiger8(skiatest::Reporter* reporter, const char* filename) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419)); // 200, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b)); // 40, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b)); // 240, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419)); // 70, -9.09969e+08f
|
||||
path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c)); // 140, -9.09969e+08f
|
||||
path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
|
||||
path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
|
||||
path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
|
||||
path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
|
||||
path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
|
||||
path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
|
||||
path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
|
||||
path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
|
||||
path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
|
||||
path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
|
||||
path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
|
||||
path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
|
||||
path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
|
||||
path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
|
||||
path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
|
||||
path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
|
||||
path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
|
||||
path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
|
||||
path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
|
||||
path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
|
||||
path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
|
||||
path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
|
||||
path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
|
||||
path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
|
||||
path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
|
||||
path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
|
||||
path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
|
||||
path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
|
||||
path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
|
||||
path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
|
||||
path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
|
||||
path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
path.close();
|
||||
REPORTER_ASSERT(reporter, !Simplify(path, &path));
|
||||
path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f
|
||||
path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
|
||||
path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f
|
||||
path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f
|
||||
path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f
|
||||
path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f
|
||||
path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f
|
||||
path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f
|
||||
path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f
|
||||
path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f
|
||||
path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f
|
||||
path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f
|
||||
path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f
|
||||
path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f
|
||||
path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f
|
||||
path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f
|
||||
path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f
|
||||
path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f
|
||||
path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f
|
||||
path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f
|
||||
path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f
|
||||
path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225
|
||||
path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f
|
||||
path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f
|
||||
path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f
|
||||
path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f
|
||||
path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f
|
||||
path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f
|
||||
path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f
|
||||
path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f
|
||||
path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
// fails to include a line of edges, probably mis-sorting
|
||||
static void tiger8a(skiatest::Reporter* reporter, const char* filename) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
|
||||
path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
|
||||
path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
|
||||
path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
|
||||
path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
|
||||
path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
|
||||
path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
|
||||
path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
|
||||
path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
|
||||
path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
|
||||
path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
|
||||
path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
|
||||
path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
|
||||
path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
|
||||
path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
|
||||
path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
|
||||
path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
|
||||
path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
|
||||
path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
|
||||
path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
|
||||
path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
|
||||
path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
|
||||
path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
|
||||
path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
|
||||
path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
|
||||
path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
|
||||
path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
|
||||
path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
|
||||
path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
|
||||
path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
|
||||
path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
|
||||
path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
|
||||
path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void tiger8a_x(skiatest::Reporter* reporter, const char* filename, uint64_t testlines) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
SkPath path;
|
||||
uint64_t i = 0;
|
||||
if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8)); // 491.101f, 221.164f, 494.218f, 218.781f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5)); // 495.677f, 217.665f, 497.404f, 217.319f
|
||||
if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1)); // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191)); // 505.529f, 216.575f, 510.015f, 210.881f
|
||||
if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3)); // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
|
||||
if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae)); // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229)); // 513.095f, 219.189f, 514.853f, 224.696f
|
||||
if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b)); // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887)); // 502.571f, 232.56f, 496.407f, 229.721f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330)); // 494.35f, 228.774f, 493.192f, 226.887f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04)); // 492.828f, 226.296f, 492.597f, 225.676f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe)); // 492.523f, 225.476f, 492.47f, 225.292f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda)); // 492.451f, 225.229f, 492.438f, 225.175f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357)); // 492.433f, 225.158f, 492.429f, 225.138f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005)); // 492.426f, 225.128f, 492.426f, 225.125f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc)); // 492.424f, 225.12f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34)); // 492.424f, 225.118f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3)); // 492.424f, 225.117f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5)); // 492.424f, 225.117f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb)); // 492.424f, 225.117f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37)); // 492.424f, 225.118f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19)); // 492.424f, 225.118f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9)); // 492.425f, 225.121f, 492.425f, 225.121f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1)); // 492.425f, 225.122f, 492.425f, 225.124f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a)); // 492.427f, 225.131f, 492.429f, 225.138f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e)); // 492.43f, 225.142f, 492.43f, 225.143f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0)); // 492.436f, 225.168f, 492.444f, 225.194f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368)); // 492.465f, 225.262f, 492.495f, 225.326f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468)); // 492.527f, 225.393f, 492.568f, 225.455f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60)); // 493.058f, 226.189f, 493.847f, 225.794f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0)); // 494.273f, 225.582f, 494.376f, 225.117f
|
||||
if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667)); // 494.4f, 225.01f, 494.4f, 224.9f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf)); // 492.424f, 225.116f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666)); // 493.4f, 224.9f
|
||||
if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a)); // 492.451f, 225.216f
|
||||
if (testlines & (1LL << i++)) path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
#include "SkRandom.h"
|
||||
|
||||
static void tiger8a_h(skiatest::Reporter* reporter, const char* filename) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
SkRandom r;
|
||||
for (int samples = 2; samples < 38; ++samples) {
|
||||
for (int tests = 0; tests < 10000; ++tests) {
|
||||
uint64_t testlines = 0;
|
||||
for (int i = 0; i < samples; ++i) {
|
||||
int bit;
|
||||
do {
|
||||
bit = r.nextRangeU(0, 38);
|
||||
} while (testlines & (1LL << bit));
|
||||
testlines |= 1LL << bit;
|
||||
}
|
||||
tiger8a_x(reporter, filename, testlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tiger8a_h_1(skiatest::Reporter* reporter, const char* filename) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
uint64_t testlines = 0x0000000280802863; // best so far: 0x0000001610031021;
|
||||
tiger8a_x(reporter, filename, testlines);
|
||||
}
|
||||
|
||||
// tries to add same edge twice
|
||||
static void tiger8b(skiatest::Reporter* reporter, const char* filename) {
|
||||
#if DEBUG_UNDER_DEVELOPMENT // tiger
|
||||
return;
|
||||
#endif
|
||||
SkPath path;
|
||||
path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 494.349f, 224.584f
|
||||
path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741)); // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
|
||||
path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667)); // 492.4f, 224.9f
|
||||
path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede)); // 492.4f, 224.79f, 492.424f, 224.683f
|
||||
path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e)); // 492.527f, 224.218f, 492.953f, 224.006f
|
||||
path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866)); // 493.742f, 223.611f, 494.232f, 224.345f
|
||||
path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966)); // 494.273f, 224.407f, 494.305f, 224.474f
|
||||
path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e)); // 494.335f, 224.538f, 494.356f, 224.606f
|
||||
path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850)); // 494.364f, 224.632f, 494.37f, 224.657f
|
||||
path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964)); // 494.37f, 224.658f, 494.371f, 224.662f
|
||||
path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d)); // 494.373f, 224.669f, 494.375f, 224.676f
|
||||
path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5)); // 494.375f, 224.678f, 494.375f, 224.679f
|
||||
path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5)); // 494.375f, 224.679f, 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97)); // 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf)); // 494.376f, 224.683f
|
||||
path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa)); // 494.376f, 224.682f
|
||||
path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50)); // 494.376f, 224.681f
|
||||
path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1)); // 494.375f, 224.679f
|
||||
path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d)); // 494.375f, 224.677f
|
||||
path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12)); // 494.375f, 224.68f, 494.376f, 224.684f
|
||||
path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94)); // 494.382f, 224.707f, 494.391f, 224.737f
|
||||
path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a)); // 494.423f, 224.848f, 494.471f, 224.978f
|
||||
path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738)); // 494.634f, 225.415f, 494.896f, 225.841f
|
||||
path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779)); // 495.737f, 227.211f, 497.243f, 227.904f
|
||||
path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133)); // 502.478f, 230.315f, 513.407f, 224.13f
|
||||
path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff)); // 513.9f, 225
|
||||
path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5)); // 512.947f, 225.304f
|
||||
path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15)); // 511.146f, 219.66f, 509.814f, 211.664f
|
||||
path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff)); // 510.8f, 211.5f
|
||||
path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d)); // 511.586f, 212.119f
|
||||
path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf)); // 506.567f, 218.489f, 497.691f, 219.296f
|
||||
path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc)); // 497.6f, 218.3f
|
||||
path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3)); // 497.796f, 219.281f
|
||||
path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e)); // 496.523f, 219.535f, 495.432f, 220.369f
|
||||
path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572)); // 493.449f, 221.886f, 494.349f, 224.584f
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
// FIXME: this should not fail -- it was isolated looking for the root cause to fuzz763_4713
|
||||
@ -5101,7 +5341,443 @@ path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x4
|
||||
path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0));
|
||||
path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
|
||||
path.close();
|
||||
testSimplifyCheck(reporter, path, filename, false);
|
||||
// DEBUG_UNDER_DEVELOPMENT fuzz763_4713_b disable expectation check for now
|
||||
testSimplifyCheck(reporter, path, filename, !FLAGS_runFail);
|
||||
}
|
||||
|
||||
static void dean4(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
|
||||
// start region
|
||||
// start loop, contour: 1
|
||||
// Segment 1145.3381097316742 2017.6783947944641 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
|
||||
path.moveTo(1145.3381347656250, 2017.6783447265625);
|
||||
path.lineTo(1145.3381347656250, 2017.0034179687500);
|
||||
// Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927231521568 2017.0033947825432
|
||||
path.lineTo(1143.6927490234375, 2017.0034179687500);
|
||||
// Segment 1143.6927231521568 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
|
||||
path.lineTo(1144.8640136718750, 2018.1589355468750);
|
||||
// Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.6783947944641
|
||||
path.lineTo(1145.3381347656250, 2017.6783447265625);
|
||||
path.close();
|
||||
// start loop, contour: 2
|
||||
// Segment 1145.3381097316742 2016.3216052055359 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
|
||||
path.moveTo(1145.3381347656250, 2016.3216552734375);
|
||||
path.lineTo(1144.8640136718750, 2015.8410644531250);
|
||||
// Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927230811802 2016.9966052174568
|
||||
path.lineTo(1143.6927490234375, 2016.9965820312500);
|
||||
// Segment 1143.6927230811802 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
|
||||
path.lineTo(1145.3381347656250, 2016.9965820312500);
|
||||
// Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.3216052055359
|
||||
path.lineTo(1145.3381347656250, 2016.3216552734375);
|
||||
path.close();
|
||||
// start loop, contour: 3
|
||||
// Segment 1147.3323798179626 2014.3542600870132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220239557 2014.8347900059885
|
||||
path.moveTo(1147.3323974609375, 2014.3542480468750);
|
||||
path.lineTo(1147.8063964843750, 2014.8348388671875);
|
||||
// Segment 1147.8064220239557 2014.8347900059885 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
|
||||
path.lineTo(1147.8063964843750, 2014.8348388671875);
|
||||
// Segment 1147.8064220516883 2014.8347899786306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2014.3542600870132
|
||||
path.lineTo(1147.3323974609375, 2014.3542480468750);
|
||||
path.close();
|
||||
// start loop, contour: 4
|
||||
// Segment 1146.3696286678314 2013.4045072346926 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708778083 2013.8850371497379
|
||||
path.moveTo(1146.3696289062500, 2013.4045410156250);
|
||||
path.lineTo(1146.8436279296875, 2013.8850097656250);
|
||||
// Segment 1146.8436708778083 2013.8850371497379 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
|
||||
path.lineTo(1146.8436279296875, 2013.8850097656250);
|
||||
// Segment 1146.8436709015571 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2013.4045072346926
|
||||
path.lineTo(1146.3696289062500, 2013.4045410156250);
|
||||
path.close();
|
||||
// start loop, contour: 5
|
||||
// Segment 1143.2063037902117 2016.5251235961914 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615802348 2016.0445936811461
|
||||
path.moveTo(1143.2062988281250, 2016.5251464843750);
|
||||
path.lineTo(1142.7322998046875, 2016.0445556640625);
|
||||
// Segment 1142.7322615802348 2016.0445936811461 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
|
||||
path.lineTo(1142.7322998046875, 2016.0445556640625);
|
||||
// Segment 1142.7322615564860 2016.0445937045740 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2016.5251235961914
|
||||
path.lineTo(1143.2062988281250, 2016.5251464843750);
|
||||
path.close();
|
||||
// start loop, contour: 6
|
||||
// Segment 1143.0687679275870 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2017.2091718784643
|
||||
path.moveTo(1143.0687255859375, 2016.7286376953125);
|
||||
path.lineTo(1143.5428466796875, 2017.2092285156250);
|
||||
// Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2017.0109272411960
|
||||
path.lineTo(1143.7437744140625, 2017.0109863281250);
|
||||
// Segment 1143.7437679395080 2017.0109272411960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
|
||||
path.lineTo(1143.7437744140625, 2016.7286376953125);
|
||||
// Segment 1143.7437679395080 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.0687679275870 2016.7286419868469
|
||||
path.lineTo(1143.0687255859375, 2016.7286376953125);
|
||||
path.close();
|
||||
// start loop, contour: 7
|
||||
// Segment 1143.2063037902117 2017.4748764038086 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615603032 2017.9554062991915
|
||||
path.moveTo(1143.2062988281250, 2017.4748535156250);
|
||||
path.lineTo(1142.7322998046875, 2017.9554443359375);
|
||||
// Segment 1142.7322615603032 2017.9554062991915 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
|
||||
path.lineTo(1142.7322998046875, 2017.9554443359375);
|
||||
// Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2017.4748764038086
|
||||
path.lineTo(1143.2062988281250, 2017.4748535156250);
|
||||
path.close();
|
||||
// start loop, contour: 8
|
||||
// Segment 1146.3696286678314 2020.5954928398132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708977399 2020.1149629444303
|
||||
path.moveTo(1146.3696289062500, 2020.5954589843750);
|
||||
path.lineTo(1146.8436279296875, 2020.1149902343750);
|
||||
// Segment 1146.8436708977399 2020.1149629444303 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
|
||||
path.lineTo(1146.8436279296875, 2020.1149902343750);
|
||||
// Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2020.5954928398132
|
||||
path.lineTo(1146.3696289062500, 2020.5954589843750);
|
||||
path.close();
|
||||
// start loop, contour: 9
|
||||
// Segment 1147.3323798179626 2019.6457400321960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220484741 2019.1652101374082
|
||||
path.moveTo(1147.3323974609375, 2019.6457519531250);
|
||||
path.lineTo(1147.8063964843750, 2019.1651611328125);
|
||||
// Segment 1147.8064220484741 2019.1652101374082 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
|
||||
path.lineTo(1147.8063964843750, 2019.1651611328125);
|
||||
// Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2019.6457400321960
|
||||
path.lineTo(1147.3323974609375, 2019.6457519531250);
|
||||
path.close();
|
||||
// start loop, contour: 10
|
||||
// Segment 1145.3381097316742 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
|
||||
path.moveTo(1145.3381347656250, 2018.3533935546875);
|
||||
path.lineTo(1156.6848144531250, 2018.3533935546875);
|
||||
// Segment 1156.6848182678223 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2017.0033947825432
|
||||
path.lineTo(1156.6848144531250, 2017.0034179687500);
|
||||
// Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
|
||||
path.lineTo(1145.3381347656250, 2017.0034179687500);
|
||||
// Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2018.3533948063850
|
||||
path.lineTo(1145.3381347656250, 2018.3533935546875);
|
||||
path.close();
|
||||
// start loop, contour: 11
|
||||
// Segment 1156.6848182678223 2018.3533948063850 0.3569631313191 0.0000000000000 -0.2645167304388 0.2609454237780 1157.6574279406423 2017.9723661860094
|
||||
path.moveTo(1156.6848144531250, 2018.3533935546875);
|
||||
path.cubicTo(1157.0417480468750, 2018.3533935546875, 1157.3929443359375, 2018.2332763671875, 1157.6574707031250, 2017.9724121093750);
|
||||
// Segment 1157.6574279406423 2017.9723661860094 0.2653344079822 -0.2617520616521 0.0000000000000 0.3596905289350 1158.0474975705147 2017.0000000000000
|
||||
path.cubicTo(1157.9227294921875, 2017.7105712890625, 1158.0474853515625, 2017.3597412109375, 1158.0474853515625, 2017.0000000000000);
|
||||
// Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6974975466728 2017.0000000000000
|
||||
path.lineTo(1156.6975097656250, 2017.0000000000000);
|
||||
// Segment 1156.6974975466728 2017.0000000000000 0.0028009248351 0.0403311981485 0.0118595244351 -0.0220843520393 1156.6941780622435 2017.0325257649940
|
||||
path.cubicTo(1156.7003173828125, 2017.0402832031250, 1156.7060546875000, 2017.0104980468750, 1156.6942138671875, 2017.0324707031250);
|
||||
// Segment 1156.6941780622435 2017.0325257649940 -0.0032637855860 0.0184860248562 0.0120617528380 -0.0065934603083 1156.7093435710913 2017.0113063061967
|
||||
path.cubicTo(1156.6909179687500, 2017.0510253906250, 1156.7214355468750, 2017.0047607421875, 1156.7093505859375, 2017.0113525390625);
|
||||
// split at 0.4496445953846
|
||||
// path.cubicTo(1156.6927490234375, 2017.0407714843750, 1156.6981201171875, 2017.0360107421875, 1156.7033691406250, 2017.0289306640625);
|
||||
// path.cubicTo(1156.7097167968750, 2017.0201416015625, 1156.7159423828125, 2017.0076904296875, 1156.7093505859375, 2017.0113525390625);
|
||||
// Segment 1156.7093435710913 2017.0113063061967 -0.0070717276929 0.0122220954353 0.0203483811973 -0.0039136894418 1156.7268834554304 2016.9985353221975
|
||||
path.cubicTo(1156.7022705078125, 2017.0235595703125, 1156.7471923828125, 2016.9946289062500, 1156.7269287109375, 2016.9985351562500);
|
||||
// Segment 1156.7268834554304 2016.9985353221975 -0.0244396787691 0.0123649140586 0.0433322464027 0.0026558844666 1156.6848182678223 2017.0033947825432
|
||||
path.cubicTo(1156.7023925781250, 2017.0108642578125, 1156.7281494140625, 2017.0061035156250, 1156.6848144531250, 2017.0034179687500);
|
||||
// split at 0.4418420493603
|
||||
// path.cubicTo(1156.7160644531250, 2017.0040283203125, 1156.7150878906250, 2017.0061035156250, 1156.7136230468750, 2017.0065917968750);
|
||||
// path.cubicTo(1156.7116699218750, 2017.0070800781250, 1156.7089843750000, 2017.0048828125000, 1156.6848144531250, 2017.0034179687500);
|
||||
// Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
|
||||
path.lineTo(1156.6848144531250, 2018.3533935546875);
|
||||
path.close();
|
||||
// start loop, contour: 12
|
||||
// Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 -0.3596905289350 0.2653344079822 0.2617520616521 1157.6574279406423 2016.0276338139906
|
||||
path.moveTo(1158.0474853515625, 2017.0000000000000);
|
||||
path.cubicTo(1158.0474853515625, 2016.6402587890625, 1157.9227294921875, 2016.2894287109375, 1157.6574707031250, 2016.0275878906250);
|
||||
// Segment 1157.6574279406423 2016.0276338139906 -0.2645167304388 -0.2609454237780 0.3569631313191 0.0000000000000 1156.6848182678223 2015.6466051936150
|
||||
path.cubicTo(1157.3929443359375, 2015.7667236328125, 1157.0417480468750, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
|
||||
// split at 0.5481675863266
|
||||
// path.cubicTo(1157.5124511718750, 2015.8846435546875, 1157.3414306640625, 2015.7839355468750, 1157.1577148437500, 2015.7220458984375);
|
||||
// path.cubicTo(1157.0062255859375, 2015.6711425781250, 1156.8460693359375, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
|
||||
// Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
|
||||
path.lineTo(1156.6848144531250, 2016.9965820312500);
|
||||
// Segment 1156.6848182678223 2016.9966052174568 0.0433322464027 -0.0026558844666 -0.0244396787691 -0.0123649140586 1156.7268834554304 2017.0014646778025
|
||||
path.cubicTo(1156.7281494140625, 2016.9938964843750, 1156.7023925781250, 2016.9891357421875, 1156.7269287109375, 2017.0014648437500);
|
||||
// split at 0.5581579208374
|
||||
// path.cubicTo(1156.7089843750000, 2016.9951171875000, 1156.7116699218750, 2016.9929199218750, 1156.7136230468750, 2016.9934082031250);
|
||||
// path.cubicTo(1156.7150878906250, 2016.9938964843750, 1156.7160644531250, 2016.9959716796875, 1156.7269287109375, 2017.0014648437500);
|
||||
// Segment 1156.7268834554304 2017.0014646778025 0.0203483811973 0.0039136894418 -0.0070717276929 -0.0122220954353 1156.7093435710913 2016.9886936938033
|
||||
path.cubicTo(1156.7471923828125, 2017.0053710937500, 1156.7022705078125, 2016.9764404296875, 1156.7093505859375, 2016.9886474609375);
|
||||
// Segment 1156.7093435710913 2016.9886936938033 0.0120617528380 0.0065934603083 -0.0032637855860 -0.0184860248562 1156.6941780622435 2016.9674742350060
|
||||
path.cubicTo(1156.7214355468750, 2016.9952392578125, 1156.6909179687500, 2016.9489746093750, 1156.6942138671875, 2016.9675292968750);
|
||||
// Segment 1156.6941780622435 2016.9674742350060 0.0118595244351 0.0220843520393 0.0028009248351 -0.0403311981485 1156.6974975466728 2017.0000000000000
|
||||
path.cubicTo(1156.7060546875000, 2016.9895019531250, 1156.7003173828125, 2016.9597167968750, 1156.6975097656250, 2017.0000000000000);
|
||||
// split at 0.4572408795357
|
||||
// path.cubicTo(1156.6995849609375, 2016.9775390625000, 1156.7014160156250, 2016.9768066406250, 1156.7014160156250, 2016.9768066406250);
|
||||
// path.cubicTo(1156.7014160156250, 2016.9769287109375, 1156.6989746093750, 2016.9781494140625, 1156.6975097656250, 2017.0000000000000);
|
||||
// Segment 1156.6974975466728 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1158.0474975705147 2017.0000000000000
|
||||
path.lineTo(1158.0474853515625, 2017.0000000000000);
|
||||
path.close();
|
||||
// start loop, contour: 13
|
||||
// Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2015.6466051936150
|
||||
path.moveTo(1156.6848144531250, 2015.6466064453125);
|
||||
path.lineTo(1145.3381347656250, 2015.6466064453125);
|
||||
// Segment 1145.3381097316742 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
|
||||
path.lineTo(1145.3381347656250, 2016.9965820312500);
|
||||
// Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
|
||||
path.lineTo(1156.6848144531250, 2016.9965820312500);
|
||||
// Segment 1156.6848182678223 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2015.6466051936150
|
||||
path.lineTo(1156.6848144531250, 2015.6466064453125);
|
||||
path.close();
|
||||
// start loop, contour: 14
|
||||
// Segment 1145.8121519375022 2016.8021351246741 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220237907 2014.8347900061515
|
||||
path.moveTo(1145.8121337890625, 2016.8021240234375);
|
||||
path.lineTo(1147.8063964843750, 2014.8348388671875);
|
||||
// Segment 1147.8064220237907 2014.8347900061515 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583376121346 2013.8737301678750
|
||||
path.lineTo(1146.8583984375000, 2013.8737792968750);
|
||||
// Segment 1146.8583376121346 2013.8737301678750 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
|
||||
path.lineTo(1144.8640136718750, 2015.8410644531250);
|
||||
// Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519375022 2016.8021351246741
|
||||
path.lineTo(1145.8121337890625, 2016.8021240234375);
|
||||
path.close();
|
||||
// start loop, contour: 15
|
||||
// Segment 1147.8064220516883 2014.8347899786306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2012.9239773430752
|
||||
path.moveTo(1147.8063964843750, 2014.8348388671875);
|
||||
path.cubicTo(1148.3494873046875, 2014.2990722656250, 1148.3494873046875, 2013.4597167968750, 1147.8063964843750, 2012.9239501953125);
|
||||
// Segment 1147.8064220516883 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2013.8850371263100
|
||||
path.lineTo(1146.8583984375000, 2013.8850097656250);
|
||||
// Segment 1146.8583375842370 2013.8850371263100 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2013.8737301953959
|
||||
path.cubicTo(1146.8654785156250, 2013.8920898437500, 1146.8654785156250, 2013.8666992187500, 1146.8583984375000, 2013.8737792968750);
|
||||
// Segment 1146.8583375842370 2013.8737301953959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
|
||||
path.lineTo(1147.8063964843750, 2014.8348388671875);
|
||||
path.close();
|
||||
// start loop, contour: 16
|
||||
// Segment 1147.8064220516883 2012.9239773430752 -0.5379138488298 -0.5306514472866 0.5379138488298 -0.5306514472866 1145.8955864341058 2012.9239773430752
|
||||
path.moveTo(1147.8063964843750, 2012.9239501953125);
|
||||
path.cubicTo(1147.2685546875000, 2012.3933105468750, 1146.4334716796875, 2012.3933105468750, 1145.8956298828125, 2012.9239501953125);
|
||||
// Segment 1145.8955864341058 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
|
||||
path.lineTo(1146.8436279296875, 2013.8850097656250);
|
||||
// Segment 1146.8436709015571 2013.8850371263100 0.0122295718664 -0.0120644598103 -0.0122295718664 -0.0120644598103 1146.8583375842370 2013.8850371263100
|
||||
path.cubicTo(1146.8559570312500, 2013.8729248046875, 1146.8460693359375, 2013.8729248046875, 1146.8583984375000, 2013.8850097656250);
|
||||
// Segment 1146.8583375842370 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2012.9239773430752
|
||||
path.lineTo(1147.8063964843750, 2012.9239501953125);
|
||||
path.close();
|
||||
// start loop, contour: 17
|
||||
// Segment 1145.8955864579798 2012.9239773195236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615803600 2016.0445936810224
|
||||
path.moveTo(1145.8956298828125, 2012.9239501953125);
|
||||
path.lineTo(1142.7322998046875, 2016.0445556640625);
|
||||
// Segment 1142.7322615803600 2016.0445936810224 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460000633 2017.0056535113604
|
||||
path.lineTo(1143.6802978515625, 2017.0056152343750);
|
||||
// Segment 1143.6803460000633 2017.0056535113604 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708776831 2013.8850371498615
|
||||
path.lineTo(1146.8436279296875, 2013.8850097656250);
|
||||
// Segment 1146.8436708776831 2013.8850371498615 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864579798 2012.9239773195236
|
||||
path.lineTo(1145.8956298828125, 2012.9239501953125);
|
||||
path.close();
|
||||
// start loop, contour: 18
|
||||
// Segment 1142.7322615564860 2016.0445937045740 -0.0343838913237 0.0339196727021 0.0561572931720 -0.0710493024751 1142.5744069596683 2016.2183613784646
|
||||
path.moveTo(1142.7322998046875, 2016.0445556640625);
|
||||
path.cubicTo(1142.6978759765625, 2016.0784912109375, 1142.6306152343750, 2016.1473388671875, 1142.5744628906250, 2016.2183837890625);
|
||||
// Segment 1142.5744069596683 2016.2183613784646 -0.0547779032556 0.0720510806539 0.0000000000000 -0.2570904015602 1142.3937679156661 2016.7286419868469
|
||||
path.cubicTo(1142.5196533203125, 2016.2904052734375, 1142.3937988281250, 2016.4715576171875, 1142.3937988281250, 2016.7286376953125);
|
||||
// Segment 1142.3937679156661 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
|
||||
path.lineTo(1143.7437744140625, 2016.7286376953125);
|
||||
// Segment 1143.7437679395080 2016.7286419868469 -0.0051909534315 0.0665915567290 0.0133980913650 -0.0361675066532 1143.6976291086639 2016.9514128270803
|
||||
path.cubicTo(1143.7385253906250, 2016.7952880859375, 1143.7110595703125, 2016.9152832031250, 1143.6976318359375, 2016.9514160156250);
|
||||
// Segment 1143.6976291086639 2016.9514128270803 -0.0142876819622 0.0277028472317 0.0040377216094 -0.0063254385208 1143.6490888124401 2017.0354042045738
|
||||
path.cubicTo(1143.6833496093750, 2016.9791259765625, 1143.6530761718750, 2017.0290527343750, 1143.6490478515625, 2017.0354003906250);
|
||||
// Segment 1143.6490888124401 2017.0354042045738 -0.0045813437564 0.0032098513409 -0.0343840362634 0.0339198156850 1143.6803460239373 2017.0056534878088
|
||||
path.cubicTo(1143.6445312500000, 2017.0385742187500, 1143.6459960937500, 2017.0395507812500, 1143.6802978515625, 2017.0056152343750);
|
||||
// Segment 1143.6803460239373 2017.0056534878088 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
|
||||
path.lineTo(1142.7322998046875, 2016.0445556640625);
|
||||
path.close();
|
||||
// start loop, contour: 19
|
||||
// Segment 1142.5947256938614 2016.2481120952295 -0.1857487117715 0.1832409092043 0.0167379373694 -0.0990717748979 1142.3430278987244 2016.7518748698508
|
||||
path.moveTo(1142.5947265625000, 2016.2481689453125);
|
||||
path.cubicTo(1142.4089355468750, 2016.4313964843750, 1142.3597412109375, 2016.6528320312500, 1142.3430175781250, 2016.7518310546875);
|
||||
// Segment 1142.3430278987244 2016.7518748698508 -0.0156657977007 0.1069052535795 0.0000000000000 -0.0339197441936 1142.3249999880791 2017.0000000000000
|
||||
path.cubicTo(1142.3273925781250, 2016.8587646484375, 1142.3249511718750, 2016.9660644531250, 1142.3249511718750, 2017.0000000000000);
|
||||
// Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6750000119209 2017.0000000000000
|
||||
path.lineTo(1143.6750488281250, 2017.0000000000000);
|
||||
// Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 -0.0339197441936 -0.0015261841961 -0.0051459911965 1143.6741640831724 2016.9767671169961
|
||||
path.cubicTo(1143.6750488281250, 2016.9660644531250, 1143.6726074218750, 2016.9716796875000, 1143.6741943359375, 2016.9768066406250);
|
||||
// Segment 1143.6741640831724 2016.9767671169961 -0.0007886982052 0.0013596649622 0.0074114058388 -0.0224954551713 1143.6525251830094 2017.0486861571169
|
||||
path.cubicTo(1143.6733398437500, 2016.9781494140625, 1143.6599121093750, 2017.0262451171875, 1143.6524658203125, 2017.0487060546875);
|
||||
// split at 0.4203657805920
|
||||
// path.cubicTo(1143.6738281250000, 2016.9774169921875, 1143.6712646484375, 2016.9862060546875, 1143.6678466796875, 2016.9979248046875);
|
||||
// path.cubicTo(1143.6630859375000, 2017.0140380859375, 1143.6567382812500, 2017.0356445312500, 1143.6524658203125, 2017.0487060546875);
|
||||
// Segment 1143.6525251830094 2017.0486861571169 -0.0119644334077 0.0236755853369 0.0381324473830 -0.0447670202574 1143.5428101613127 2017.2091718784643
|
||||
path.cubicTo(1143.6405029296875, 2017.0723876953125, 1143.5809326171875, 2017.1644287109375, 1143.5428466796875, 2017.2092285156250);
|
||||
// Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2016.2481120952295
|
||||
path.lineTo(1142.5947265625000, 2016.2481689453125);
|
||||
path.close();
|
||||
// start loop, contour: 20
|
||||
// Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0339197441936 -0.0156657977007 -0.1069052535795 1142.3430278987244 2017.2481251301492
|
||||
path.moveTo(1142.3249511718750, 2017.0000000000000);
|
||||
path.cubicTo(1142.3249511718750, 2017.0339355468750, 1142.3273925781250, 2017.1412353515625, 1142.3430175781250, 2017.2481689453125);
|
||||
// Segment 1142.3430278987244 2017.2481251301492 0.0167379373694 0.0990717748979 -0.1857487117715 -0.1832409092043 1142.5947256938614 2017.7518879047705
|
||||
path.cubicTo(1142.3597412109375, 2017.3471679687500, 1142.4089355468750, 2017.5686035156250, 1142.5947265625000, 2017.7518310546875);
|
||||
// split at 0.4008532166481
|
||||
// path.cubicTo(1142.3497314453125, 2017.2878417968750, 1142.3616943359375, 2017.3471679687500, 1142.3854980468750, 2017.4158935546875);
|
||||
// path.cubicTo(1142.4211425781250, 2017.5185546875000, 1142.4833984375000, 2017.6420898437500, 1142.5947265625000, 2017.7518310546875);
|
||||
// Segment 1142.5947256938614 2017.7518879047705 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2016.7908281215357
|
||||
path.lineTo(1143.5428466796875, 2016.7907714843750);
|
||||
// Segment 1143.5428101613127 2016.7908281215357 0.0381324473830 0.0447670202574 -0.0119644334077 -0.0236755853369 1143.6525251830094 2016.9513138428831
|
||||
path.cubicTo(1143.5809326171875, 2016.8355712890625, 1143.6405029296875, 2016.9276123046875, 1143.6524658203125, 2016.9512939453125);
|
||||
// Segment 1143.6525251830094 2016.9513138428831 0.0074114058388 0.0224954551713 -0.0007886982052 -0.0013596649622 1143.6741640831724 2017.0232328830039
|
||||
path.cubicTo(1143.6599121093750, 2016.9737548828125, 1143.6733398437500, 2017.0218505859375, 1143.6741943359375, 2017.0231933593750);
|
||||
// Segment 1143.6741640831724 2017.0232328830039 -0.0015261841961 0.0051459911965 0.0000000000000 0.0339197441936 1143.6750000119209 2017.0000000000000
|
||||
path.cubicTo(1143.6726074218750, 2017.0283203125000, 1143.6750488281250, 2017.0339355468750, 1143.6750488281250, 2017.0000000000000);
|
||||
// Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.3249999880791 2017.0000000000000
|
||||
path.lineTo(1142.3249511718750, 2017.0000000000000);
|
||||
path.close();
|
||||
// start loop, contour: 21
|
||||
// Segment 1142.5947256938614 2017.7518879047705 -0.0799271403989 -0.1522613934208 -0.2174629955730 -0.2879403701950 1142.7322615564860 2017.9554062954260
|
||||
path.moveTo(1142.5947265625000, 2017.7518310546875);
|
||||
path.cubicTo(1142.5147705078125, 2017.5996093750000, 1142.5147705078125, 2017.6674804687500, 1142.7322998046875, 2017.9554443359375);
|
||||
// Segment 1142.7322615564860 2017.9554062954260 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460239373 2016.9943465121912
|
||||
path.lineTo(1143.6802978515625, 2016.9943847656250);
|
||||
// Segment 1143.6803460239373 2016.9943465121912 0.0799271403989 0.1522613934208 0.2174629955730 0.2879403701950 1143.5428101613127 2016.7908281215357
|
||||
path.cubicTo(1143.7602539062500, 2017.1466064453125, 1143.7602539062500, 2017.0787353515625, 1143.5428466796875, 2016.7907714843750);
|
||||
// Segment 1143.5428101613127 2016.7908281215357 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2017.7518879047705
|
||||
path.lineTo(1142.5947265625000, 2017.7518310546875);
|
||||
path.close();
|
||||
// start loop, contour: 22
|
||||
// Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864522438 2021.0760227493236
|
||||
path.moveTo(1142.7322998046875, 2017.9554443359375);
|
||||
path.lineTo(1145.8956298828125, 2021.0760498046875);
|
||||
// Segment 1145.8955864522438 2021.0760227493236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
|
||||
path.lineTo(1146.8436279296875, 2020.1149902343750);
|
||||
// Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460057993 2016.9943464942983
|
||||
path.lineTo(1143.6802978515625, 2016.9943847656250);
|
||||
// Segment 1143.6803460057993 2016.9943464942983 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
|
||||
path.lineTo(1142.7322998046875, 2017.9554443359375);
|
||||
path.close();
|
||||
// start loop, contour: 23
|
||||
// Segment 1145.8955864341058 2021.0760227314306 0.2730164534637 0.2693304447891 -0.3016608168437 0.0000000000000 1146.8510041236877 2021.4740112423897
|
||||
path.moveTo(1145.8956298828125, 2021.0760498046875);
|
||||
path.cubicTo(1146.1685791015625, 2021.3453369140625, 1146.5493164062500, 2021.4739990234375, 1146.8509521484375, 2021.4739990234375);
|
||||
// Segment 1146.8510041236877 2021.4740112423897 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2020.1240112185478
|
||||
path.lineTo(1146.8509521484375, 2020.1240234375000);
|
||||
// Segment 1146.8510041236877 2020.1240112185478 -0.0031276099109 0.0031991747760 0.0281856144058 0.0140930868099 1146.8580791488898 2020.1202473991566
|
||||
path.cubicTo(1146.8479003906250, 2020.1271972656250, 1146.8862304687500, 2020.1343994140625, 1146.8580322265625, 2020.1202392578125);
|
||||
// split at 0.3845077157021
|
||||
// path.cubicTo(1146.8497314453125, 2020.1252441406250, 1146.8547363281250, 2020.1270751953125, 1146.8596191406250, 2020.1280517578125);
|
||||
// path.cubicTo(1146.8675537109375, 2020.1296386718750, 1146.8753662109375, 2020.1289062500000, 1146.8580322265625, 2020.1202392578125);
|
||||
// Segment 1146.8580791488898 2020.1202473991566 -0.0369995545027 -0.0123195805663 0.0067223483810 0.0136883790721 1146.8436709015571 2020.1149629481959
|
||||
path.cubicTo(1146.8210449218750, 2020.1079101562500, 1146.8503417968750, 2020.1286621093750, 1146.8436279296875, 2020.1149902343750);
|
||||
// Segment 1146.8436709015571 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864341058 2021.0760227314306
|
||||
path.lineTo(1145.8956298828125, 2021.0760498046875);
|
||||
path.close();
|
||||
// start loop, contour: 24
|
||||
// Segment 1146.8510041236877 2021.4740112423897 0.3016605789999 0.0000000000000 -0.2730166120260 0.2693306012106 1147.8064220516883 2021.0760227314306
|
||||
path.moveTo(1146.8509521484375, 2021.4739990234375);
|
||||
path.cubicTo(1147.1527099609375, 2021.4739990234375, 1147.5334472656250, 2021.3453369140625, 1147.8063964843750, 2021.0760498046875);
|
||||
// Segment 1147.8064220516883 2021.0760227314306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1149629481959
|
||||
path.lineTo(1146.8583984375000, 2020.1149902343750);
|
||||
// Segment 1146.8583375842370 2020.1149629481959 -0.0067222671256 0.0136883164611 0.0369996293611 -0.0123196021258 1146.8439293663473 2020.1202473404985
|
||||
path.cubicTo(1146.8515625000000, 2020.1286621093750, 1146.8809814453125, 2020.1079101562500, 1146.8438720703125, 2020.1202392578125);
|
||||
// Segment 1146.8439293663473 2020.1202473404985 -0.0281857033438 0.0140931104690 0.0031276541428 0.0031991704542 1146.8510041236877 2020.1240112185478
|
||||
path.cubicTo(1146.8157958984375, 2020.1343994140625, 1146.8541259765625, 2020.1271972656250, 1146.8509521484375, 2020.1240234375000);
|
||||
// Segment 1146.8510041236877 2020.1240112185478 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2021.4740112423897
|
||||
path.lineTo(1146.8509521484375, 2021.4739990234375);
|
||||
path.close();
|
||||
// start loop, contour: 25
|
||||
// Segment 1147.8064220516883 2021.0760227314306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2019.1652101405787
|
||||
path.moveTo(1147.8063964843750, 2021.0760498046875);
|
||||
path.cubicTo(1148.3494873046875, 2020.5402832031250, 1148.3494873046875, 2019.7009277343750, 1147.8063964843750, 2019.1651611328125);
|
||||
// Segment 1147.8064220516883 2019.1652101405787 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1262699238134
|
||||
path.lineTo(1146.8583984375000, 2020.1262207031250);
|
||||
// Segment 1146.8583375842370 2020.1262699238134 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2020.1149629481959
|
||||
path.cubicTo(1146.8654785156250, 2020.1333007812500, 1146.8654785156250, 2020.1079101562500, 1146.8583984375000, 2020.1149902343750);
|
||||
// Segment 1146.8583375842370 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2021.0760227314306
|
||||
path.lineTo(1147.8063964843750, 2021.0760498046875);
|
||||
path.close();
|
||||
// start loop, contour: 26
|
||||
// Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519520594 2017.1978648896866
|
||||
path.moveTo(1147.8063964843750, 2019.1651611328125);
|
||||
path.lineTo(1145.8121337890625, 2017.1978759765625);
|
||||
// Segment 1145.8121519520594 2017.1978648896866 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
|
||||
path.lineTo(1144.8640136718750, 2018.1589355468750);
|
||||
// Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375975775 2020.1262699369736
|
||||
path.lineTo(1146.8583984375000, 2020.1262207031250);
|
||||
// Segment 1146.8583375975775 2020.1262699369736 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
|
||||
path.lineTo(1147.8063964843750, 2019.1651611328125);
|
||||
path.close();
|
||||
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads66(skiatest::Reporter* reporter,const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(2, 0);
|
||||
path.quadTo(3, 1, 2, 2);
|
||||
path.lineTo(2, 3);
|
||||
path.close();
|
||||
path.moveTo(2, 1);
|
||||
path.lineTo(2, 1);
|
||||
path.quadTo(1, 2, 2, 2);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads67(skiatest::Reporter* reporter,const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(3, 2);
|
||||
path.quadTo(1, 3, 3, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.close();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(1, 0);
|
||||
path.quadTo(2, 3, 3, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads68(skiatest::Reporter* reporter,const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(1, 2);
|
||||
path.quadTo(0, 3, 2, 3);
|
||||
path.lineTo(2, 3);
|
||||
path.close();
|
||||
path.moveTo(1, 0);
|
||||
path.lineTo(0, 1);
|
||||
path.quadTo(1, 3, 2, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads69(skiatest::Reporter* reporter,const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(1, 0);
|
||||
path.quadTo(2, 2, 2, 3);
|
||||
path.lineTo(2, 3);
|
||||
path.close();
|
||||
path.moveTo(1, 0);
|
||||
path.lineTo(1, 0);
|
||||
path.quadTo(3, 0, 1, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads70(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(1, 1);
|
||||
path.quadTo(2, 3, 3, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.close();
|
||||
path.moveTo(2, 0);
|
||||
path.lineTo(2, 2);
|
||||
path.quadTo(1, 3, 3, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads71(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(1, 1);
|
||||
path.quadTo(2, 3, 3, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.close();
|
||||
path.moveTo(3, 0);
|
||||
path.lineTo(2, 2);
|
||||
path.quadTo(1, 3, 3, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void testQuads72(skiatest::Reporter* reporter, const char* filename) {
|
||||
SkPath path;
|
||||
path.moveTo(1, 1);
|
||||
path.quadTo(2, 3, 3, 3);
|
||||
path.lineTo(3, 3);
|
||||
path.close();
|
||||
path.moveTo(0, 1);
|
||||
path.lineTo(2, 2);
|
||||
path.quadTo(1, 3, 3, 3);
|
||||
path.close();
|
||||
testSimplify(reporter, path, filename);
|
||||
}
|
||||
|
||||
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
@ -5109,8 +5785,20 @@ static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
|
||||
|
||||
static TestDesc tests[] = {
|
||||
TEST(testQuads72),
|
||||
TEST(testQuads71),
|
||||
TEST(testQuads70),
|
||||
TEST(testQuads69),
|
||||
TEST(testQuads68),
|
||||
TEST(testQuads67),
|
||||
TEST(testQuads66),
|
||||
TEST(dean4),
|
||||
TEST(tiger8a_h_1),
|
||||
TEST(tiger8a_h),
|
||||
TEST(tiger8a),
|
||||
TEST(tiger8b),
|
||||
TEST(tiger8),
|
||||
TEST(fuzz763_4713_b),
|
||||
TEST(fuzz_59),
|
||||
TEST(fuzz_twister2),
|
||||
TEST(fuzz_twister),
|
||||
TEST(fuzz994s_3414),
|
||||
|
@ -6,51 +6,18 @@
|
||||
<title></title>
|
||||
<div style="height:0">
|
||||
|
||||
<div id="sect0">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
<div id="angle">
|
||||
{{{2, 6, 1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f}}}
|
||||
{{{1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6}}}
|
||||
{{{1.995156049728393555, 5.980457782745361328}, {2.08147298604749853, 5.917692615073925744}, {2.164281118403629023, 5.850987095630077128}, {2.242042064666748047, 5.780299663543701172}}}
|
||||
{{{1.995156049728393555, 5.980457782745361328}, {1.82665117196054072, 6.185735619599722845}, {1.80264212281170999, 5.19703332512428684}, {1.994958639144897461, 5.979661464691162109}}}
|
||||
{{{1.995156049728393555, 5.980457782745361328}, {1.825196881732315868, 6.187507280789372288}, {1.801190554235020613, 5.204762216940081565}, {2, 6}}}
|
||||
</div>
|
||||
|
||||
<div id="sect1">
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f} id=1
|
||||
{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f} id=2
|
||||
{{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f} id=4
|
||||
</div>
|
||||
|
||||
<div id="sect2">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f} id=3
|
||||
{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f} id=2
|
||||
</div>
|
||||
|
||||
<div id="sect3">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f} id=3
|
||||
{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f} id=6
|
||||
</div>
|
||||
|
||||
<div id="sect4">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f} id=3
|
||||
{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f} id=6
|
||||
</div>
|
||||
|
||||
<div id="sect5">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f} id=3
|
||||
{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f} id=6
|
||||
</div>
|
||||
|
||||
<div id="sect6">
|
||||
{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
|
||||
{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
|
||||
{{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f} id=3
|
||||
{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f} id=6
|
||||
<div id="ref">
|
||||
{{{0.7153972983360290527, 4.712343692779541016}, {0.2472269223126296878, 4.55502436068874772}, {-0.1220090791716240131, 4.244018092892478222}, {0, 4}}},
|
||||
{{{0.7153972983360290527, 4.712343692779541016}, {0.1339736781445877156, 4.133975051508096854}, {0.7320473976783675729, 3.63397630116081638}, {2, 3}}},
|
||||
{{fX=-0.0012699038296868359 fY=-0.0012605104293301750 } {fX=-0.0025337575085910835 fY=-0.0025229424048465177 }
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -58,13 +25,8 @@
|
||||
<script type="text/javascript">
|
||||
|
||||
var testDivs = [
|
||||
sect0,
|
||||
sect1,
|
||||
sect2,
|
||||
sect3,
|
||||
sect4,
|
||||
sect5,
|
||||
sect6,
|
||||
angle,
|
||||
ref,
|
||||
];
|
||||
|
||||
var decimal_places = 3;
|
||||
@ -499,7 +461,7 @@ function dxy_at_t(curve, t) {
|
||||
function drawPointAtT(curve) {
|
||||
var x = x_at_t(curve, curveT);
|
||||
var y = y_at_t(curve, curveT);
|
||||
drawPoint(x, y);
|
||||
drawPoint(x, y, false);
|
||||
}
|
||||
|
||||
function drawLine(x1, y1, x2, y2) {
|
||||
@ -511,7 +473,7 @@ function dxy_at_t(curve, t) {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawPoint(px, py) {
|
||||
function drawPoint(px, py, xend) {
|
||||
for (var pts = 0; pts < drawnPts.length; pts += 2) {
|
||||
var x = drawnPts[pts];
|
||||
var y = drawnPts[pts + 1];
|
||||
@ -524,8 +486,15 @@ function dxy_at_t(curve, t) {
|
||||
var _px = (px - srcLeft) * scale;
|
||||
var _py = (py - srcTop) * scale;
|
||||
ctx.beginPath();
|
||||
ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
|
||||
ctx.closePath();
|
||||
if (xend) {
|
||||
ctx.moveTo(_px - 3, _py - 3);
|
||||
ctx.lineTo(_px + 3, _py + 3);
|
||||
ctx.moveTo(_px - 3, _py + 3);
|
||||
ctx.lineTo(_px + 3, _py - 3);
|
||||
} else {
|
||||
ctx.arc(_px, _py, 3, 0, Math.PI * 2, true);
|
||||
ctx.closePath();
|
||||
}
|
||||
ctx.stroke();
|
||||
if (draw_point_xy) {
|
||||
var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
|
||||
@ -537,7 +506,7 @@ function dxy_at_t(curve, t) {
|
||||
}
|
||||
|
||||
function drawPointSolid(px, py) {
|
||||
drawPoint(px, py);
|
||||
drawPoint(px, py, false);
|
||||
ctx.fillStyle = "rgba(0,0,0, 0.4)";
|
||||
ctx.fill();
|
||||
}
|
||||
@ -814,15 +783,17 @@ function dxy_at_t(curve, t) {
|
||||
}
|
||||
ctx.stroke();
|
||||
if (draw_endpoints > 0) {
|
||||
drawPoint(curve[0], curve[1]);
|
||||
drawPoint(curve[0], curve[1], false);
|
||||
if (draw_endpoints > 1 || curve.length == 4) {
|
||||
drawPoint(curve[2], curve[3]);
|
||||
drawPoint(curve[2], curve[3], curve.length == 4 && draw_endpoints == 3);
|
||||
}
|
||||
if (curve.length == 6 || curve.length == 7 ||
|
||||
(draw_endpoints > 1 && curve.length == 8)) {
|
||||
drawPoint(curve[4], curve[5]);
|
||||
drawPoint(curve[4], curve[5], (curve.length == 6 || curve.length == 7) && draw_endpoints == 3);
|
||||
}
|
||||
if (curve.length == 8) {
|
||||
drawPoint(curve[6], curve[7], curve.length == 8 && draw_endpoints == 3);
|
||||
}
|
||||
if (curve.length == 8) drawPoint(curve[6], curve[7]);
|
||||
}
|
||||
if (draw_midpoint != 0) {
|
||||
if ((curves == 0) == (midLeft == 0)) {
|
||||
@ -1168,7 +1139,7 @@ function dxy_at_t(curve, t) {
|
||||
redraw();
|
||||
break;
|
||||
case 'e':
|
||||
draw_endpoints = (draw_endpoints + 1) % 3;
|
||||
draw_endpoints = (draw_endpoints + 1) % 4;
|
||||
redraw();
|
||||
break;
|
||||
case 'f':
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user