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:
caryclark 2016-07-18 10:01:36 -07:00 committed by Commit bot
parent 6451a0cea6
commit 55888e4417
49 changed files with 6052 additions and 2979 deletions

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
}

View File

@ -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
}

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);
};

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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
}

View File

@ -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

View File

@ -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);

View File

@ -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));
}

View File

@ -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>

View File

@ -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)) {

View File

@ -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
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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

View File

@ -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];

View File

@ -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("");
}
}

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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 );

View File

@ -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[] = {

View File

@ -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[] = {

View File

@ -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);
}

View File

@ -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);

View File

@ -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),

View File

@ -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