fix minor skp-found bugs

remove globals from pathops_unittest

BUG=skia:2460
TBR=mtklein

Author: caryclark@google.com

Review URL: https://codereview.chromium.org/239563004

git-svn-id: http://skia.googlecode.com/svn/trunk@14378 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-04-25 12:59:11 +00:00
parent e1ba93ee01
commit 8cb1daaa1e
29 changed files with 1869 additions and 211 deletions

View File

@ -24,6 +24,7 @@
'sources': [
'../tests/PathOpsAngleIdeas.cpp',
'../tests/PathOpsDebug.cpp',
'../tests/PathOpsOpLoopThreadedTest.cpp',
'../tests/PathOpsSkpClipTest.cpp',
'../tests/Test.cpp',
'../tests/skia_test.cpp',

View File

@ -494,7 +494,18 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
cubicNearEnd(c1, false, c2, c2Bounds);
}
if (!(exactEndBits & 8)) {
if (selfIntersect && fUsed) {
return fUsed;
}
cubicNearEnd(c1, true, c2, c2Bounds);
if (selfIntersect && fUsed && ((approximately_less_than_zero(fT[0][0])
&& approximately_less_than_zero(fT[1][0]))
|| (approximately_greater_than_one(fT[0][0])
&& approximately_greater_than_one(fT[1][0])))) {
SkASSERT(fUsed == 1);
fUsed = 0;
return fUsed;
}
}
if (!selfIntersect) {
SkDRect c1Bounds;

View File

@ -292,7 +292,7 @@ int SkIntersections::vertical(const SkDLine& line, double x) {
int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
double x, bool flipped) {
fMax = 2;
fMax = 3; // cleanup parallel lines will bring this back line
// see if end points intersect the opposite line
double t;
SkDPoint topPt = { x, top };
@ -344,6 +344,7 @@ int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
}
}
cleanUpParallelLines(result == 2);
SkASSERT(fUsed <= 2);
return fUsed;
}

View File

@ -98,7 +98,7 @@ public:
, fLine(l)
, fIntersections(i)
, fAllowNear(true) {
i->setMax(2);
i->setMax(3); // allow short partial coincidence plus discrete intersection
}
void allowNear(bool allow) {
@ -331,6 +331,9 @@ protected:
*pt = fLine[1];
*lineT = 1;
}
if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) {
return false;
}
if (gridPt == fQuad[0].asSkPoint()) {
*pt = fQuad[0];
*quadT = 0;

View File

@ -164,7 +164,7 @@ public:
quad.set(a);
SkDLine line;
line.set(b);
fMax = 2;
fMax = 3; // 2; permit small coincident segment + non-coincident intersection
return intersect(quad, line);
}

View File

