skia2/tests/PathOpsDebug.cpp
caryclark e4097e3a0b Fix last pathops skp bug
This fixes the last bug discovered by iterating through the 800K
skp corpus representing the top 1M websites. For every clip on the
stack, the paths are replaced with the pathop intersection. The
resulting draw is compared with the original draw for pixel errors.

At least two prominent bugs remain. In one, the winding value is
confused by a cubic with an inflection. In the other, a quad/cubic
pair, nearly coincident, fails to find an intersection.

These minor changes include ignoring very tiny self-intersections
of cubics, and processing degenerate edges that don't connect to
anything else.

R=reed@android.com
TBR=reed

Author: caryclark@google.com

Review URL: https://codereview.chromium.org/340103002
2014-06-18 07:24:19 -07:00

744 lines
21 KiB
C++
Executable File

#include "SkOpContour.h"
#include "SkIntersectionHelper.h"
#include "SkOpSegment.h"
inline void DebugDumpDouble(double x) {
if (x == floor(x)) {
SkDebugf("%.0f", x);
} else {
SkDebugf("%1.19g", x);
}
}
inline void DebugDumpFloat(float x) {
if (x == floorf(x)) {
SkDebugf("%.0f", x);
} else {
SkDebugf("%1.9gf", x);
}
}
// if not defined by PathOpsDebug.cpp ...
#if !defined SK_DEBUG && FORCE_RELEASE
bool SkPathOpsDebug::ValidWind(int wind) {
return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
}
void SkPathOpsDebug::WindingPrintf(int wind) {
if (wind == SK_MinS32) {
SkDebugf("?");
} else {
SkDebugf("%d", wind);
}
}
#endif
void SkOpAngle::dump() const {
dumpOne(true);
SkDebugf("\n");
}
void SkOpAngle::dumpOne(bool functionHeader) const {
// fSegment->debugValidate();
const SkOpSpan& mSpan = fSegment->span(SkMin32(fStart, fEnd));
if (functionHeader) {
SkDebugf("%s ", __FUNCTION__);
}
SkDebugf("[%d", fSegment->debugID());
SkDebugf("/%d", debugID());
SkDebugf("] next=");
if (fNext) {
SkDebugf("%d", fNext->fSegment->debugID());
SkDebugf("/%d", fNext->debugID());
} else {
SkDebugf("?");
}
SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fSegment->span(fStart).fT, fStart,
fSegment->span(fEnd).fT, fEnd);
SkDebugf(" sgn=%d windVal=%d", sign(), mSpan.fWindValue);
SkDebugf(" windSum=");
SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
if (mSpan.fOppValue != 0 || mSpan.fOppSum != SK_MinS32) {
SkDebugf(" oppVal=%d", mSpan.fOppValue);
SkDebugf(" oppSum=");
SkPathOpsDebug::WindingPrintf(mSpan.fOppSum);
}
if (mSpan.fDone) {
SkDebugf(" done");
}
if (unorderable()) {
SkDebugf(" unorderable");
}
if (small()) {
SkDebugf(" small");
}
if (mSpan.fTiny) {
SkDebugf(" tiny");
}
if (fSegment->operand()) {
SkDebugf(" operand");
}
if (fStop) {
SkDebugf(" stop");
}
}
void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
const SkOpAngle* first = this;
const SkOpAngle* next = this;
const char* indent = "";
do {
SkDebugf("%s", indent);
next->dumpOne(false);
if (segment == next->fSegment) {
if (this == fNext) {
SkDebugf(" << from");
}
if (to == fNext) {
SkDebugf(" << to");
}
}
SkDebugf("\n");
indent = " ";
next = next->fNext;
} while (next && next != first);
}
void SkOpAngle::dumpLoop() const {
const SkOpAngle* first = this;
const SkOpAngle* next = this;
do {
next->dumpOne(false);
SkDebugf("\n");
next = next->fNext;
} while (next && next != first);
}
void SkOpAngle::dumpPartials() const {
const SkOpAngle* first = this;
const SkOpAngle* next = this;
do {
next->fCurvePart.dumpNumber();
next = next->fNext;
} while (next && next != first);
}
void SkOpAngleSet::dump() const {
// FIXME: unimplemented
/* This requires access to the internal SkChunkAlloc data
Defer implementing this until it is needed for debugging
*/
SkASSERT(0);
}
void SkOpContour::dump() const {
int segmentCount = fSegments.count();
SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
for (int test = 0; test < segmentCount; ++test) {
SkDebugf(" [%d] ((SkOpSegment*) 0x%p) [%d]\n", test, &fSegments[test],
fSegments[test].debugID());
}
}
void SkOpContour::dumpAngles() const {
int segmentCount = fSegments.count();
SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
for (int test = 0; test < segmentCount; ++test) {
SkDebugf(" [%d] ", test);
fSegments[test].dumpAngles();
}
}
void SkOpContour::dumpCoincidence(const SkCoincidence& coin) const {
int thisIndex = coin.fSegments[0];
const SkOpSegment& s1 = fSegments[thisIndex];
int otherIndex = coin.fSegments[1];
const SkOpSegment& s2 = coin.fOther->fSegments[otherIndex];
SkDebugf("((SkOpSegment*) 0x%p) [%d] ((SkOpSegment*) 0x%p) [%d]\n", &s1, s1.debugID(),
&s2, s2.debugID());
for (int index = 0; index < 2; ++index) {
SkDebugf(" {%1.9gf, %1.9gf}", coin.fPts[0][index].fX, coin.fPts[0][index].fY);
if (coin.fNearly[index]) {
SkDebugf(" {%1.9gf, %1.9gf}", coin.fPts[1][index].fX, coin.fPts[1][index].fY);
}
SkDebugf(" seg1t=%1.9g seg2t=%1.9g\n", coin.fTs[0][index], coin.fTs[1][index]);
}
}
void SkOpContour::dumpCoincidences() const {
int count = fCoincidences.count();
if (count > 0) {
SkDebugf("fCoincidences count=%d\n", count);
for (int test = 0; test < count; ++test) {
dumpCoincidence(fCoincidences[test]);
}
}
count = fPartialCoincidences.count();
if (count == 0) {
return;
}
SkDebugf("fPartialCoincidences count=%d\n", count);
for (int test = 0; test < count; ++test) {
dumpCoincidence(fPartialCoincidences[test]);
}
}
void SkOpContour::dumpPt(int index) const {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
const SkOpSegment& segment = fSegments[test];
if (segment.debugID() == index) {
fSegments[test].dumpPts();
}
}
}
void SkOpContour::dumpPts() const {
int segmentCount = fSegments.count();
SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
for (int test = 0; test < segmentCount; ++test) {
SkDebugf(" [%d] ", test);
fSegments[test].dumpPts();
}
}
void SkOpContour::dumpSpan(int index) const {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
const SkOpSegment& segment = fSegments[test];
if (segment.debugID() == index) {
fSegments[test].dumpSpans();
}
}
}
void SkOpContour::dumpSpans() const {
int segmentCount = fSegments.count();
SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
for (int test = 0; test < segmentCount; ++test) {
SkDebugf(" [%d] ", test);
fSegments[test].dumpSpans();
}
}
void SkDCubic::dump() const {
SkDebugf("{{");
int index = 0;
do {
fPts[index].dump();
SkDebugf(", ");
} while (++index < 3);
fPts[index].dump();
SkDebugf("}}\n");
}
void SkDCubic::dumpNumber() const {
SkDebugf("{{");
int index = 0;
bool dumpedOne = false;
do {
if (!(fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY)) {
continue;
}
if (dumpedOne) {
SkDebugf(", ");
}
fPts[index].dump();
dumpedOne = true;
} while (++index < 3);
if (fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY) {
if (dumpedOne) {
SkDebugf(", ");
}
fPts[index].dump();
}
SkDebugf("}}\n");
}
void SkDLine::dump() const {
SkDebugf("{{");
fPts[0].dump();
SkDebugf(", ");
fPts[1].dump();
SkDebugf("}}\n");
}
void SkDPoint::dump() const {
SkDebugf("{");
DebugDumpDouble(fX);
SkDebugf(", ");
DebugDumpDouble(fY);
SkDebugf("}");
}
void SkDPoint::Dump(const SkPoint& pt) {
SkDebugf("{");
DebugDumpFloat(pt.fX);
SkDebugf(", ");
DebugDumpFloat(pt.fY);
SkDebugf("}");
}
void SkDQuad::dumpComma(const char* comma) const {
SkDebugf("{{");
int index = 0;
do {
fPts[index].dump();
SkDebugf(", ");
} while (++index < 2);
fPts[index].dump();
SkDebugf("}}%s\n", comma ? comma : "");
}
void SkDQuad::dump() const {
dumpComma("");
}
void SkIntersectionHelper::dump() const {
SkDPoint::Dump(pts()[0]);
SkDPoint::Dump(pts()[1]);
if (verb() >= SkPath::kQuad_Verb) {
SkDPoint::Dump(pts()[2]);
}
if (verb() >= SkPath::kCubic_Verb) {
SkDPoint::Dump(pts()[3]);
}
}
const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const {
return fTs;
}
void SkOpSegment::dumpAngles() const {
SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
const SkOpAngle* fromAngle = NULL;
const SkOpAngle* toAngle = NULL;
for (int index = 0; index < count(); ++index) {
const SkOpAngle* fAngle = fTs[index].fFromAngle;
const SkOpAngle* tAngle = fTs[index].fToAngle;
if (fromAngle == fAngle && toAngle == tAngle) {
continue;
}
if (fAngle) {
SkDebugf(" [%d] from=%d ", index, fAngle->debugID());
fAngle->dumpTo(this, tAngle);
}
if (tAngle) {
SkDebugf(" [%d] to=%d ", index, tAngle->debugID());
tAngle->dumpTo(this, fAngle);
}
fromAngle = fAngle;
toAngle = tAngle;
}
}
void SkOpSegment::dumpContour(int firstID, int lastID) const {
if (debugID() < 0) {
return;
}
const SkOpSegment* test = this - (debugID() - 1);
test += (firstID - 1);
const SkOpSegment* last = test + (lastID - firstID);
while (test <= last) {
test->dumpSpans();
++test;
}
}
void SkOpSegment::dumpPts() const {
int last = SkPathOpsVerbToPoints(fVerb);
SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
int index = 0;
do {
SkDPoint::Dump(fPts[index]);
SkDebugf(", ");
} while (++index < last);
SkDPoint::Dump(fPts[index]);
SkDebugf("}}\n");
}
void SkOpSegment::dumpDPts() const {
int count = SkPathOpsVerbToPoints(fVerb);
SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
int index = 0;
do {
SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
dPt.dump();
if (index != count) {
SkDebugf(", ");
}
} while (++index <= count);
SkDebugf("}}\n");
}
void SkOpSegment::dumpSpans() const {
int count = this->count();
SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
for (int index = 0; index < count; ++index) {
const SkOpSpan& span = this->span(index);
SkDebugf(" [%d] ", index);
span.dumpOne();
}
}
void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour, true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpCoincidences();
}
}
void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour* , true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpCoincidences();
}
}
void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour, true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dump();
}
}
void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour* , true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dump();
}
}
void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour, true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpAngles();
}
}
void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour* , true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpAngles();
}
}
void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour, true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpPts();
}
}
void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour* , true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpPts();
}
}
void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour, true>& contours, int segmentID) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpPt(segmentID);
}
}
void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpPt(segmentID);
}
}
void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour, true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpSpans();
}
}
void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour* , true>& contours) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpSpans();
}
}
void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour, true>& contours, int segmentID) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index].dumpSpan(segmentID);
}
}
void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
int count = contours.count();
for (int index = 0; index < count; ++index) {
contours[index]->dumpSpan(segmentID);
}
}
void SkPathOpsDebug::DumpSpans(const SkTDArray<SkOpSpan *>& spans) {
int count = spans.count();
for (int index = 0; index < count; ++index) {
const SkOpSpan* span = spans[index];
const SkOpSpan& oSpan = span->fOther->span(span->fOtherIndex);
const SkOpSegment* segment = oSpan.fOther;
SkDebugf("((SkOpSegment*) 0x%p) [%d] ", segment, segment->debugID());
SkDebugf("spanIndex:%d ", oSpan.fOtherIndex);
span->dumpOne();
}
}
// this does not require that other T index is initialized or correct
const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const {
if (!fOther) {
return NULL;
}
int oppCount = fOther->count();
for (int index = 0; index < oppCount; ++index) {
const SkOpSpan& otherSpan = fOther->span(index);
double otherTestT = otherSpan.fT;
if (otherTestT < fOtherT) {
continue;
}
SkASSERT(otherTestT == fOtherT);
const SkOpSegment* candidate = otherSpan.fOther;
const SkOpSpan* first = candidate->debugSpans().begin();
const SkOpSpan* last = candidate->debugSpans().end() - 1;
if (first <= this && this <= last) {
if (spanIndex) {
*spanIndex = this - first;
}
return candidate;
}
}
SkASSERT(0);
return NULL;
}
void SkOpSpan::dumpOne() const {
SkDebugf("t=");
DebugDumpDouble(fT);
SkDebugf(" pt=");
SkDPoint::Dump(fPt);
if (fOther) {
SkDebugf(" other.fID=%d", fOther->debugID());
SkDebugf(" [%d] otherT=", fOtherIndex);
DebugDumpDouble(fOtherT);
} else {
SkDebugf(" other.fID=? [?] otherT=?");
}
if (fWindSum != SK_MinS32) {
SkDebugf(" windSum=%d", fWindSum);
}
if (fOppSum != SK_MinS32 && (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0)) {
SkDebugf(" oppSum=%d", fOppSum);
}
SkDebugf(" windValue=%d", fWindValue);
if (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0) {
SkDebugf(" oppValue=%d", fOppValue);
}
if (fFromAngle && fFromAngle->debugID()) {
SkDebugf(" from=%d", fFromAngle->debugID());
}
if (fToAngle && fToAngle->debugID()) {
SkDebugf(" to=%d", fToAngle->debugID());
}
if (fChased) {
SkDebugf(" chased");
}
if (fCoincident) {
SkDebugf(" coincident");
}
if (fDone) {
SkDebugf(" done");
}
if (fLoop) {
SkDebugf(" loop");
}
if (fMultiple) {
SkDebugf(" multiple");
}
if (fNear) {
SkDebugf(" near");
}
if (fSmall) {
SkDebugf(" small");
}
if (fTiny) {
SkDebugf(" tiny");
}
SkDebugf("\n");
}
void SkOpSpan::dump() const {
ptrdiff_t spanIndex;
const SkOpSegment* segment = debugToSegment(&spanIndex);
if (segment) {
SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", segment, segment->debugID());
SkDebugf(" [%d] ", spanIndex);
} else {
SkDebugf("((SkOpSegment*) ?) [?]\n");
SkDebugf(" [?] ");
}
dumpOne();
}
void Dump(const SkTArray<class SkOpContour, true>& contours) {
SkPathOpsDebug::DumpContours(contours);
}
void Dump(const SkTArray<class SkOpContour* , true>& contours) {
SkPathOpsDebug::DumpContours(contours);
}
void Dump(const SkTArray<class SkOpContour, true>* contours) {
SkPathOpsDebug::DumpContours(*contours);
}
void Dump(const SkTArray<class SkOpContour* , true>* contours) {
SkPathOpsDebug::DumpContours(*contours);
}
void Dump(const SkTDArray<SkOpSpan *>& chase) {
SkPathOpsDebug::DumpSpans(chase);
}
void Dump(const SkTDArray<SkOpSpan *>* chase) {
SkPathOpsDebug::DumpSpans(*chase);
}
void DumpAngles(const SkTArray<class SkOpContour, true>& contours) {
SkPathOpsDebug::DumpContourAngles(contours);
}
void DumpAngles(const SkTArray<class SkOpContour* , true>& contours) {
SkPathOpsDebug::DumpContourAngles(contours);
}
void DumpAngles(const SkTArray<class SkOpContour, true>* contours) {
SkPathOpsDebug::DumpContourAngles(*contours);
}
void DumpAngles(const SkTArray<class SkOpContour* , true>* contours) {
SkPathOpsDebug::DumpContourAngles(*contours);
}
void DumpCoin(const SkTArray<class SkOpContour, true>& contours) {
SkPathOpsDebug::DumpCoincidence(contours);
}
void DumpCoin(const SkTArray<class SkOpContour* , true>& contours) {
SkPathOpsDebug::DumpCoincidence(contours);
}
void DumpCoin(const SkTArray<class SkOpContour, true>* contours) {
SkPathOpsDebug::DumpCoincidence(*contours);
}
void DumpCoin(const SkTArray<class SkOpContour* , true>* contours) {
SkPathOpsDebug::DumpCoincidence(*contours);
}
void DumpSpans(const SkTArray<class SkOpContour, true>& contours) {
SkPathOpsDebug::DumpContourSpans(contours);
}
void DumpSpans(const SkTArray<class SkOpContour* , true>& contours) {
SkPathOpsDebug::DumpContourSpans(contours);
}
void DumpSpans(const SkTArray<class SkOpContour, true>* contours) {
SkPathOpsDebug::DumpContourSpans(*contours);
}
void DumpSpans(const SkTArray<class SkOpContour* , true>* contours) {
SkPathOpsDebug::DumpContourSpans(*contours);
}
void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
SkPathOpsDebug::DumpContourSpan(contours, segmentID);
}
void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
SkPathOpsDebug::DumpContourSpan(contours, segmentID);
}
void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
}
void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
}
void DumpPts(const SkTArray<class SkOpContour, true>& contours) {
SkPathOpsDebug::DumpContourPts(contours);
}
void DumpPts(const SkTArray<class SkOpContour* , true>& contours) {
SkPathOpsDebug::DumpContourPts(contours);
}
void DumpPts(const SkTArray<class SkOpContour, true>* contours) {
SkPathOpsDebug::DumpContourPts(*contours);
}
void DumpPts(const SkTArray<class SkOpContour* , true>* contours) {
SkPathOpsDebug::DumpContourPts(*contours);
}
void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
SkPathOpsDebug::DumpContourPt(contours, segmentID);
}
void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
SkPathOpsDebug::DumpContourPt(contours, segmentID);
}
void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
SkPathOpsDebug::DumpContourPt(*contours, segmentID);
}
void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
SkPathOpsDebug::DumpContourPt(*contours, segmentID);
}
static void dumpTestCase(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) {
SkDebugf("<div id=\"quad%d\">\n", testNo);
quad1.dumpComma(",");
quad2.dump();
SkDebugf("</div>\n\n");
}
static void dumpTestTrailer() {
SkDebugf("</div>\n\n<script type=\"text/javascript\">\n\n");
SkDebugf(" var testDivs = [\n");
}
static void dumpTestList(int testNo, double min) {
SkDebugf(" quad%d,", testNo);
if (min > 0) {
SkDebugf(" // %1.9g", min);
}
SkDebugf("\n");
}
void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) {
SkDebugf("\n");
dumpTestCase(quad1, quad2, testNo);
dumpTestTrailer();
dumpTestList(testNo, 0);
SkDebugf("\n");
}
void DumpT(const SkDQuad& quad, double t) {
SkDLine line = {{quad.ptAtT(t), quad[0]}};
line.dump();
}