@ -321,9 +321,11 @@ recomputeSector:
fUnorderable = true;
return false;
}
int saveEnd = fEnd;
fEnd = checkEnd - step;
setSpans();
setSector();
fEnd = saveEnd;
return !fUnorderable;
}
@ -658,6 +660,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
}
SkOpAngle* next = fNext;
if (next->fNext == this) {
if (angle->overlap(*this)) {
return;
}
if (singleton || angle->after(this)) {
this->fNext = angle;
angle->fNext = next;
@ -671,6 +676,9 @@ void SkOpAngle::insert(SkOpAngle* angle) {
SkOpAngle* last = this;
do {
SkASSERT(last->fNext == next);
if (angle->overlap(*last) || angle->overlap(*next)) {
return;
}
if (angle->after(last)) {
last->fNext = angle;
angle->fNext = next;
@ -701,6 +709,33 @@ SkOpSpan* SkOpAngle::lastMarked() const {
return fLastMarked;
}
bool SkOpAngle::loopContains(const SkOpAngle& test) const {
if (!fNext) {
return false;
}
const SkOpAngle* first = this;
const SkOpAngle* loop = this;
const SkOpSegment* tSegment = test.fSegment;
double tStart = tSegment->span(test.fStart).fT;
double tEnd = tSegment->span(test.fEnd).fT;
do {
const SkOpSegment* lSegment = loop->fSegment;
// FIXME : use precisely_equal ? or compare points exactly ?
if (lSegment != tSegment) {
continue;
}
double lStart = lSegment->span(loop->fStart).fT;
if (lStart != tEnd) {
continue;
}
double lEnd = lSegment->span(loop->fEnd).fT;
if (lEnd == tStart) {
return true;
}
} while ((loop = loop->fNext) != first);
return false;
}
int SkOpAngle::loopCount() const {
int count = 0;
const SkOpAngle* first = this;
@ -813,6 +848,23 @@ unorderable:
return true;
}
bool SkOpAngle::overlap(const SkOpAngle& other) const {
int min = SkTMin(fStart, fEnd);
const SkOpSpan& span = fSegment->span(min);
const SkOpSegment* oSeg = other.fSegment;
int oMin = SkTMin(other.fStart, other.fEnd);
const SkOpSpan& oSpan = oSeg->span(oMin);
if (!span.fSmall && !oSpan.fSmall) {
return false;
}
if (fSegment->span(fStart).fPt != oSeg->span(other.fStart).fPt) {
return false;
}
// see if small span is contained by opposite span
return span.fSmall ? oSeg->containsPt(fSegment->span(fEnd).fPt, other.fEnd, other.fStart)
: fSegment->containsPt(oSeg->span(other.fEnd).fPt, fEnd, fStart);
}
// OPTIMIZE: if this shows up in a profile, add a previous pointer
// as is, this should be rarely called
SkOpAngle* SkOpAngle::previous() const {

View File

@ -24,6 +24,7 @@ public:
kBinaryOpp,
};
int end() const {
return fEnd;
}
@ -37,6 +38,7 @@ public:
void insert(SkOpAngle* );
bool isHorizontal() const;
SkOpSpan* lastMarked() const;
bool loopContains(const SkOpAngle& ) const;
int loopCount() const;
void markStops();
bool merge(SkOpAngle* );
@ -104,6 +106,7 @@ private:
double midT() const;
bool oppositePlanes(const SkOpAngle& rh) const;
bool orderable(const SkOpAngle& rh) const; // false == this < rh ; true == this > rh
bool overlap(const SkOpAngle& test) const;
void setCurveHullSweep();
void setSector();
void setSpans();

View File

@ -211,9 +211,12 @@ void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden
}
bool swapStart = startT > endT;
bool swapOther = oStartT > oEndT;
const SkPoint* startPt = &coincidence.fPts[0];
const SkPoint* endPt = &coincidence.fPts[1];
if (swapStart) {
SkTSwap<double>(startT, endT);
SkTSwap<double>(oStartT, oEndT);
SkTSwap(startT, endT);
SkTSwap(oStartT, oEndT);
SkTSwap(startPt, endPt);
}
bool cancel = swapOther != swapStart;
int step = swapStart ? -1 : 1;
@ -222,17 +225,18 @@ void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coinciden
if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
bool added = false;
if (oMatchStart != 0) {
added = thisOne.joinCoincidence(&other, oMatchStart, oStep, cancel);
const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
}
if (!cancel && startT != 0 && !added) {
(void) other.joinCoincidence(&thisOne, startT, step, cancel);
(void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
}
}
double oMatchEnd = cancel ? oStartT : oEndT;
if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
bool added = false;
if (cancel && endT != 1 && !added) {
(void) other.joinCoincidence(&thisOne, endT, -step, cancel);
(void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
}
}
}
@ -329,7 +333,7 @@ void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
continue;
}
fDone = false;
SkPoint testXY = testSegment->activeLeftTop(true, NULL);
SkPoint testXY = testSegment->activeLeftTop(NULL);
if (*topStart) {
if (testXY.fY < topLeft.fY) {
continue;

View File

@ -13,7 +13,7 @@ void SkOpEdgeBuilder::init() {
fOperand = false;
fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
: kWinding_PathOpsMask;
#ifdef SK_DEBUG
#if defined(SK_DEBUG) || !FORCE_RELEASE
SkPathOpsDebug::gContourID = 0;
SkPathOpsDebug::gSegmentID = 0;
#endif

View File

@ -70,16 +70,12 @@ const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end,
int next = nextExactSpan(index, 1);
if (next > 0) {
const SkOpSpan& upSpan = fTs[index];
if (upSpan.fUnsortableStart) {
*sortable = false;
return NULL;
}
if (upSpan.fWindValue || upSpan.fOppValue) {
if (*end < 0) {
*start = index;
*end = next;
}
if (!upSpan.fDone && !upSpan.fUnsortableEnd) {
if (!upSpan.fDone) {
if (upSpan.fWindSum != SK_MinS32) {
return spanToAngle(index, next);
}
@ -93,10 +89,6 @@ const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end,
// edge leading into junction
if (prev >= 0) {
const SkOpSpan& downSpan = fTs[prev];
if (downSpan.fUnsortableEnd) {
*sortable = false;
return NULL;
}
if (downSpan.fWindValue || downSpan.fOppValue) {
if (*end < 0) {
*start = index;
@ -123,19 +115,15 @@ const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end,
return other->activeAngleInner(oIndex, start, end, done, sortable);
}
SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
SkASSERT(!done());
SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
int count = fTs.count();
// see if either end is not done since we want smaller Y of the pair
bool lastDone = true;
bool lastUnsortable = false;
double lastT = -1;
for (int index = 0; index < count; ++index) {
const SkOpSpan& span = fTs[index];
if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
goto next;
}
if (span.fDone && lastDone) {
goto next;
}
@ -164,7 +152,6 @@ SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
}
next:
lastDone = span.fDone;
lastUnsortable = span.fUnsortableEnd;
}
return topPt;
}
@ -345,16 +332,19 @@ void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
do {
workPt = &fTs[++tIndex].fPt;
} while (nextPt == *workPt);
const SkPoint* oWorkPt;
do {
workPt = &other->fTs[++oIndex].fPt;
} while (nextPt == *workPt);
oWorkPt = &other->fTs[++oIndex].fPt;
} while (nextPt == *oWorkPt);
nextPt = *workPt;
double tStart = fTs[tIndex].fT;
double oStart = other->fTs[oIndex].fT;
if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
break;
}
addTPair(tStart, other, oStart, false, nextPt);
if (*workPt == *oWorkPt) {
addTPair(tStart, other, oStart, false, nextPt);
}
} while (endPt != nextPt);
}
@ -618,8 +608,6 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
span->fLoop = false;
span->fSmall = false;
span->fTiny = false;
span->fUnsortableStart = false;
span->fUnsortableEnd = false;
int less = -1;
// find range of spans with nearly the same point as this one
while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
@ -834,18 +822,27 @@ bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
aligned = true;
}
double oT = oSpan->fT;
if (oT == 0 || oT == 1) {
if (oT == 0) {
return aligned;
}
int oStart = other->nextSpan(oIndex, -1) + 1;
int oEnd = other->nextSpan(oIndex, 1);
oSpan = &other->fTs[oStart];
int otherIndex = oStart;
if (oT == 1) {
if (aligned) {
while (oSpan->fPt == thisPt && oSpan->fT != 1) {
oSpan->fTiny = true;
++oSpan;
}
}
return aligned;
}
oT = oSpan->fT;
int oEnd = other->nextSpan(oIndex, 1);
bool oAligned = false;
if (oSpan->fPt != thisPt) {
oAligned |= other->alignSpan(oStart, oT, thisPt);
}
int otherIndex = oStart;
while (++otherIndex < oEnd) {
SkOpSpan* oNextSpan = &other->fTs[otherIndex];
if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
@ -1352,14 +1349,17 @@ void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* ne
nextAngle->setLastMarked(last);
}
void SkOpSegment::constructLine(SkPoint shortLine[2]) {
addLine(shortLine, false, false);
addT(NULL, shortLine[0], 0);
addT(NULL, shortLine[1], 1);
addStartSpan(1);
addEndSpan(1);
SkOpAngle& angle = fAngles.push_back();
angle.set(this, 0, 1);
bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
int step = index < endIndex ? 1 : -1;
do {
const SkOpSpan& span = this->span(index);
if (span.fPt == pt) {
const SkOpSpan& endSpan = this->span(endIndex);
return span.fT == endSpan.fT && pt != endSpan.fPt;
}
index += step;
} while (index != endIndex);
return false;
}
int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
@ -1923,7 +1923,7 @@ nextSmallCheck:
missing.fPt)) {
continue;
}
int otherTIndex = missingOther->findT(missing.fOtherT, missing.fSegment);
int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
if (otherSpan.fSmall) {
const SkOpSpan* nextSpan = &otherSpan;
@ -1955,7 +1955,9 @@ nextSmallCheck:
void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
SkTArray<MissingSpan, true>* checkMultiple) {
SkASSERT(span.fSmall);
SkASSERT(span.fWindValue);
if (0 && !span.fWindValue) {
return;
}
SkASSERT(&span < fTs.end() - 1);
const SkOpSpan* next = &span + 1;
SkASSERT(!next->fSmall || checkMultiple);
@ -2271,11 +2273,13 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
SkOpAngle* angle = spanToAngle(end, startIndex);
if (angle->unorderable()) {
*unsortable = true;
markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
#if DEBUG_SORT
@ -2283,6 +2287,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
angle->debugLoop();
#endif
int sumMiWinding = updateWinding(endIndex, startIndex);
if (sumMiWinding == SK_MinS32) {
*unsortable = true;
markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
int sumSuWinding = updateOppWinding(endIndex, startIndex);
if (operand()) {
SkTSwap<int>(sumMiWinding, sumSuWinding);
@ -2302,6 +2311,7 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
if (!foundAngle || (foundDone && activeCount & 1)) {
if (nextSegment->isTiny(nextAngle)) {
*unsortable = true;
markDoneBinary(SkMin32(startIndex, endIndex));
return NULL;
}
foundAngle = nextAngle;
@ -2393,6 +2403,7 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
bool sortable = calcWinding != SK_NaN32;
if (!sortable) {
*unsortable = true;
markDoneUnary(SkMin32(startIndex, endIndex));
return NULL;
}
SkOpAngle* angle = spanToAngle(end, startIndex);
@ -2415,6 +2426,7 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
if (!foundAngle || (foundDone && activeCount & 1)) {
if (nextSegment->isTiny(nextAngle)) {
*unsortable = true;
markDoneUnary(SkMin32(startIndex, endIndex));
return NULL;
}
foundAngle = nextAngle;
@ -2433,7 +2445,6 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
SkOpSpan* last = nextAngle->lastMarked();
if (last) {
SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
// assert here that span isn't already in array
*chase->append() = last;
#if DEBUG_WINDING
SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
@ -2584,7 +2595,7 @@ int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
return -1;
}
int SkOpSegment::findT(double t, const SkOpSegment* match) const {
int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
int count = this->count();
for (int index = 0; index < count; ++index) {
const SkOpSpan& span = fTs[index];
@ -2592,18 +2603,28 @@ int SkOpSegment::findT(double t, const SkOpSegment* match) const {
return index;
}
}
// Usually, the pair of ts are an exact match. It's possible that the t values have
// been adjusted to make multiple intersections align. In this rare case, look for a
// matching point / match pair instead.
for (int index = 0; index < count; ++index) {
const SkOpSpan& span = fTs[index];
if (span.fPt == pt && span.fOther == match) {
return index;
}
}
SkASSERT(0);
return -1;
}
SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable) {
SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
bool firstPass) {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
SkASSERT(!done());
int firstT = -1;
/* SkPoint topPt = */ activeLeftTop(true, &firstT);
/* SkPoint topPt = */ activeLeftTop(&firstT);
if (firstT < 0) {
*unsortable = true;
*unsortable = !firstPass;
firstT = 0;
while (fTs[firstT].fDone) {
SkASSERT(firstT < fTs.count());
@ -2655,14 +2676,24 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
#endif
// skip edges that have already been processed
angle = firstAngle;
SkOpSegment* leftSegment;
SkOpSegment* leftSegment = NULL;
bool looped = false;
do {
// SkASSERT(!angle->unsortable());
leftSegment = angle->segment();
*tIndexPtr = angle->end();
*endIndexPtr = angle->start();
*unsortable = angle->unorderable();
if (firstPass || !*unsortable) {
leftSegment = angle->segment();
*tIndexPtr = angle->end();
*endIndexPtr = angle->start();
if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
break;
}
}
angle = angle->next();
} while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone);
looped = true;
} while (angle != firstAngle);
if (angle == firstAngle && looped) {
return NULL;
}
if (leftSegment->verb() >= SkPath::kQuad_Verb) {
const int tIndex = *tIndexPtr;
const int endIndex = *endIndexPtr;
@ -2670,8 +2701,9 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
&& !leftSegment->serpentine(tIndex, endIndex);
#if DEBUG_SWAP_TOP
SkDebugf("%s swap=%d serpentine=%d containedByEnds=%d monotonic=%d\n", __FUNCTION__,
swap,
SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
__FUNCTION__,
swap, leftSegment->debugInflections(tIndex, endIndex),
leftSegment->serpentine(tIndex, endIndex),
leftSegment->controlsContainedByEnds(tIndex, endIndex),
leftSegment->monotonicInY(tIndex, endIndex));
@ -2840,13 +2872,6 @@ bool SkOpSegment::isSimple(int end) const {
#endif
}
bool SkOpSegment::isSmall(const SkOpAngle* angle) const {
int start = angle->start();
int end = angle->end();
const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
return mSpan.fSmall;
}
bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
int start = angle->start();
int end = angle->end();
@ -2863,8 +2888,9 @@ bool SkOpSegment::isTiny(int index) const {
// if both are active, look to see if they both the connect to another coincident pair
// if at least one is a line, then make the pair coincident
// if neither is a line, test for coincidence
bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, int step, bool cancel) {
int otherTIndex = other->findT(otherT, this);
bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
int step, bool cancel) {
int otherTIndex = other->findT(otherT, otherPt, this);
int next = other->nextExactSpan(otherTIndex, step);
int otherMin = SkMin32(otherTIndex, next);
int otherWind = other->span(otherMin).fWindValue;
@ -3106,7 +3132,9 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
debugShowNewWinding(funName, span, winding);
#endif
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
#endif
span.fWindSum = winding;
return &span;
}
@ -3121,10 +3149,14 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
debugShowNewWinding(funName, span, winding, oppWinding);
#endif
SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
#endif
span.fWindSum = winding;
SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
span.fOppSum = oppWinding;
debugValidate();
return &span;
@ -3157,9 +3189,7 @@ bool SkOpSegment::clockwise(int tStart, int tEnd) const {
}
bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
if (fVerb == SkPath::kLine_Verb) {
return false;
}
SkASSERT(fVerb != SkPath::kLine_Verb);
if (fVerb == SkPath::kQuad_Verb) {
SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
return dst.monotonicInY();
@ -3210,33 +3240,6 @@ SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
return &span;
}
// note that just because a span has one end that is unsortable, that's
// not enough to mark it done. The other end may be sortable, allowing the
// span to be added.
// FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
void SkOpSegment::markUnsortable(int start, int end) {
SkOpSpan* span = &fTs[start];
if (start < end) {
#if DEBUG_UNSORTABLE
debugShowNewWinding(__FUNCTION__, *span, 0);
#endif
span->fUnsortableStart = true;
} else {
--span;
#if DEBUG_UNSORTABLE
debugShowNewWinding(__FUNCTION__, *span, 0);
#endif
span->fUnsortableEnd = true;
}
if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
debugValidate();
return;
}
span->fDone = true;
fDoneSpans++;
debugValidate();
}
void SkOpSegment::markWinding(int index, int winding) {
// SkASSERT(!done());
SkASSERT(winding);
@ -3426,8 +3429,10 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int*
*oppMaxWinding = *sumSuWinding;
*oppSumWinding = *sumSuWinding -= oppDeltaSum;
}
SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
}
void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
@ -3435,7 +3440,9 @@ void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
int deltaSum = spanSign(index, endIndex);
*maxWinding = *sumMiWinding;
*sumWinding = *sumMiWinding -= deltaSum;
SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
#if DEBUG_LIMIT_WIND_SUM
SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
#endif
}
void SkOpSegment::sortAngles() {
@ -3494,7 +3501,10 @@ void SkOpSegment::sortAngles() {
wroteAfterHeader = true;
}
#endif
baseAngle->insert(&other->angle(otherAngleIndex));
SkOpAngle* oAngle = &other->angle(otherAngleIndex);
if (!oAngle->loopContains(*baseAngle)) {
baseAngle->insert(oAngle);
}
}
otherAngleIndex = oSpan.fToAngleIndex;
if (otherAngleIndex >= 0) {
@ -3505,7 +3515,10 @@ void SkOpSegment::sortAngles() {
wroteAfterHeader = true;
}
#endif
baseAngle->insert(&other->angle(otherAngleIndex));
SkOpAngle* oAngle = &other->angle(otherAngleIndex);
if (!oAngle->loopContains(*baseAngle)) {
baseAngle->insert(oAngle);
}
}
if (++index == spanCount) {
break;
@ -3673,6 +3686,9 @@ int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
int SkOpSegment::updateWinding(int index, int endIndex) const {
int lesser = SkMin32(index, endIndex);
int winding = windSum(lesser);
if (winding == SK_MinS32) {
return winding;
}
int spanWinding = spanSign(index, endIndex);
if (winding && UseInnerWinding(winding - spanWinding, winding)
&& winding != SK_MaxS32) {

View File

@ -48,8 +48,6 @@ public:
return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
}
void constructLine(SkPoint shortLine[2]);
int count() const {
return fTs.count();
}
@ -193,11 +191,6 @@ public:
return const_cast<SkOpAngle*>(cAngle);
}
// OPTIMIZATION: mark as debugging only if used solely by tests
const SkTDArray<SkOpSpan>& spans() const {
return fTs;
}
int spanSign(const SkOpAngle* angle) const {
SkASSERT(angle->segment() == this);
return spanSign(angle->start(), angle->end());
@ -219,10 +212,6 @@ public:
return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
}
bool unsortable(int index) const {
return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
}
void updatePts(const SkPoint pts[]) {
fPts = pts;
}
@ -267,7 +256,7 @@ public:
const SkOpAngle* activeAngle(int index, int* start, int* end, bool* done,
bool* sortable) const;
SkPoint activeLeftTop(bool onlySortable, int* firstT) const;
SkPoint activeLeftTop(int* firstT) const;
bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op);
bool activeWinding(int index, int endIndex);
void addCubic(const SkPoint pts[4], bool operand, bool evenOdd);
@ -297,6 +286,7 @@ public:
bool checkSmall(int index) const;
void checkTiny();
int computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType);
bool containsPt(const SkPoint& , int index, int endIndex) const;
int crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, bool* hitSomething,
double mid, bool opp, bool current) const;
bool findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, int oEnd,
@ -307,16 +297,16 @@ public:
bool* unsortable);
SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable);
int findExactT(double t, const SkOpSegment* ) const;
int findT(double t, const SkOpSegment* ) const;
SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable);
int findT(double t, const SkPoint& , const SkOpSegment* ) const;
SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool firstPass);
void fixOtherTIndex();
void initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType);
void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
SkScalar hitOppDx);
bool isMissing(double startT, const SkPoint& pt) const;
bool isSmall(const SkOpAngle* angle) const;
bool isTiny(const SkOpAngle* angle) const;
bool joinCoincidence(SkOpSegment* other, double otherT, int step, bool cancel);
bool joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt, int step,
bool cancel);
SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
SkOpSpan* markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding);
@ -361,6 +351,7 @@ public:
#if DEBUG_SHOW_WINDING
int debugShowWindingValues(int slotCount, int ofInterest) const;
#endif
const SkTDArray<SkOpSpan>& debugSpans() const;
void debugValidate() const;
// available to testing only
void dumpAngles() const;
@ -439,7 +430,6 @@ private:
SkOpSpan* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding);
void markWinding(int index, int winding);
void markWinding(int index, int winding, int oppWinding);
void markUnsortable(int start, int end);
bool monotonicInY(int tStart, int tEnd) const;
bool multipleEnds() const {
@ -490,6 +480,9 @@ private:
#if DEBUG_ANGLE
void debugCheckPointsEqualish(int tStart, int tEnd) const;
#endif
#if DEBUG_SWAP_TOP
int debugInflections(int index, int endIndex) const;
#endif
#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);

View File

@ -28,8 +28,6 @@ struct SkOpSpan {
bool fLoop; // set when a cubic loops back to this point
bool fSmall; // if set, consecutive points are almost equal
bool fTiny; // if set, consecutive points are equal but consecutive ts are not precisely equal
bool fUnsortableStart; // set when start is part of an unsortable pair
bool fUnsortableEnd; // set when end is part of an unsortable pair
// available to testing only
const SkOpSegment* debugToSegment(ptrdiff_t* ) const;

View File

@ -206,7 +206,7 @@ void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourList,
int* index, int* endIndex, SkPoint* topLeft, bool* unsortable,
bool* done, bool onlySortable) {
bool* done, bool firstPass) {
SkOpSegment* result;
const SkOpSegment* lastTopStart = NULL;
int lastIndex = -1, lastEndIndex = -1;
@ -238,7 +238,7 @@ static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourL
return NULL;
}
*topLeft = bestXY;
result = topStart->findTop(index, endIndex, unsortable);
result = topStart->findTop(index, endIndex, unsortable, firstPass);
if (!result) {
if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
*done = true;
@ -249,9 +249,11 @@ static SkOpSegment* findSortableTop(const SkTArray<SkOpContour*, true>& contourL
lastEndIndex = *endIndex;
}
} while (!result);
#if 0
if (result) {
*unsortable = false;
}
#endif
return result;
}
@ -283,18 +285,20 @@ static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
if (contour->done()) {
continue;
}
*current = contour->nonVerticalSegment(index, endIndex);
if (*current) {
SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
if (nonVertical) {
*current = nonVertical;
return;
}
}
return;
}
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done) {
int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
SkOpSegment* current = findSortableTop(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
done, true);
done, firstPass);
if (!current) {
return NULL;
}
@ -332,7 +336,7 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
// if only remaining candidates are vertical, then they can be marked done
SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
skipVertical(contourList, &current, indexPtr, endIndexPtr);
SkASSERT(current); // FIXME: if null, all remaining are vertical
SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
tryAgain = false;
contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
@ -348,6 +352,9 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
} while (tryAgain);
current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding,
hitOppDx);
if (current->done()) {
return NULL;
}
return current;
}

View File

@ -18,7 +18,7 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple);
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex);
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& , SkOpAngle::IncludeType ,
bool* firstContour, int* index, int* endIndex, SkPoint* topLeft,
bool* unsortable, bool* done);
bool* unsortable, bool* done, bool firstPass);
SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end);
void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
bool evenOdd, bool oppEvenOdd);

View File

@ -94,6 +94,11 @@ bool SkDCubic::monotonicInY() const {
}
bool SkDCubic::serpentine() const {
#if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicOp53d
double tValues[2];
// OPTIMIZATION : another case where caching the present of cubic inflections would be useful
return findInflections(tValues) > 1;
#endif
if (!controlsContainedByEnds()) {
return false;
}

View File

@ -10,12 +10,12 @@
#if defined SK_DEBUG || !FORCE_RELEASE
int SkPathOpsDebug::gMaxWindSum = SK_MaxS32;
int SkPathOpsDebug::gMaxWindValue = SK_MaxS32;
const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};
#if defined(SK_DEBUG) || !FORCE_RELEASE
int SkPathOpsDebug::gContourID;
int SkPathOpsDebug::gSegmentID;
#endif
#if DEBUG_SORT || DEBUG_SWAP_TOP
int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
@ -393,6 +393,17 @@ bool SkOpSegment::debugContains(const SkOpAngle* angle) const {
}
#endif
#if DEBUG_SWAP_TOP
int SkOpSegment::debugInflections(int tStart, int tEnd) const {
if (fVerb != SkPath::kCubic_Verb) {
return false;
}
SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
double inflections[2];
return dst.findInflections(inflections);
}
#endif
void SkOpSegment::debugReset() {
fTs.reset();
fAngles.reset();

View File

@ -52,6 +52,7 @@
#define DEBUG_CROSS 0
#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 0
#define DEBUG_LIMIT_WIND_SUM 0
#define DEBUG_MARK_DONE 0
#define DEBUG_PATH_CONSTRUCTION 0
#define DEBUG_SHOW_TEST_NAME 0
@ -85,6 +86,7 @@
#define DEBUG_CROSS 01
#define DEBUG_FLAT_QUADS 0
#define DEBUG_FLOW 1
#define DEBUG_LIMIT_WIND_SUM 4
#define DEBUG_MARK_DONE 1
#define DEBUG_PATH_CONSTRUCTION 1
#define DEBUG_SHOW_TEST_NAME 1
@ -96,7 +98,7 @@
#define DEBUG_SORT_SINGLE 0
#define DEBUG_SWAP_TOP 1
#define DEBUG_UNSORTABLE 1
#define DEBUG_VALIDATE 1
#define DEBUG_VALIDATE 0
#define DEBUG_WIND_BUMP 0
#define DEBUG_WINDING 1
#define DEBUG_WINDING_AT_T 1
@ -134,12 +136,12 @@
class SkPathOpsDebug {
public:
static int gMaxWindSum;
static int gMaxWindValue;
static const char* kLVerbStr[];
#if defined(SK_DEBUG) || !FORCE_RELEASE
static int gContourID;
static int gSegmentID;
#endif
#if DEBUG_SORT || DEBUG_SWAP_TOP
static int gSortCountDefault;

View File

@ -21,6 +21,9 @@ static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
*endIndex = -1;
if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
&sortable)) {
if (last->unorderable()) {
continue;
}
*tIndex = last->start();
*endIndex = last->end();
#if TRY_ROTATE
@ -116,21 +119,31 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
bool firstPass = true;
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
int index, endIndex;
bool done;
bool topDone;
lastTopLeft = topLeft;
SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
&index, &endIndex, &topLeft, &topUnsortable, &done);
&index, &endIndex, &topLeft, &topUnsortable, &topDone, firstPass);
if (!current) {
if (topUnsortable || !done) {
topUnsortable = false;
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
if (firstPass) {
firstPass = false;
} else {
break;
}
}
topLeft.fX = topLeft.fY = SK_ScalarMin;
continue;
}
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
SkTDArray<SkOpSpan*> chaseArray;
do {
if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {

View File

@ -13,21 +13,24 @@ static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWrite
bool firstContour = true;
bool unsortable = false;
bool topUnsortable = false;
bool firstPass = true;
SkPoint lastTopLeft;
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
do {
int index, endIndex;
bool topDone;
lastTopLeft = topLeft;
SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
&index, &endIndex, &topLeft, &topUnsortable, &topDone);
&index, &endIndex, &topLeft, &topUnsortable, &topDone, firstPass);
if (!current) {
if (topUnsortable || !topDone) {
topUnsortable = false;
if ((!topUnsortable || firstPass) && !topDone) {
SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
topLeft.fX = topLeft.fY = SK_ScalarMin;
continue;
}
break;
}
firstPass = !topUnsortable || lastTopLeft != topLeft;
SkTDArray<SkOpSpan*> chaseArray;
do {
if (current->activeWinding(index, endIndex)) {

View File

@ -161,8 +161,8 @@ static int check_linear(const SkDCubic& cubic,
while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
--endIndex;
if (endIndex == 0) {
SkDebugf("%s shouldn't get here if all four points are about equal\n", __FUNCTION__);
SkASSERT(0);
endIndex = 3;
break;
}
}
if (!cubic.isLinear(startIndex, endIndex)) {

View File

@ -202,6 +202,10 @@ void SkIntersectionHelper::dump() const {
}
}
const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const {
return fTs;
}
void SkOpSegment::dumpAngles() const {
SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
int fromIndex = -1, toIndex = -1;
@ -371,8 +375,8 @@ const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const {
}
SkASSERT(otherTestT == fOtherT);
const SkOpSegment* candidate = otherSpan.fOther;
const SkOpSpan* first = candidate->spans().begin();
const SkOpSpan* last = candidate->spans().end() - 1;
const SkOpSpan* first = candidate->debugSpans().begin();
const SkOpSpan* last = candidate->debugSpans().end() - 1;
if (first <= this && this <= last) {
if (spanIndex) {
*spanIndex = this - first;
@ -415,12 +419,6 @@ void SkOpSpan::dumpOne() const {
if (fDone) {
SkDebugf(" done");
}
if (fUnsortableStart) {
SkDebugf(" unsortable-start");
}
if (fUnsortableEnd) {
SkDebugf(" unsortable-end");
}
if (fTiny) {
SkDebugf(" tiny");
}

View File

@ -11,7 +11,7 @@
#include "SkCanvas.h"
#include "SkForceLinking.h"
#include "SkMatrix.h"
#include "SkPaint.h"
#include "SkPaint.h"
#include "SkRTConf.h"
#include "SkStream.h"
#include "SkThread.h"
@ -566,8 +566,12 @@ bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char*
}
#if DEBUG_SHOW_TEST_NAME
SK_DECLARE_STATIC_MUTEX(gTestMutex);
void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
const char* testName) {
SkAutoMutexAcquire ac(gTestMutex);
ShowFunctionHeader(testName);
showPath(a, "path", true);
showPath(b, "pathB", true);
@ -661,10 +665,6 @@ int initializeTests(skiatest::Reporter* reporter, const char* test) {
#if 0 // doesn't work yet
SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
SK_CONF_SET("images.png.suppressDecoderWarnings", true);
#endif
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = 4;
SkPathOpsDebug::gMaxWindValue = 4;
#endif
if (reporter->verbose()) {
SkAutoMutexAcquire lock(gMutex);

View File

@ -0,0 +1,103 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "PathOpsExtendedTest.h"
#include "PathOpsThreadedCommon.h"
static void testOpLoopsMain(PathOpsThreadState* data) {
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
SkASSERT(data);
PathOpsThreadState& state = *data;
char pathStr[1024]; // gdb: set print elements 400
bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
if (progress) {
sk_bzero(pathStr, sizeof(pathStr));
}
for (int a = 0 ; a < 6; ++a) {
for (int b = a + 1 ; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1 ; d < 7; ++d) {
// define 4 points that form two lines that often cross; one line is (a, b) (c, d)
SkVector v = {SkIntToScalar(a - c), SkIntToScalar(b - d)};
SkPoint midA = { SkIntToScalar(a * state.fA + c * (6 - state.fA)) / 6,
SkIntToScalar(b * state.fA + d * (6 - state.fA)) / 6 };
SkPoint midB = { SkIntToScalar(a * state.fB + c * (6 - state.fB)) / 6,
SkIntToScalar(b * state.fB + d * (6 - state.fB)) / 6 };
SkPoint endC = { midA.fX + v.fY * state.fC / 3,
midA.fY + v.fX * state.fC / 3 };
SkPoint endD = { midB.fX - v.fY * state.fD / 3,
midB.fY + v.fX * state.fD / 3 };
SkPath pathA, pathB;
if (progress) {
char* str = pathStr;
str += sprintf(str, " path.moveTo(%d,%d);\n", a, b);
str += sprintf(str, " path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
c, d, endC.fX, endC.fY, endD.fX, endD.fY);
str += sprintf(str, " path.close();\n");
str += sprintf(str, " pathB.moveTo(%d,%d);\n", c, d);
str += sprintf(str, " pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n",
endC.fX, endC.fY, endD.fX, endD.fY, a, b);
str += sprintf(str, " pathB.close();\n");
}
pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b));
pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY);
pathA.close();
pathB.moveTo(SkIntToScalar(c), SkIntToScalar(d));
pathB.cubicTo(endC.fX, endC.fY, endD.fX, endD.fY, SkIntToScalar(a), SkIntToScalar(b));
pathB.close();
// SkDebugf("%s\n", pathStr);
if (progress) {
outputProgress(state.fPathStr, pathStr, kIntersect_PathOp);
}
testThreadedPathOp(state.fReporter, pathA, pathB, kIntersect_PathOp, "loops");
}
}
}
}
}
DEF_TEST(PathOpsOpLoopsThreaded, reporter) {
int threadCount = initializeTests(reporter, "cubicOp");
PathOpsThreadedTestRunner testRunner(reporter, threadCount);
for (int a = 0; a < 6; ++a) { // outermost
for (int b = a + 1; b < 7; ++b) {
for (int c = 0 ; c < 6; ++c) {
for (int d = c + 1; d < 7; ++d) {
*testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
(&testOpLoopsMain, a, b, c, d, &testRunner));
}
}
if (!reporter->allowExtendedTest()) goto finish;
}
}
finish:
testRunner.render();
ShowTestArray();
}
DEF_TEST(PathOpsOpLoops, reporter) {
(void) initializeTests(reporter, "cubicOp");
PathOpsThreadState state;
state.fReporter = reporter;
SkBitmap bitmap;
state.fBitmap = &bitmap;
char pathStr[PATH_STR_SIZE];
state.fPathStr = pathStr;
for (state.fA = 0; state.fA < 6; ++state.fA) { // outermost
for (state.fB = state.fA + 1; state.fB < 7; ++state.fB) {
for (state.fC = 0 ; state.fC < 6; ++state.fC) {
for (state.fD = state.fC + 1; state.fD < 7; ++state.fD) {
testOpLoopsMain(&state);
}
}
if (!reporter->allowExtendedTest()) goto finish;
}
}
finish:
ShowTestArray();
}

View File

@ -3257,10 +3257,70 @@ static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// triggers addSimpleAngle with non-zero argument
static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(2,4);
path.cubicTo(2,3, 6,4, 1,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(4,6);
pathB.cubicTo(0,1, 4,2, 3,2);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
// triggers untested calcLoopSpanCount code path
#if 0
static void cubicOp113(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(2,4);
path.cubicTo(3,5, 2.33333325f,4.33333349f, 3.83333325f,3.83333349f);
path.close();
pathB.moveTo(3,5);
pathB.cubicTo(2.33333325f,4.33333349f, 3.83333325f,3.83333349f, 2,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
}
#endif
static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0, 1);
path.cubicTo(1, 3, -1, 2, 3.5f, 1.33333337f);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1, 3);
pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void quadOp10i(skiatest::Reporter* reporter, const char* filename) {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(1, 8, 3, 5);
path.lineTo(8, 1);
path.close();
pathB.moveTo(0, 0);
pathB.quadTo(8, 1, 4, 8);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
}
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static struct TestDesc tests[] = {
TEST(quadOp10i),
#if 0 // FIXME: serpentine curve is ordered the wrong way
TEST(cubicOp114),
#endif
#if 0 // FIXME: currently failing
TEST(cubicOp113),
#endif
#if SKPS_WORKING
// fails because a cubic/quadratic intersection is missed
// the internal quad/quad is far enough away from the real cubic/quad that it is rejected
@ -3269,6 +3329,7 @@ static struct TestDesc tests[] = {
#if ISSUE_1417_WORKING_ON_LINUX_32
TEST(issue1417),
#endif
TEST(cubicOp112),
TEST(skpadspert_net23),
TEST(skpadspert_de11),
TEST(findFirst1),
@ -3467,11 +3528,9 @@ static struct TestDesc tests[] = {
static const size_t testCount = SK_ARRAY_COUNT(tests);
static struct TestDesc subTests[] = {
TEST(cubicOp6d),
TEST(cubicOp8d),
TEST(cubicOp70d),
TEST(cubicOp16d),
TEST(skp5),
TEST(cubicOp114),
TEST(cubicOp58d),
TEST(cubicOp53d),
};
static const size_t subTestCount = SK_ARRAY_COUNT(subTests);
@ -3483,10 +3542,6 @@ static bool runSubTestsFirst = false;
static bool runReverse = false;
DEF_TEST(PathOpsOp, reporter) {
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = 4;
SkPathOpsDebug::gMaxWindValue = 4;
#endif
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
@ -3497,10 +3552,6 @@ DEF_TEST(PathOpsOp, reporter) {
if (runSubTests && !runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
}
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = SK_MaxS32;
SkPathOpsDebug::gMaxWindValue = SK_MaxS32;
#endif
}
static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
@ -3518,10 +3569,6 @@ static struct TestDesc failTests[] = {
static const size_t failTestCount = SK_ARRAY_COUNT(failTests);
DEF_TEST(PathOpsFailOp, reporter) {
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = 4;
SkPathOpsDebug::gMaxWindValue = 4;
#endif
#if DEBUG_SHOW_TEST_NAME
strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif

View File

@ -5086,10 +5086,6 @@ static bool runReverse = false;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
DEF_TEST(PathOpsSimplify, reporter) {
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = 4;
SkPathOpsDebug::gMaxWindValue = 4;
#endif
if (runSubTests && runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
}
@ -5097,8 +5093,4 @@ DEF_TEST(PathOpsSimplify, reporter) {
if (runSubTests && !runSubTestsFirst) {
RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
}
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = SK_MaxS32;
SkPathOpsDebug::gMaxWindValue = SK_MaxS32;
#endif
}

View File

@ -26,22 +26,86 @@
#define OUT_DIR "D:\\skpOut\\1\\"
#else
#define PATH_SLASH "/"
#define IN_DIR "/skp/slave"
#define OUT_DIR "/skpOut/1/"
#define IN_DIR "/skp/2311328-7fc2228/slave"
#define OUT_DIR "/skpOut/2/"
#endif
const struct {
int directory;
const char* filename;
} skipOverSept[] = {
{1, "http___elpais_com_.skp"},
{1, "http___namecheap_com_.skp"},
{1, "http___www_alrakoba_net_.skp"},
{1, "http___www_briian_com_.skp"}, // triggers assert at line 467 of SkRRect.cpp
{1, "http___www_cityads_ru_.skp"},
{3, "http___www_abeautifulmess_com_.skp"}, // asserts in IntToFixed from SkScan::AntiFilllXRect
{1, "http___www_dealnews_com_.skp"},
{1, "http___www_inmotionhosting_com.skp"},
{ 9, "http___www_catingueiraonline_com_.skp"}, // infinite loop
{13, "http___www_galaxystwo_com_.skp"}, // infinite loop
{15, "http___www_giffingtool_com_.skp"}, // joinCoincidence / findT / assert
{15, "http___www_thaienews_blogspot_com_.skp"}, // infinite loop
{17, "http___www_gruposejaumdivulgador_com_br_.skp"}, // calcCoincidentWinding asserts zeroSpan
{18, "http___www_argus_presse_fr_.skp"}, // can't find winding of remaining vertical edge
{21, "http___www_fashionscandal_com_.skp"}, // infinite loop
{21, "http___www_kenlevine_blogspot_com_.skp"}, // infinite loop
{25, "http___www_defense_studies_blogspot_com_.skp"}, // infinite loop
{27, "http___www_brokeroutpost_com_.skp"}, // suspect infinite loop
{28, "http___www_jaimebatistadasilva_blogspot_com_br_.skp"}, // suspect infinite loop
{28, "http___www_odia_com_br_.skp"}, // !simple->isClosed()
{29, "http___www_hubbyscook_com_.skp"}, // joinCoincidence / findT / assert
{30, "http___www_spankystokes_com_.skp"}, // suspect infinite loop
{32, "http___www_adalbertoday_blogspot_com_br_.skp"}, // suspect infinite loop
{32, "http___www_galery_annisa_com_.skp"}, // suspect infinite loop
{33, "http___www_pindosiya_com_.skp"}, // line quad intersection SkIntersections::assert
{36, "http___www_educationalcraft_com_.skp"}, // cubic / cubic near end / assert in SkIntersections::insert (missing skp test)
{36, "http___www_shaam_org_.skp"}, // suspect infinite loop
{36, "http___www_my_pillow_book_blogspot_gr_.skp"}, // suspect infinite loop
{39, "http___www_opbeat_com_.skp"}, // suspect infinite loop
{40, "http___www_phototransferapp_com_.skp"}, // !simple->isClosed()
{41, "http___www_freeismylife_com_.skp"}, // suspect infinite loop
{41, "http___www_accordidelmomento_com_.skp"}, // suspect infinite loop
{41, "http___www_evolvehq_com_.skp"}, // joinCoincidence / findT / assert
{44, "http___www_contextualnewsfeeds_com_.skp"}, // !simple->isClosed()
{44, "http___www_cooksnaps_com_.skp"}, // !simple->isClosed()
{44, "http___www_helha_be_.skp"}, // !simple->isClosed()
{45, "http___www_blondesmakemoney_blogspot_com_.skp"}, // suspect infinite loop
{46, "http___www_cheaphealthygood_blogspot_com_.skp"}, // suspect infinite loop
{47, "http___www_ajitvadakayil_blogspot_in_.skp"}, // suspect infinite loop
{49, "http___www_karnivool_com_au_.skp"}, // SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
{49, "http___www_tunero_de_.skp"}, // computeonesumreverse calls markwinding with 0 winding
{49, "http___www_thaienews_blogspot_sg_.skp"}, // suspect infinite loop
{50, "http___www_docgelo_com_.skp"}, // rightAngleWinding (probably same as argus_presse)
{53, "http___www_lojaanabotafogo_com_br_.skp"}, // rrect validate assert
{54, "http___www_odecktestanswer2013_blogspot_in_.skp"}, // suspect infinite loop
{54, "http___www_cleristonsilva_com_br_.skp"}, // suspect infinite loop
{56, "http___www_simplysaru_com_.skp"}, // joinCoincidence / findT / assert
{57, "http___www_koukfamily_blogspot_gr_.skp"}, // suspect infinite loop
{57, "http___www_dinar2010_blogspot_com_.skp"}, // suspect infinite loop
{58, "http___www_artblart_com_.skp"}, // rightAngleWinding
{59, "http___www_accrispin_blogspot_com_.skp"}, // suspect infinite loop
{59, "http___www_vicisitudysordidez_blogspot_com_es_.skp"}, // suspect infinite loop
{60, "http___www_thehousingbubbleblog_com_.skp"}, // suspect infinite loop
{61, "http___www_jessicaslens_wordpress_com_.skp"}, // joinCoincidence / findT / assert
{61, "http___www_partsdata_de_.skp"}, // cubic-cubic intersection reduce checkLinear assert
{62, "http___www_blondesmakemoney_blogspot_com_au_.skp"}, // suspect infinite loop
{62, "http___www_intellibriefs_blogspot_in_.skp"}, // suspect infinite loop
{63, "http___www_tankerenemy_com_.skp"}, // suspect infinite loop
{65, "http___www_kpopexplorer_net_.skp"}, // joinCoincidence / findT / assert
{65, "http___www_bestthingsinbeauty_blogspot_com_.skp"}, // suspect infinite loop
{65, "http___www_wartepop_blogspot_com_br_.skp"}, // !simple->isClosed()
{65, "http___www_eolake_blogspot_com_.skp"}, // suspect infinite loop
{67, "http___www_cacadordemisterio_blogspot_com_br_.skp"}, // suspect infinite loop
{69, "http___www_misnotasyapuntes_blogspot_mx_.skp"}, // suspect infinite loop
{69, "http___www_awalkintheparknyc_blogspot_com_.skp"}, // suspect infinite loop
{71, "http___www_lokado_de_.skp"}, // joinCoincidence / findT / assert
{72, "http___www_karlosdesanjuan_blogspot_com_.skp"}, // suspect infinite loop
{73, "http___www_cyberlawsinindia_blogspot_in_.skp"}, // suspect infinite loop
{73, "http___www_taxiemmovimento_blogspot_com_br_.skp"}, // suspect infinite loop
{74, "http___www_giveusliberty1776_blogspot_com_.skp"}, // suspect infinite loop
{75, "http___www_e_cynical_blogspot_gr_.skp"}, // suspect infinite loop
{76, "http___www_seopack_blogspot_com_.skp"}, // SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
{77, "http___www_sunsky_russia_com_.skp"}, // joinCoincidence / findT / assert (no op test, already fixed hopefully)
{78, "http___www_bisnisonlineinfo_com_.skp"}, // suspect infinite loop
{79, "http___www_danielsgroupcpa_com_.skp"}, // joinCoincidence / findT / assert (no op test, already fixed hopefully)
{80, "http___www_clinique_portugal_com_.skp"}, // suspect infinite loop
{81, "http___www_europebusines_blogspot_com_.skp"}, // suspect infinite loop
{82, "http___www_apopsignomi_blogspot_gr_.skp"}, // suspect infinite loop
{85, "http___www_ajitvadakayil_blogspot_com_.skp"}, // suspect infinite loop
{86, "http___www_madhousefamilyreviews_blogspot_co_uk_.skp"}, // suspect infinite loop
};
size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]);
@ -409,6 +473,9 @@ void TestResult::testOne() {
int height = pic->height();
SkBitmap oldBitmap, opBitmap;
fScale = 1;
while (width / fScale > 32767 || height / fScale > 32767) {
++fScale;
}
do {
int dimX = (width + fScale - 1) / fScale;
int dimY = (height + fScale - 1) / fScale;
@ -417,7 +484,7 @@ void TestResult::testOne() {
break;
}
SkDebugf("-%d-", fScale);
} while ((fScale *= 2) < 256);
} while (++fScale < 256);
if (fScale >= 256) {
SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", fFilename,
width, height);
@ -458,9 +525,10 @@ static SkString makeStatusString(int dirNo) {
class PreParser {
public:
PreParser(int dirNo)
PreParser(int dirNo, bool threaded)
: fDirNo(dirNo)
, fIndex(0) {
, fIndex(0)
, fThreaded(threaded) {
SkString statusPath = makeStatusString(dirNo);
if (!sk_exists(statusPath.c_str())) {
return;
@ -481,7 +549,7 @@ public:
do {
bool readOne = reader.read(&c, 1) != 0;
if (!readOne) {
SkASSERT(i == 0);
// SkASSERT(i == 0); // the current text may be incomplete -- if so, ignore it
return false;
}
if (c == ' ') {
@ -492,7 +560,9 @@ public:
SkASSERT(i < kMaxLength);
} while (true);
do {
SkAssertResult(reader.read(&c, 1));
if (!reader.read(&c, 1)) {
return false;
}
if (c == ' ') {
break;
}
@ -501,7 +571,9 @@ public:
} while (true);
bool minus = false;
do {
SkAssertResult(reader.read(&c, 1));
if (!reader.read(&c, 1)) {
return false;
}
if (c == '\n') {
break;
}
@ -519,7 +591,17 @@ public:
}
bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) {
if (fIndex < fResults.count()) {
if (fThreaded) {
for (int index = 0; index < fResults.count(); ++index) {
const TestResult& test = fResults[index];
if (filename.equals(test.fFilename)) {
*result = test;
SkString outStr(result->status());
stream->write(outStr.c_str(), outStr.size());
return true;
}
}
} else if (fIndex < fResults.count()) {
*result = fResults[fIndex++];
SkASSERT(filename.equals(result->fFilename));
SkString outStr(result->status());
@ -533,9 +615,10 @@ private:
int fDirNo;
int fIndex;
SkTArray<TestResult, true> fResults;
bool fThreaded;
};
static bool doOneDir(TestState* state) {
static bool doOneDir(TestState* state, bool threaded) {
int dirNo = state->fResult.fDirNo;
skiatest::Reporter* reporter = state->fReporter;
SkString dirName = make_in_dir_name(dirNo);
@ -545,7 +628,7 @@ static bool doOneDir(TestState* state) {
SkOSFile::Iter iter(dirName.c_str(), "skp");
SkString filename;
int testCount = 0;
PreParser preParser(dirNo);
PreParser preParser(dirNo, threaded);
SkFILEWStream statusStream(makeStatusString(dirNo).c_str());
while (iter.next(&filename)) {
for (size_t index = 0; index < skipOverSeptCount; ++index) {
@ -632,7 +715,7 @@ DEF_TEST(PathOpsSkpClip, reporter) {
SkDebugf("dirNo=%d\n", dirNo);
}
state.fResult.fDirNo = dirNo;
if (!doOneDir(&state)) {
if (!doOneDir(&state, false)) {
break;
}
}
@ -640,7 +723,7 @@ DEF_TEST(PathOpsSkpClip, reporter) {
}
static void testSkpClipMain(TestState* data) {
(void) doOneDir(data);
(void) doOneDir(data, true);
}
DEF_TEST(PathOpsSkpClipThreaded, reporter) {

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,4 @@ void PathOpsThreadedTestRunner::render() {
for (int index = 0; index < fRunnables.count(); ++ index) {
pool.add(fRunnables[index]);
}
#ifdef SK_DEBUG
SkPathOpsDebug::gMaxWindSum = SK_MaxS32;
SkPathOpsDebug::gMaxWindValue = SK_MaxS32;
#endif
}

View File

@ -826,11 +826,60 @@ op intersect
{{{1000, 13}, {999.969971f, 37.0299988f}}}
</div>
<div id="skpwww_maturesupertube_com_21">
{{{{3.87867975f, 11831.8789f}, {4.7573595f, 11831}, {6, 11831}}},
{{{2, 11830}, {4.5f, 11832.5f}}}},
</div>
<div id="loop1">
{{1, 4, 2, 6, 0, 5, 4.5f, 4.33333302f
{{2, 6, 0, 5, 4.5f, 4.33333302f, 1, 4
{{{3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}, {2, 4}}}
{{{2, 4}, {3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}}}
</div>
<div id="serp1">
{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
</div>
<div id="serp2">
{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
</div>
<div id="serp3">
{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
</div>
<div id="skpwww_seopack_blogspot_com_2153">
{{{924, 245.472672f}, {1143, 247}}}
{{{1000, 246}, {927.340759f, 245.505722f}}}
{{{999.892212f, 246}, {927.340759f, 245.505722f}}}
</div>
<div id="self1">
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
</div>
<div id="skpwww_pindosiya_com_99">
{{{901.0869140625, 547}, {899, 556}}}
{{{900.0235595703125, 551.60284423828125}, {900.06072998046875, 551.29705810546875}, {900.15655517578125, 551.0157470703125}}}
</div>
</div>
<script type="text/javascript">
var testDivs = [
skpwww_pindosiya_com_99,
self1,
skpwww_seopack_blogspot_com_2153,
serp1,
serp2,
serp3,
loop1,
skpwww_maturesupertube_com_21,
skpwww_cityads_ru_249,
skpelpais_com_18,
testQuads60_b,
@ -1492,7 +1541,7 @@ function dxy_at_t(curve, t) {
for (var curves in test) {
var curve = test[curves];
if (curve.length == 6 || curve.length == 8) {
var opp = curves == 1 ? 0 : 1;
var opp = curves == 0 || curves == 1 ? 0 : 1;
var sects = ray_curve_intersect(origin, hullEnds[opp], curve);
intersect.push(sects);
if (sects.length > 1) {
@ -1515,7 +1564,7 @@ function dxy_at_t(curve, t) {
}
}
}
var midLeft = crossPt(origin, midSpokes[0], midSpokes[1]);
var midLeft = curves != 0 ? crossPt(origin, midSpokes[0], midSpokes[1]) : 0;
var firstInside;
if (useIntersect) {
var sect1 = intersect[0].length > 1;