diff options
author | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-11-01 17:36:03 +0000 |
---|---|---|
committer | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-11-01 17:36:03 +0000 |
commit | a2bbc6e19d5332e81784e582c290cc060f40c4c7 (patch) | |
tree | 507a82e41b5a59f261295718091f0f1491b3d894 | |
parent | 045c3d330c6c14f090c2222ece08d82cb84fb3ea (diff) |
pathops work in progress
BUG=
Review URL: https://codereview.chromium.org/52653002
git-svn-id: http://skia.googlecode.com/svn/trunk@12089 2bbb7eff-a529-9590-31e7-b0007b416f81
42 files changed, 2957 insertions, 327 deletions
@@ -48,6 +48,7 @@ VALID_TARGETS := \ SampleApp_APK \ skhello \ skia_lib \ + skpskgr_test \ tests \ tools \ skpdiff diff --git a/gyp/most.gyp b/gyp/most.gyp index cfe528e8d0..8a8b85da2f 100644 --- a/gyp/most.gyp +++ b/gyp/most.gyp @@ -18,6 +18,7 @@ 'tests.gyp:tests', 'tools.gyp:tools', 'pathops_unittest.gyp:*', + 'skpskgr_test.gyp:*', # 'pdfviewer.gyp:pdfviewer', ], 'conditions': [ diff --git a/gyp/skpskgr_test.gyp b/gyp/skpskgr_test.gyp new file mode 100755 index 0000000000..3a5e4d1b8c --- /dev/null +++ b/gyp/skpskgr_test.gyp @@ -0,0 +1,46 @@ +# GYP file to build unit tests. +{ + 'includes': [ + 'apptype_console.gypi', + ], + 'targets': [ + { + 'target_name': 'skpskgr_test', + 'type': 'executable', + 'suppress_wildcard': '1', + 'include_dirs' : [ + '../src/core', + '../src/effects', + '../src/lazy', + '../src/pathops', + '../src/pdf', + '../src/pipe/utils', + '../src/utils', + '../tools/', + ], + 'sources': [ + '../tests/SkpSkGrTest.cpp', + '../tests/Test.cpp', + '../tests/skia_test.cpp', + '../tests/Test.h', + ], + 'dependencies': [ + 'skia_lib.gyp:skia_lib', + 'flags.gyp:flags', + ], + 'conditions': [ + [ 'skia_gpu == 1', { + 'include_dirs': [ + '../src/gpu', + ], + }], + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp index 5fa80ec506..7d5fc0d4a6 100644 --- a/src/pathops/SkAddIntersections.cpp +++ b/src/pathops/SkAddIntersections.cpp @@ -383,8 +383,8 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) { for (int pt = 0; pt < pts - 1; ++pt) { const SkDPoint& point = ts.pt(pt); const SkDPoint& next = ts.pt(pt + 1); - if (wt.isNear(ts[swap][pt], ts[swap][pt + 1], point, next) - && wn.isNear(ts[!swap][pt], ts[!swap][pt + 1], point, next)) { + if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next) + && wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) { if (!wt.addPartialCoincident(wn, ts, pt, swap)) { // remove extra point if two map to same float values ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp index ce2344841b..27b16341a8 100644 --- a/src/pathops/SkDCubicIntersection.cpp +++ b/src/pathops/SkDCubicIntersection.cpp @@ -292,6 +292,7 @@ bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const Sk tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY; tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX; SkIntersections impTs; + impTs.allowNear(false); impTs.intersectRay(cubic1, tmpLine); for (int index = 0; index < impTs.used(); ++index) { SkDPoint realPt = impTs.pt(index); @@ -446,6 +447,7 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) { return fUsed; } } else { + // OPTIMIZATION: set exact end bits here to avoid cubic exact end later for (int i1 = 0; i1 < 4; i1 += 3) { for (int i2 = 0; i2 < 4; i2 += 3) { if (c1[i1].approximatelyEqual(c2[i2])) { @@ -483,21 +485,22 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) { return fUsed; } // FIXME: pass in cached bounds from caller - SkDRect c1Bounds, c2Bounds; - c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? + SkDRect c2Bounds; c2Bounds.setBounds(c2); - if (!(exactEndBits & 1)) { + if (!(exactEndBits & 4)) { cubicNearEnd(c1, false, c2, c2Bounds); } - if (!(exactEndBits & 2)) { + if (!(exactEndBits & 8)) { cubicNearEnd(c1, true, c2, c2Bounds); } if (!selfIntersect) { + SkDRect c1Bounds; + c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? swap(); - if (!(exactEndBits & 4)) { + if (!(exactEndBits & 1)) { cubicNearEnd(c2, false, c1, c1Bounds); } - if (!(exactEndBits & 8)) { + if (!(exactEndBits & 2)) { cubicNearEnd(c2, true, c1, c1Bounds); } swap(); @@ -506,7 +509,61 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) { SkASSERT(!selfIntersect); return fUsed; } - ::intersect(c1, 0, 1, c2, 0, 1, 1, *this); + SkIntersections i; + i.fAllowNear = false; + i.fMax = 9; + ::intersect(c1, 0, 1, c2, 0, 1, 1, i); + int compCount = i.used(); + if (compCount) { + int exactCount = used(); + if (exactCount == 0) { + set(i); + } else { + // at least one is exact or near, and at least one was computed. Eliminate duplicates + for (int exIdx = 0; exIdx < exactCount; ++exIdx) { + for (int cpIdx = 0; cpIdx < compCount; ) { + if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) { + i.removeOne(cpIdx); + --compCount; + continue; + } + double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2; + SkDPoint pt = c1.ptAtT(tAvg); + if (!pt.approximatelyEqual(fPt[exIdx])) { + ++cpIdx; + continue; + } + tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2; + pt = c2.ptAtT(tAvg); + if (!pt.approximatelyEqual(fPt[exIdx])) { + ++cpIdx; + continue; + } + i.removeOne(cpIdx); + --compCount; + } + } + // if mid t evaluates to nearly the same point, skip the t + for (int cpIdx = 0; cpIdx < compCount - 1; ) { + double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2; + SkDPoint pt = c1.ptAtT(tAvg); + if (!pt.approximatelyEqual(fPt[cpIdx])) { + ++cpIdx; + continue; + } + tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2; + pt = c2.ptAtT(tAvg); + if (!pt.approximatelyEqual(fPt[cpIdx])) { + ++cpIdx; + continue; + } + i.removeOne(cpIdx); + --compCount; + } + // in addition to adding below missing function, think about how to say + append(i); + } + } // If an end point and a second point very close to the end is returned, the second // point may have been detected because the approximate quads // intersected at the end and close to it. Verify that the second point is valid. diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp index e9997e45dd..abbc4e32b2 100644 --- a/src/pathops/SkDCubicLineIntersection.cpp +++ b/src/pathops/SkDCubicLineIntersection.cpp @@ -215,6 +215,8 @@ public: } } + /* Note that this does not look for endpoints of the line that are near the cubic. + These points are found later when check ends looks for missing points */ void addNearEndPoints() { for (int cIndex = 0; cIndex < 4; cIndex += 3) { double cubicT = (double) (cIndex >> 1); diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp index fca0a04d1f..33c8480cd5 100644 --- a/src/pathops/SkDLineIntersection.cpp +++ b/src/pathops/SkDLineIntersection.cpp @@ -181,7 +181,7 @@ static int horizontal_coincident(const SkDLine& line, double y) { } static double horizontal_intercept(const SkDLine& line, double y) { - return (y - line[0].fY) / (line[1].fY - line[0].fY); + return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY)); } int SkIntersections::horizontal(const SkDLine& line, double y) { @@ -267,7 +267,7 @@ static int vertical_coincident(const SkDLine& line, double x) { } static double vertical_intercept(const SkDLine& line, double x) { - return (x - line[0].fX) / (line[1].fX - line[0].fX); + return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX)); } int SkIntersections::vertical(const SkDLine& line, double x) { diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp index 6e5f3e6012..48725089da 100644 --- a/src/pathops/SkDQuadIntersection.cpp +++ b/src/pathops/SkDQuadIntersection.cpp @@ -301,7 +301,7 @@ static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1 *pt = t1[1]; #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__, - t1Seed, t2Seed, t1[1].fX, t1[1].fY, t1[2].fX, t1[2].fY); + t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY); #endif return true; } @@ -490,15 +490,11 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { pts2[index] = q2.ptAtT(roots2Copy[index]); } if (r1Count == r2Count && r1Count <= 1) { - if (r1Count == 1) { + if (r1Count == 1 && used() == 0) { if (pts1[0].approximatelyEqual(pts2[0])) { insert(roots1Copy[0], roots2Copy[0], pts1[0]); } else if (pts1[0].moreRoughlyEqual(pts2[0])) { // experiment: try to find intersection by chasing t - rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0); - (void) addValidRoots(roots1, rootCount, roots1Copy); - rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0); - (void) addValidRoots(roots2, rootCount2, roots2Copy); if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) { insert(roots1Copy[0], roots2Copy[0], pts1[0]); } diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp index 14d7d9cea0..8fce7a089f 100644 --- a/src/pathops/SkDQuadLineIntersection.cpp +++ b/src/pathops/SkDQuadLineIntersection.cpp @@ -141,14 +141,18 @@ public: if (fAllowNear) { addNearEndPoints(); } - double rootVals[2]; - int roots = intersectRay(rootVals); - for (int index = 0; index < roots; ++index) { - double quadT = rootVals[index]; - double lineT = findLineT(quadT); - SkDPoint pt; - if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) { - fIntersections->insert(quadT, lineT, pt); + if (fIntersections->used() == 2) { + // FIXME : need sharable code that turns spans into coincident if middle point is on + } else { + double rootVals[2]; + int roots = intersectRay(rootVals); + for (int index = 0; index < roots; ++index) { + double quadT = rootVals[index]; + double lineT = findLineT(quadT); + SkDPoint pt; + if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) { + fIntersections->insert(quadT, lineT, pt); + } } } return fIntersections->used(); diff --git a/src/pathops/SkIntersectionHelper.h b/src/pathops/SkIntersectionHelper.h index 1a4b1f0441..f5eeaf8813 100644 --- a/src/pathops/SkIntersectionHelper.h +++ b/src/pathops/SkIntersectionHelper.h @@ -7,6 +7,10 @@ #include "SkOpContour.h" #include "SkPath.h" +#if SK_DEBUG +#include "SkPathOpsPoint.h" +#endif + class SkIntersectionHelper { public: enum SegmentType { @@ -81,6 +85,14 @@ public: return midPtByT.approximatelyEqual(midPtByAvg); } + bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const { + const SkOpSegment& segment = fContour->segments()[fIndex]; + double mid = (t1 + t2) / 2; + SkDPoint midPtByT = segment.dPtAtT(mid); + SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2); + return midPtByT.approximatelyPEqual(midPtByAvg); + } + SkScalar left() const { return bounds().fLeft; } @@ -137,6 +149,19 @@ public: return y() != pts()[0].fY; } +#ifdef SK_DEBUG + void dump() { + 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]); + } + } +#endif + private: SkOpContour* fContour; int fIndex; diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp index 608ffe3b6d..35846f6cc9 100644 --- a/src/pathops/SkIntersections.cpp +++ b/src/pathops/SkIntersections.cpp @@ -7,6 +7,12 @@ #include "SkIntersections.h" +void SkIntersections::append(const SkIntersections& i) { + for (int index = 0; index < i.fUsed; ++index) { + insert(i[0][index], i[1][index], i.pt(index)); + } +} + int (SkIntersections::*CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = { NULL, &SkIntersections::verticalLine, @@ -16,7 +22,7 @@ int (SkIntersections::*CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkS int (SkIntersections::*CurveRay[])(const SkPoint[], const SkDLine&) = { NULL, - NULL, + &SkIntersections::lineRay, &SkIntersections::quadRay, &SkIntersections::cubicRay }; @@ -126,6 +132,13 @@ void SkIntersections::insertCoincident(double one, double two, const SkDPoint& p fIsCoincident[1] |= bit; } +int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) { + SkDLine l; + l.set(pts); + fMax = 2; + return intersectRay(l, line); +} + void SkIntersections::offset(int base, double start, double end) { for (int index = base; index < fUsed; ++index) { double val = fT[fSwap][index]; diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h index f63a023ef0..a3e8332650 100644 --- a/src/pathops/SkIntersections.h +++ b/src/pathops/SkIntersections.h @@ -183,9 +183,6 @@ public: return intersect(aQuad, bQuad); } - int quadRay(const SkPoint pts[3], const SkDLine& line); - void removeOne(int index); - // leaves flip, swap, max alone void reset() { fAllowNear = true; @@ -218,6 +215,7 @@ public: SkASSERT(++fDepth < 16); } + void append(const SkIntersections& ); static double Axial(const SkDQuad& , const SkDPoint& , bool vertical); void cleanUpCoincidence(); int coincidentUsed() const; @@ -246,8 +244,11 @@ public: int intersectRay(const SkDQuad&, const SkDLine&); int intersectRay(const SkDCubic&, const SkDLine&); static SkDPoint Line(const SkDLine&, const SkDLine&); + int lineRay(const SkPoint pts[2], const SkDLine& line); void offset(int base, double start, double end); void quickRemoveOne(int index, int replace); + int quadRay(const SkPoint pts[3], const SkDLine& line); + void removeOne(int index); static bool Test(const SkDLine& , const SkDLine&); int vertical(const SkDLine&, double x); int vertical(const SkDLine&, double top, double bottom, double x, bool flipped); diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp index 5e1d9e745e..4144add6fb 100644 --- a/src/pathops/SkOpAngle.cpp +++ b/src/pathops/SkOpAngle.cpp @@ -207,7 +207,10 @@ bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; r return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh); } SkASSERT(fSide != 0 && rh.fSide != 0); - SkASSERT(fSide * rh.fSide > 0); // both are the same sign + if (fSide * rh.fSide < 0) { + fUnsortable = true; + return COMPARE_RESULT("14 fSide * rh.fSide < 0", this < &rh); + } SkDPoint lLoc; double best = SK_ScalarInfinity; #if DEBUG_SORT @@ -246,7 +249,7 @@ bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; r if (flip) { leftLessThanRight = !leftLessThanRight; } - return COMPARE_RESULT("14 leftLessThanRight", leftLessThanRight); + return COMPARE_RESULT("15 leftLessThanRight", leftLessThanRight); } bool SkOpAngle::isHorizontal() const { diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp index 4aa12cd465..5feef79801 100644 --- a/src/pathops/SkOpContour.cpp +++ b/src/pathops/SkOpContour.cpp @@ -174,6 +174,63 @@ void SkOpContour::calcPartialCoincidentWinding() { } } +void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) { + int count = coincidences.count(); +#if DEBUG_CONCIDENT + if (count > 0) { + SkDebugf("%s count=%d\n", __FUNCTION__, count); + } +#endif + // look for a lineup where the partial implies another adjoining coincidence + for (int index = 0; index < count; ++index) { + const SkCoincidence& coincidence = coincidences[index]; + int thisIndex = coincidence.fSegments[0]; + SkOpSegment& thisOne = fSegments[thisIndex]; + SkOpContour* otherContour = coincidence.fOther; + int otherIndex = coincidence.fSegments[1]; + SkOpSegment& other = otherContour->fSegments[otherIndex]; + double startT = coincidence.fTs[0][0]; + double endT = coincidence.fTs[0][1]; + if (startT == endT) { // this can happen in very large compares + continue; + } + double oStartT = coincidence.fTs[1][0]; + double oEndT = coincidence.fTs[1][1]; + if (oStartT == oEndT) { + continue; + } + bool swapStart = startT > endT; + bool swapOther = oStartT > oEndT; + if (swapStart) { + SkTSwap<double>(startT, endT); + SkTSwap<double>(oStartT, oEndT); + } + bool cancel = swapOther != swapStart; + int step = swapStart ? -1 : 1; + int oStep = swapOther ? -1 : 1; + double oMatchStart = cancel ? oEndT : oStartT; + if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) { + bool added = false; + if (oMatchStart != 0) { + added = thisOne.joinCoincidence(false, &other, oMatchStart, oStep, cancel); + } + if (startT != 0 && !added) { + (void) other.joinCoincidence(cancel, &thisOne, startT, step, cancel); + } + } + double oMatchEnd = cancel ? oStartT : oEndT; + if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) { + bool added = false; + if (oMatchEnd != 1) { + added = thisOne.joinCoincidence(true, &other, oMatchEnd, -oStep, cancel); + } + if (endT != 1 && !added) { + (void) other.joinCoincidence(!cancel, &thisOne, endT, -step, cancel); + } + } + } +} + void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) { int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h index a4ec6d398f..6b412e5f53 100644 --- a/src/pathops/SkOpContour.h +++ b/src/pathops/SkOpContour.h @@ -152,6 +152,11 @@ public: } } + void joinCoincidence() { + joinCoincidence(fCoincidences, false); + joinCoincidence(fPartialCoincidences, true); + } + SkOpSegment* nonVerticalSegment(int* start, int* end); bool operand() const { @@ -239,7 +244,8 @@ public: #endif private: - void calcCommonCoincidentWinding(const SkCoincidence& coincidence); + void calcCommonCoincidentWinding(const SkCoincidence& ); + void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial); void setBounds(); SkTArray<SkOpSegment> fSegments; diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp index 676c34fb37..ae72e29385 100644 --- a/src/pathops/SkOpEdgeBuilder.cpp +++ b/src/pathops/SkOpEdgeBuilder.cpp @@ -42,16 +42,11 @@ bool SkOpEdgeBuilder::finish() { } void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) { - if ((!AlmostEqualUlps(curveEnd.fX, curveStart.fX) - || !AlmostEqualUlps(curveEnd.fY, curveStart.fY))) { + if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) { fPathVerbs.push_back(SkPath::kLine_Verb); fPathPts.push_back_n(1, &curveStart); } else { - if (curveEnd.fX != curveStart.fX || curveEnd.fY != curveStart.fY) { - fPathPts[fPathPts.count() - 1] = curveStart; - } else { - fPathPts[fPathPts.count() - 1] = curveStart; - } + fPathPts[fPathPts.count() - 1] = curveStart; } fPathVerbs.push_back(SkPath::kClose_Verb); } @@ -82,9 +77,9 @@ int SkOpEdgeBuilder::preFetch() { lastCurve = false; continue; case SkPath::kLine_Verb: - if (AlmostEqualUlps(curve[0].fX, pts[1].fX) - && AlmostEqualUlps(curve[0].fY, pts[1].fY)) { - if (fPathVerbs.back() != SkPath::kLine_Verb) { + if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) { + uint8_t lastVerb = fPathVerbs.back(); + if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { fPathPts.back() = pts[1]; } continue; // skip degenerate points diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index 4d11eb39e8..6fe1fbb49d 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -1298,6 +1298,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi SkIntersections intersections; // OPTIMIZE: use specialty function that intersects ray with curve, // returning t values only for curve (we don't care about t on ray) + intersections.allowNear(false); int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)]) (fPts, top, bottom, basePt.fX, false); if (pts == 0 || (current && pts == 1)) { @@ -1420,15 +1421,29 @@ void SkOpSegment::checkEnds() { } // t start/last describe the range of spans that match the t of this span double t = span.fT; - int tStart = index; - while (--tStart >= 0 && (t == fTs[tStart].fT || fTs[tStart].fTiny)) - ; - int tLast = index; - while (fTs[tLast].fTiny) { - ++tLast; + double tBottom = -1; + int tStart = -1; + int tLast = count; + bool lastSmall = false; + double afterT = t; + for (int inner = 0; inner < count; ++inner) { + double innerT = fTs[inner].fT; + if (innerT <= t && innerT > tBottom) { + if (innerT < t || !lastSmall) { + tStart = inner - 1; + } + tBottom = innerT; + } + if (innerT > afterT) { + if (t == afterT && lastSmall) { + afterT = innerT; + } else { + tLast = inner; + break; + } + } + lastSmall = innerT <= t ? fTs[inner].fSmall : false; } - while (++tLast < count && t == fTs[tLast].fT) - ; for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) { if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span continue; @@ -1696,6 +1711,70 @@ void SkOpSegment::checkTiny() { } } +bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, + int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const { + SkASSERT(span->fT == 0 || span->fT == 1); + SkASSERT(span->fOtherT == 0 || span->fOtherT == 1); + const SkOpSpan* otherSpan = &other->span(oEnd); + double refT = otherSpan->fT; + const SkPoint& refPt = otherSpan->fPt; + const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0); + do { + const SkOpSegment* match = span->fOther; + if (match == otherSpan->fOther) { + // find start of respective spans and see if both have winding + int startIndex, endIndex; + if (span->fOtherT == 1) { + endIndex = span->fOtherIndex; + startIndex = match->nextExactSpan(endIndex, -1); + } else { + startIndex = span->fOtherIndex; + endIndex = match->nextExactSpan(startIndex, 1); + } + const SkOpSpan& startSpan = match->span(startIndex); + if (startSpan.fWindValue != 0) { + // draw ray from endSpan.fPt perpendicular to end tangent and measure distance + // to other segment. + const SkOpSpan& endSpan = match->span(endIndex); + SkDLine ray; + SkVector dxdy; + if (span->fOtherT == 1) { + ray.fPts[0].set(startSpan.fPt); + dxdy = match->dxdy(startIndex); + } else { + ray.fPts[0].set(endSpan.fPt); + dxdy = match->dxdy(endIndex); + } + ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY; + ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX; + SkIntersections i; + int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray); + for (int index = 0; index < roots; ++index) { + if (ray.fPts[0].approximatelyEqual(i.pt(index))) { + double matchMidT = (match->span(startIndex).fT + + match->span(endIndex).fT) / 2; + SkPoint matchMidPt = match->ptAtT(matchMidT); + double otherMidT = (i[0][index] + other->span(oStart).fT) / 2; + SkPoint otherMidPt = other->ptAtT(otherMidT); + if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) { + *startPt = startSpan.fPt; + *endPt = endSpan.fPt; + *endT = endSpan.fT; + return true; + } + } + } + } + return false; + } + if (otherSpan == lastSpan) { + break; + } + otherSpan += step; + } while (otherSpan->fT == refT || otherSpan->fPt == refPt); + return false; +} + /* The M and S variable name parts stand for the operators. Mi stands for Minuend (see wiki subtraction, analogous to difference) @@ -2076,6 +2155,18 @@ int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int return firstIndex; } +int SkOpSegment::findT(double t, const SkOpSegment* match) const { + int count = this->count(); + for (int index = 0; index < count; ++index) { + const SkOpSpan& span = fTs[index]; + if (span.fT == t && span.fOther == match) { + return index; + } + } + SkASSERT(0); + return -1; +} + // FIXME: either: // a) mark spans with either end unsortable as done, or // b) rewrite findTop / findTopSegment / findTopContour to iterate further @@ -2299,6 +2390,76 @@ bool SkOpSegment::isSimple(int end) const { return false; } +bool SkOpSegment::isTiny(const SkOpAngle* angle) const { + int start = angle->start(); + int end = angle->end(); + const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; + return mSpan.fTiny; +} + +bool SkOpSegment::isTiny(int index) const { + return fTs[index].fTiny; +} + +// look pair of active edges going away from coincident edge +// one of them should be the continuation of other +// if both are active, look to see if they both the connect to another coincident pair +// if one at least one is a line, then make the pair coincident +// if neither is a line, test for coincidence +bool SkOpSegment::joinCoincidence(bool end, SkOpSegment* other, double otherT, int step, + bool cancel) { + int otherTIndex = other->findT(otherT, this); + int next = other->nextExactSpan(otherTIndex, step); + int otherMin = SkTMin(otherTIndex, next); + int otherWind = other->span(otherMin).fWindValue; + if (otherWind == 0) { + return false; + } + SkASSERT(next >= 0); + if (end) { + int tIndex = count() - 1; + do { + SkOpSpan* test = &fTs[tIndex]; + SkASSERT(test->fT == 1); + if (test->fOther == other || test->fOtherT != 0) { + continue; + } + SkPoint startPt, endPt; + double endT; + if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { + SkOpSegment* match = test->fOther; + if (cancel) { + match->addTCancel(startPt, endPt, other); + } else { + match->addTCoincident(startPt, endPt, endT, other); + } + return true; + } + } while (fTs[--tIndex].fT == 1); + } else { + int tIndex = 0; + do { + SkOpSpan* test = &fTs[tIndex]; + SkASSERT(test->fT == 0); + if (test->fOther == other || test->fOtherT != 1) { + continue; + } + SkPoint startPt, endPt; + double endT; + if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) { + SkOpSegment* match = test->fOther; + if (cancel) { + match->addTCancel(startPt, endPt, other); + } else { + match->addTCoincident(startPt, endPt, endT, other); + } + return true; + } + } while (fTs[++tIndex].fT == 0); + } + return false; +} + // this span is excluded by the winding rule -- chase the ends // as long as they are unambiguous to mark connections as done // and give them the same winding value @@ -3018,17 +3179,6 @@ void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) c (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge); } -bool SkOpSegment::isTiny(const SkOpAngle* angle) const { - int start = angle->start(); - int end = angle->end(); - const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; - return mSpan.fTiny; -} - -bool SkOpSegment::isTiny(int index) const { - return fTs[index].fTiny; -} - void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt, const SkPoint& startPt) { int outCount = outsidePts->count(); @@ -3558,10 +3708,10 @@ void SkOpSegment::dumpPts() const { SkDebugf("{{"); int index = 0; do { - SkDPoint::DumpSkPoint(fPts[index]); + SkDPoint::dump(fPts[index]); SkDebugf(", "); } while (++index < last); - SkDPoint::DumpSkPoint(fPts[index]); + SkDPoint::dump(fPts[index]); SkDebugf("}}\n"); } diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h index 85531f5262..d56ce8e206 100644 --- a/src/pathops/SkOpSegment.h +++ b/src/pathops/SkOpSegment.h @@ -259,12 +259,15 @@ public: SkTArray<SkOpAngle, true>* angles, SkTArray<SkOpAngle*, true>* sorted); 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, + int step, SkPoint* startPt, SkPoint* endPt, double* endT) const; SkOpSegment* findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd, bool* unsortable, SkPathOp op, const int xorMiMask, const int xorSuMask); SkOpSegment* findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd, bool* unsortable); SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable); + int findT(double t, const SkOpSegment* ) const; SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool onlySortable); void fixOtherTIndex(); void initWinding(int start, int end); @@ -272,6 +275,7 @@ public: SkScalar hitOppDx); bool isMissing(double startT, const SkPoint& pt) const; bool isTiny(const SkOpAngle* angle) const; + bool joinCoincidence(bool end, SkOpSegment* other, double otherT, 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); diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index 4db60797ec..c48a7eef68 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -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 "SkAddIntersections.h" #include "SkOpEdgeBuilder.h" #include "SkPathOpsCommon.h" #include "SkPathWriter.h" @@ -350,7 +351,7 @@ SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, return current; } -void CheckEnds(SkTArray<SkOpContour*, true>* contourList) { +static void checkEnds(SkTArray<SkOpContour*, true>* contourList) { // it's hard to determine if the end of a cubic or conic nearly intersects another curve. // instead, look to see if the connecting curve intersected at that same end. int contourCount = (*contourList).count(); @@ -361,7 +362,7 @@ void CheckEnds(SkTArray<SkOpContour*, true>* contourList) { } // A tiny interval may indicate an undiscovered coincidence. Find and fix. -void CheckTiny(SkTArray<SkOpContour*, true>* contourList) { +static void checkTiny(SkTArray<SkOpContour*, true>* contourList) { int contourCount = (*contourList).count(); for (int cTest = 0; cTest < contourCount; ++cTest) { SkOpContour* contour = (*contourList)[cTest]; @@ -369,7 +370,7 @@ void CheckTiny(SkTArray<SkOpContour*, true>* contourList) { } } -void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) { +static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) { int contourCount = (*contourList).count(); for (int cTest = 0; cTest < contourCount; ++cTest) { SkOpContour* contour = (*contourList)[cTest]; @@ -377,7 +378,15 @@ void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) { } } -void SortSegments(SkTArray<SkOpContour*, true>* contourList) { +static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) { + int contourCount = (*contourList).count(); + for (int cTest = 0; cTest < contourCount; ++cTest) { + SkOpContour* contour = (*contourList)[cTest]; + contour->joinCoincidence(); + } +} + +static void sortSegments(SkTArray<SkOpContour*, true>* contourList) { int contourCount = (*contourList).count(); for (int cTest = 0; cTest < contourCount; ++cTest) { SkOpContour* contour = (*contourList)[cTest]; @@ -603,3 +612,21 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) { } #endif } + +void HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) { +#if DEBUG_SHOW_WINDING + SkOpContour::debugShowWindingValues(contourList); +#endif + CoincidenceCheck(contourList, total); +#if DEBUG_SHOW_WINDING + SkOpContour::debugShowWindingValues(contourList); +#endif + fixOtherTIndex(contourList); + checkEnds(contourList); + checkTiny(contourList); + joinCoincidence(contourList); + sortSegments(contourList); +#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY + DebugShowActiveSpans(*contourList); +#endif +} diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h index e1ae998b10..afc751d130 100644 --- a/src/pathops/SkPathOpsCommon.h +++ b/src/pathops/SkPathOpsCommon.h @@ -14,18 +14,15 @@ class SkPathWriter; void Assemble(const SkPathWriter& path, SkPathWriter* simple); -void CheckEnds(SkTArray<SkOpContour*, true>* contourList); -void CheckTiny(SkTArray<SkOpContour*, true>* contourList); // FIXME: find chase uses insert, so it can't be converted to SkTArray yet 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); SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end); -void FixOtherTIndex(SkTArray<SkOpContour*, true>* contourList); void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list, bool evenOdd, bool oppEvenOdd); -void SortSegments(SkTArray<SkOpContour*, true>* contourList); +void HandleCoincidence(SkTArray<SkOpContour*, true>* , int ); #if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList); diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp index 1b19fe5ce1..95e2204c33 100644 --- a/src/pathops/SkPathOpsDebug.cpp +++ b/src/pathops/SkPathOpsDebug.cpp @@ -103,7 +103,7 @@ void SkOpSpan::dump() const { SkDebugf("t="); DebugDumpDouble(fT); SkDebugf(" pt="); - SkDPoint::DumpSkPoint(fPt); + SkDPoint::dump(fPt); SkDebugf(" other.fID=%d", fOther->debugID()); SkDebugf(" [%d] otherT=", fOtherIndex); DebugDumpDouble(fOtherT); @@ -157,3 +157,8 @@ void Dump(const SkTArray<class SkOpAngle* , true>* angles) { } #endif + +#if !FORCE_RELEASE && 0 // enable when building without extended test +void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) { +} +#endif diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index 71ebef00b3..9d6cd51b45 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -304,20 +304,7 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { for (index = 0; index < contourList.count(); ++index) { total += contourList[index]->segments().count(); } -#if DEBUG_SHOW_WINDING - SkOpContour::debugShowWindingValues(contourList); -#endif - CoincidenceCheck(&contourList, total); -#if DEBUG_SHOW_WINDING - SkOpContour::debugShowWindingValues(contourList); -#endif - FixOtherTIndex(&contourList); - CheckEnds(&contourList); - CheckTiny(&contourList); - SortSegments(&contourList); -#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY - DebugShowActiveSpans(contourList); -#endif + HandleCoincidence(&contourList, total); // construct closed contours SkPathWriter wrapper(*result); bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper); diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h index 40688d8072..c3e0b40ab9 100644 --- a/src/pathops/SkPathOpsPoint.h +++ b/src/pathops/SkPathOpsPoint.h @@ -98,7 +98,7 @@ struct SkDPoint { // note: this can not be implemented with // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX); - // because that will not take the magnitude of the values + // because that will not take the magnitude of the values into account bool approximatelyEqual(const SkDPoint& a) const { if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) { return true; @@ -136,6 +136,20 @@ struct SkDPoint { return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance? } + bool approximatelyPEqual(const SkDPoint& a) const { + if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) { + return true; + } + if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) { + return false; + } + double dist = distance(a); // OPTIMIZATION: can we compare against distSq instead ? + double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY); + double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY); + largest = SkTMax(largest, -tiniest); + return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance? + } + bool approximatelyZero() const { return approximately_zero(fX) && approximately_zero(fY); } @@ -186,7 +200,7 @@ struct SkDPoint { SkDebugf("}"); } - static void DumpSkPoint(const SkPoint& pt) { + static void dump(const SkPoint& pt) { SkDebugf("{"); DebugDumpFloat(pt.fX); SkDebugf(", "); diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp index 76e3413089..548f83e660 100644 --- a/src/pathops/SkPathOpsSimplify.cpp +++ b/src/pathops/SkPathOpsSimplify.cpp @@ -182,15 +182,7 @@ bool Simplify(const SkPath& path, SkPath* result) { next = *nextPtr++; } while (AddIntersectTs(current, next) && nextPtr != listEnd); } while (currentPtr != listEnd); - // eat through coincident edges - CoincidenceCheck(&contourList, 0); - FixOtherTIndex(&contourList); - CheckEnds(&contourList); - CheckTiny(&contourList); - SortSegments(&contourList); -#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY - DebugShowActiveSpans(contourList); -#endif + HandleCoincidence(&contourList, 0); // construct closed contours SkPathWriter simple(*result); if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple) diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp index 49391667ac..003968ddba 100644 --- a/src/pathops/SkPathOpsTriangle.cpp +++ b/src/pathops/SkPathOpsTriangle.cpp @@ -8,6 +8,7 @@ #include "SkPathOpsTriangle.h" // http://www.blackpawn.com/texts/pointinpoly/default.html +// return true if pt is inside triangle; false if outside or on the line bool SkDTriangle::contains(const SkDPoint& pt) const { // Compute vectors SkDVector v0 = fPts[2] - fPts[0]; @@ -21,11 +22,30 @@ bool SkDTriangle::contains(const SkDPoint& pt) const { double dot11 = v1.dot(v1); double dot12 = v1.dot(v2); +// original code doesn't handle degenerate input; isn't symmetric with inclusion of corner pts; +// introduces necessary error with divide; doesn't short circuit on early answer +#if 0 // Compute barycentric coordinates double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); double u = (dot11 * dot02 - dot01 * dot12) * invDenom; double v = (dot00 * dot12 - dot01 * dot02) * invDenom; // Check if point is in triangle - return (u >= 0) && (v >= 0) && (u + v < 1); + return (u >= 0) && (v >= 0) && (u + v <= 1); +#else + double w = dot00 * dot11 - dot01 * dot01; + if (w == 0) { + return false; + } + double wSign = w < 0 ? -1 : 1; + double u = (dot11 * dot02 - dot01 * dot12) * wSign; + if (u <= 0) { + return false; + } + double v = (dot00 * dot12 - dot01 * dot02) * wSign; + if (v <= 0) { + return false; + } + return u + v < w * wSign; +#endif } diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp index df73d11ce4..dbed086fbd 100644 --- a/src/pathops/SkPathOpsTypes.cpp +++ b/src/pathops/SkPathOpsTypes.cpp @@ -8,17 +8,17 @@ #include "SkPathOpsTypes.h" static bool arguments_denormalized(float a, float b, int epsilon) { - float denomalizedCheck = FLT_EPSILON * epsilon / 2; - return fabsf(a) <= denomalizedCheck && fabsf(b) <= denomalizedCheck; + float denormalizedCheck = FLT_EPSILON * epsilon / 2; + return fabsf(a) <= denormalizedCheck && fabsf(b) <= denormalizedCheck; } // from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ // FIXME: move to SkFloatBits.h -static bool equal_ulps(float a, float b, int epsilon) { +static bool equal_ulps(float a, float b, int epsilon, int depsilon) { if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) { return false; } - if (arguments_denormalized(a, b, epsilon)) { + if (arguments_denormalized(a, b, depsilon)) { return true; } int aBits = SkFloatAs2sCompliment(a); @@ -89,7 +89,12 @@ static bool less_or_equal_ulps(float a, float b, int epsilon) { // equality using the same error term as between bool AlmostBequalUlps(float a, float b) { const int UlpsEpsilon = 2; - return equal_ulps(a, b, UlpsEpsilon); + return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon); +} + +bool AlmostPequalUlps(float a, float b) { + const int UlpsEpsilon = 8; + return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon); } bool AlmostDequalUlps(float a, float b) { @@ -99,7 +104,7 @@ bool AlmostDequalUlps(float a, float b) { bool AlmostEqualUlps(float a, float b) { const int UlpsEpsilon = 16; - return equal_ulps(a, b, UlpsEpsilon); + return equal_ulps(a, b, UlpsEpsilon, UlpsEpsilon); } bool NotAlmostEqualUlps(float a, float b) { @@ -114,7 +119,8 @@ bool NotAlmostDequalUlps(float a, float b) { bool RoughlyEqualUlps(float a, float b) { const int UlpsEpsilon = 256; - return equal_ulps(a, b, UlpsEpsilon); + const int DUlpsEpsilon = 1024; + return equal_ulps(a, b, UlpsEpsilon, DUlpsEpsilon); } bool AlmostBetweenUlps(float a, float b, float c) { diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h index 0ad10c2eba..4fa86abd91 100644 --- a/src/pathops/SkPathOpsTypes.h +++ b/src/pathops/SkPathOpsTypes.h @@ -50,6 +50,11 @@ inline bool AlmostBequalUlps(double a, double b) { return AlmostBequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } +bool AlmostPequalUlps(float a, float b); +inline bool AlmostPequalUlps(double a, double b) { + return AlmostPequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); +} + bool RoughlyEqualUlps(float a, float b); inline bool RoughlyEqualUlps(double a, double b) { return RoughlyEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); diff --git a/src/pathops/SkQuarticRoot.cpp b/src/pathops/SkQuarticRoot.cpp index dca96ded3a..e5b486c76c 100644 --- a/src/pathops/SkQuarticRoot.cpp +++ b/src/pathops/SkQuarticRoot.cpp @@ -71,7 +71,7 @@ int SkReducedQuarticRoots(const double t4, const double t3, const double t2, con return num; } if (oneHint) { - SkASSERT(approximately_zero(t4 + t3 + t2 + t1 + t0)); // 1 is one root + SkASSERT(approximately_zero_double(t4 + t3 + t2 + t1 + t0)); // 1 is one root // note that -C == A + B + D + E int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots); for (int i = 0; i < num; ++i) { diff --git a/tests/PathOpsCubicIntersectionTest.cpp b/tests/PathOpsCubicIntersectionTest.cpp index 04797b4ef3..c8a2e05e66 100644 --- a/tests/PathOpsCubicIntersectionTest.cpp +++ b/tests/PathOpsCubicIntersectionTest.cpp @@ -164,11 +164,23 @@ static const SkDCubic testSet[] = { const size_t testSetCount = SK_ARRAY_COUNT(testSet); static const SkDCubic newTestSet[] = { +{{{275,532}, {277.209137,532}, {279,530.209106}, {279,528}}}, +{{{278,529}, {278,530.65686}, {276.65686,532}, {275,532}}}, + #if 0 // FIXME: asserts coincidence, not working yet {{{195, 785}, {124.30755615234375, 785}, {67, 841.85986328125}, {67, 912}}}, {{{67, 913}, {67, 842.30755615234375}, {123.85984039306641, 785}, {194, 785}}}, #endif +{{{149,710.001465}, {149.000809,712.209961}, {150.791367,714}, {153,714}}}, +{{{154,715}, {151.238571,715}, {149,712.761414}, {149,710}}}, + +{{{1,2}, {1,2}, {2,0}, {6,0}}}, +{{{0,2}, {0,6}, {2,1}, {2,1}}}, + +{{{0,1}, {2,3}, {5,1}, {4,3}}}, +{{{1,5}, {3,4}, {1,0}, {3,2}}}, + {{{399,657}, {399,661.970581}, {403.029449,666}, {408,666}}}, {{{406,666}, {402.686279,666}, {400,663.313721}, {400,660}}}, diff --git a/tests/PathOpsCubicLineIntersectionTest.cpp b/tests/PathOpsCubicLineIntersectionTest.cpp index 49219fbfe5..53e9d60d88 100644 --- a/tests/PathOpsCubicLineIntersectionTest.cpp +++ b/tests/PathOpsCubicLineIntersectionTest.cpp @@ -15,6 +15,9 @@ static struct lineCubic { SkDCubic cubic; SkDLine line; } lineCubicTests[] = { + {{{{154,715}, {151.238571,715}, {149,712.761414}, {149,710}}}, + {{{149,675}, {149,710.001465}}}}, + {{{{0,1}, {1,6}, {4,1}, {4,3}}}, {{{6,1}, {1,4}}}}, diff --git a/tests/PathOpsCubicQuadIntersectionTest.cpp b/tests/PathOpsCubicQuadIntersectionTest.cpp index c9da2c7378..35b49d2351 100644 --- a/tests/PathOpsCubicQuadIntersectionTest.cpp +++ b/tests/PathOpsCubicQuadIntersectionTest.cpp @@ -17,14 +17,32 @@ static struct lineCubic { int answerCount; SkDPoint answers[2]; } quadCubicTests[] = { + {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
+ {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
+ {{778, 14089}, {776.82855609581270,14091.828250841330}}},
+ + {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}}, + {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2, + {{1110, 817}, {1110.70715f, 817.292908f}}}, + + {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}}, + {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2, + {{1110.70715f, 817.292908f}, {1111, 818}}}, + + {{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}}, + {{{55, 207}, {52.929431915283203, 206.99949645996094}, + {51.464466094970703, 205.53553771972656}}}, 2, + {{55, 207}, {51.464466094970703, 205.53553771972656}}}, + {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}}, {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688}, {34.355339050292969, 82.355339050292969}}}, 2, - {{34.355339050292969,82.355339050292969}, {34.306797674910243,82.403823585863449}}}, + {{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}}, {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}}, {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1, {{18,226}, {0,0}}}, + {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}}, {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1, {{10,234}, {0,0}}}, @@ -69,6 +87,10 @@ static void PathOpsCubicQuadIntersectionTest(skiatest::Reporter* reporter) { for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) { found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1); } + if (!found) { + SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n", + __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY); + } REPORTER_ASSERT(reporter, found); } reporter->bumpTestCount(); diff --git a/tests/PathOpsDQuadTest.cpp b/tests/PathOpsDQuadTest.cpp index 5921b69578..e6f1deb72f 100644 --- a/tests/PathOpsDQuadTest.cpp +++ b/tests/PathOpsDQuadTest.cpp @@ -5,7 +5,9 @@ * found in the LICENSE file. */ #include "PathOpsTestCommon.h" +#include "SkPath.h" #include "SkPathOpsQuad.h" +#include "SkRRect.h" #include "Test.h" static const SkDQuad tests[] = { @@ -21,7 +23,7 @@ static const SkDPoint inPoint[]= { {1, 0.8}, {1.8, 1}, {1.5, 1}, - {0.5, 0.5}, + {0.4999, 0.5}, // was 0.5, 0.5; points on the hull are considered outside }; static const SkDPoint outPoint[]= { @@ -51,5 +53,16 @@ static void PathOpsDQuadTest(skiatest::Reporter* reporter) { } } +static void PathOpsRRectTest(skiatest::Reporter* reporter) { + SkPath path; + SkRRect rRect; + SkRect rect = {135, 143, 250, 177}; + SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}}; + rRect.setRectRadii(rect, radii); + path.addRRect(rRect); +} + #include "TestClassDef.h" DEFINE_TESTCLASS_SHORT(PathOpsDQuadTest) + +DEFINE_TESTCLASS_SHORT(PathOpsRRectTest) diff --git a/tests/PathOpsDTriangleTest.cpp b/tests/PathOpsDTriangleTest.cpp index 35bfe06b88..6aec3086bb 100644 --- a/tests/PathOpsDTriangleTest.cpp +++ b/tests/PathOpsDTriangleTest.cpp @@ -45,5 +45,31 @@ static void PathOpsTriangleUtilitiesTest(skiatest::Reporter* reporter) { } } -#include "TestClassDef.h" -DEFINE_TESTCLASS_SHORT(PathOpsTriangleUtilitiesTest) +static const SkDTriangle oneOff[] = {
+ {{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753},
+ {279.25839233398438, 7.7416000366210938}}},
+
+ {{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877},
+ {279.25839233398438, 7.7416000366210938}}}
+};
+
+static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff);
+
+static void PathOpsTriangleOneOffTest(skiatest::Reporter* reporter) {
+ for (size_t index = 0; index < oneOff_count; ++index) {
+ const SkDTriangle& triangle = oneOff[index];
+ SkASSERT(ValidTriangle(triangle));
+ for (int inner = 0; inner < 3; ++inner) {
+ bool result = triangle.contains(triangle.fPts[inner]);
+ if (result) {
+ SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner);
+ REPORTER_ASSERT(reporter, 0);
+ }
+ }
+ }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS_SHORT(PathOpsTriangleUtilitiesTest)
+
+DEFINE_TESTCLASS_SHORT(PathOpsTriangleOneOffTest)
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp index 28830ed702..ca6f86507d 100644 --- a/tests/PathOpsExtendedTest.cpp +++ b/tests/PathOpsExtendedTest.cpp @@ -12,6 +12,7 @@ #include "SkForceLinking.h" #include "SkMatrix.h" #include "SkPaint.h" +#include "SkRTConf.h" #include "SkStream.h" #include "SkThreadPool.h" @@ -634,6 +635,10 @@ bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP SK_DECLARE_STATIC_MUTEX(gMutex); 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; diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp index 08ae1b939d..d192e0b359 100644 --- a/tests/PathOpsOpTest.cpp +++ b/tests/PathOpsOpTest.cpp @@ -2718,8 +2718,6 @@ static void skpakmmos_ru100(skiatest::Reporter* reporter) { testPathOp(reporter, path, pathB, kIntersect_PathOp); } -#define SKPS_WORKING 0 -#if SKPS_WORKING static void skpcarpetplanet_ru22(skiatest::Reporter* reporter) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -2744,6 +2742,8 @@ static void skpcarpetplanet_ru22(skiatest::Reporter* reporter) { testPathOp(reporter, path, pathB, kIntersect_PathOp); } +#define SKPS_WORKING 0 +#if SKPS_WORKING static void skpcarrot_is24(skiatest::Reporter* reporter) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -3010,7 +3010,33 @@ static void cubicOp96d(skiatest::Reporter* reporter) { testPathOp(reporter, path, pathB, kDifference_PathOp); } -static void (*firstTest)(skiatest::Reporter* ) = 0; +static void cubicOp97x(skiatest::Reporter* reporter) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(0, 2);
+ path.cubicTo(0, 6, 2, 1, 2, 1);
+ path.close();
+ pathB.setFillType(SkPath::kEvenOdd_FillType);
+ pathB.moveTo(1, 2);
+ pathB.cubicTo(1, 2, 2, 0, 6, 0);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kXOR_PathOp);
+} + +static void cubicOp98x(skiatest::Reporter* reporter) {
+ SkPath path, pathB;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(0, 3);
+ path.cubicTo(3, 6, 4, 1, 6, 3);
+ path.close();
+ pathB.setFillType(SkPath::kEvenOdd_FillType);
+ pathB.moveTo(1, 4);
+ pathB.cubicTo(3, 6, 3, 0, 6, 3);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kXOR_PathOp);
+} + +static void (*firstTest)(skiatest::Reporter* ) = bufferOverflow; static struct TestDesc tests[] = { #if ISSUE_1435_WORKING @@ -3018,11 +3044,13 @@ static struct TestDesc tests[] = { #endif #if SKPS_WORKING TEST(skpcarrot_is24), - TEST(skpcarpetplanet_ru22), // cubic/cubic intersect detects unwanted coincidence #endif #if ISSUE_1417_WORKING_ON_LINUX_32 TEST(issue1417), #endif + TEST(cubicOp98x), + TEST(cubicOp97x), + TEST(skpcarpetplanet_ru22), // cubic/cubic intersect detects unwanted coincidence TEST(cubicOp96d), TEST(cubicOp95u), TEST(skpadbox_lt15), diff --git a/tests/PathOpsQuadIntersectionTest.cpp b/tests/PathOpsQuadIntersectionTest.cpp index 07d2dac854..900123bbd2 100644 --- a/tests/PathOpsQuadIntersectionTest.cpp +++ b/tests/PathOpsQuadIntersectionTest.cpp @@ -53,6 +53,9 @@ static void standardTestCases(skiatest::Reporter* reporter) { } static const SkDQuad testSet[] = { +{{{164, -40}, {231.51681518554687, -40}, {279.25839233398438, 7.7416000366210938}}},
+{{{279.25839233398438, 7.7416000366210938}, {275.2164306640625, 3.6996400356292725}, {271.03286743164062, -5.3290705182007514e-015}}},
+ {{{2.9999997378517067, 1.9737872594345709}, {2.9999997432230918, 1.9739647181863822}, {1.2414155459263587e-163, 5.2957833941332142e-315}}}, {{{2.9999047485265304, 1.9739164225694723}, {3.0000947268526112, 1.9738379076623633}, {0.61149411077591886, 0.0028382324376270418}}}, diff --git a/tests/PathOpsQuadLineIntersectionTest.cpp b/tests/PathOpsQuadLineIntersectionTest.cpp index a871417f1f..4793a13a20 100644 --- a/tests/PathOpsQuadLineIntersectionTest.cpp +++ b/tests/PathOpsQuadLineIntersectionTest.cpp @@ -59,6 +59,9 @@ static struct oneLineQuad { SkDQuad quad; SkDLine line; } oneOffs[] = { + {{{{447.96701049804687, 894.4381103515625}, {448.007080078125, 894.4239501953125},
+ {448.0140380859375, 894.4215087890625}}},
+ {{{490.43548583984375, 879.40740966796875}, {405.59262084960937, 909.435546875}}}}, {{{{142.589081, 102.283646}, {149.821579, 100}, {158, 100}}}, {{{90, 230}, {160, 60}}}}, {{{{1101, 10}, {1101, 8.3431453704833984}, {1099.828857421875, 7.1711997985839844}}}, @@ -94,7 +97,7 @@ static void testOneOffs(skiatest::Reporter* reporter) { } } -static void PathOpsQuadLineIntersectionTestOne(skiatest::Reporter* reporter) { +static void PathOpsQuadLineIntersectionOneOffTest(skiatest::Reporter* reporter) { testOneOffs(reporter); } @@ -148,4 +151,4 @@ static void PathOpsQuadLineIntersectionTest(skiatest::Reporter* reporter) { #include "TestClassDef.h" DEFINE_TESTCLASS_SHORT(PathOpsQuadLineIntersectionTest) -DEFINE_TESTCLASS_SHORT(PathOpsQuadLineIntersectionTestOne) +DEFINE_TESTCLASS_SHORT(PathOpsQuadLineIntersectionOneOffTest) diff --git a/tests/PathOpsSkpClipTest.cpp b/tests/PathOpsSkpClipTest.cpp index d2fa988c6c..7905faa9a5 100755 --- a/tests/PathOpsSkpClipTest.cpp +++ b/tests/PathOpsSkpClipTest.cpp @@ -1,207 +1,573 @@ -#include "PathOpsExtendedTest.h" -#include "PathOpsThreadedCommon.h" + #include "SkBitmap.h" +#include "SkCanvas.h" #include "SkColor.h" +#include "SkColorPriv.h" #include "SkDevice.h" -#include "SkCanvas.h" +#include "SkGraphics.h" #include "SkImageDecoder.h" #include "SkImageEncoder.h" -#include "SkStream.h" #include "SkOSFile.h" +#include "SkPathOpsDebug.h" #include "SkPicture.h" +#include "SkRTConf.h" +#include "SkStream.h" #include "SkString.h" +#include "SkTArray.h" +#include "SkTDArray.h" +#include "SkThreadPool.h" +#include "SkTime.h" +#include "Test.h" #ifdef SK_BUILD_FOR_WIN #define PATH_SLASH "\\" - #define IN_DIR "D:" PATH_SLASH "skp" - #define OUT_DIR "D:" PATH_SLASH + #define IN_DIR "D:\\9-30-13\\" + #define OUT_DIR "D:\\opSkpClip\\1\\" #else #define PATH_SLASH "/" - #if 1 - #define IN_DIR "/usr/local/google/home/caryclark/new10k" PATH_SLASH - #define OUT_DIR "/usr/local/google/home/caryclark/out10k" PATH_SLASH + #ifdef SK_BUILD_FOR_MAC + #define IN_DIR "/Volumes/tera/9-30-13/skp" + #define OUT_DIR "/Volumes/tera/out/9-30-13/1/" #else - #define IN_DIR "/usr/local/google/home/caryclark/6-18-13" PATH_SLASH - #define OUT_DIR "/usr/local/google/home/caryclark" PATH_SLASH + #define IN_DIR "/usr/local/google/home/caryclark/skps/9-30-13/skp" + #define OUT_DIR "/mnt/skia/opSkpClip/1/" #endif #endif -static const char pictDir[] = IN_DIR ; -static const char outSkpClipDir[] = OUT_DIR "skpClip"; -static const char outOldClipDir[] = OUT_DIR "oldClip"; +const struct { + int directory; + const char* filename; +} skipOverSept[] = { + {9, "http___www_symptome_ch_.skp"}, // triangle clip with corner at x.999 + {11, "http___www_menly_fr_.skp"}, + {12, "http___www_banrasdr_com_.skp"}, +}; + +size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]); + +enum TestStep { + kCompareBits, + kEncodeFiles, +}; + +enum { + kMaxLength = 128, + kMaxFiles = 128, + kSmallLimit = 1000, +}; + +struct TestResult { + void init(int dirNo) { + fDirNo = dirNo; + sk_bzero(fFilename, sizeof(fFilename)); + fTestStep = kCompareBits; + fScaleOversized = true; + } + + SkString status() { + SkString outStr; + outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime); + return outStr; + } + + static void Test(int dirNo, const char* filename, TestStep testStep) { + TestResult test; + test.init(dirNo); + test.fTestStep = testStep; + strcpy(test.fFilename, filename); + test.testOne(); + } + + void test(int dirNo, const SkString& filename) { + init(dirNo); + strcpy(fFilename, filename.c_str()); + testOne(); + } + + void testOne(); + + char fFilename[kMaxLength]; + TestStep fTestStep; + int fDirNo; + int fPixelError; + int fTime; + bool fScaleOversized; +}; + +struct TestState { + void init(int dirNo, skiatest::Reporter* reporter) { + fReporter = reporter; + fResult.init(dirNo); + fFoundCount = 0; + TestState::fSmallCount = 0; + fSmallestError = 0; + sk_bzero(fFilesFound, sizeof(fFilesFound)); + sk_bzero(fDirsFound, sizeof(fDirsFound)); + sk_bzero(fError, sizeof(fError)); + } + + static bool bumpSmallCount() { + sk_atomic_inc(&fSmallCount); + return fSmallCount > kSmallLimit; + } + + static void clearSmallCount() { + if (fSmallCount < kSmallLimit) { + fSmallCount = 0; + } + } + + char fFilesFound[kMaxFiles][kMaxLength]; + int fDirsFound[kMaxFiles]; + int fError[kMaxFiles]; + int fFoundCount; + static int fSmallCount; + int fSmallestError; + skiatest::Reporter* fReporter; + TestResult fResult; +}; + +int TestState::fSmallCount; + +struct TestRunner { + TestRunner(skiatest::Reporter* reporter, int threadCount) + : fNumThreads(threadCount) + , fReporter(reporter) { + } + + ~TestRunner(); + void render(); + int fNumThreads; + SkTDArray<class TestRunnable*> fRunnables; + skiatest::Reporter* fReporter; +}; + +class TestRunnable : public SkRunnable { +public: + TestRunnable(void (*testFun)(TestState*), int dirNo, TestRunner* runner) { + fState.init(dirNo, runner->fReporter); + fTestFun = testFun; + } + + virtual void run() SK_OVERRIDE { + SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); + (*fTestFun)(&fState); + } + + TestState fState; + void (*fTestFun)(TestState*); +}; + +TestRunner::~TestRunner() { + for (int index = 0; index < fRunnables.count(); index++) { + SkDELETE(fRunnables[index]); + } +} + +void TestRunner::render() { + SkThreadPool pool(fNumThreads); + for (int index = 0; index < fRunnables.count(); ++ index) { + pool.add(fRunnables[index]); + } +} + +//////////////////////////////////////////////// + +static const char outOpDir[] = OUT_DIR "opClip"; +static const char outOldDir[] = OUT_DIR "oldClip"; +static const char outSkpDir[] = OUT_DIR "skpTest"; +static const char outDiffDir[] = OUT_DIR "outTest"; +static const char outStatusDir[] = OUT_DIR "statusTest"; -static SkString make_filepath(const char* dir, const SkString& name) { +static SkString make_filepath(int dirNo, const char* dir, const char* name) { SkString path(dir); - size_t len = strlen(dir); - if (len > 0 && dir[len - 1] != PATH_SLASH[0]) { - path.append(PATH_SLASH); + if (dirNo) { + path.appendf("%d", dirNo); } + path.append(PATH_SLASH); path.append(name); return path; } -static SkString make_png_name(const SkString& filename) { +static SkString make_in_dir_name(int dirNo) { + SkString dirName(IN_DIR); + dirName.appendf("%d", dirNo); + if (!sk_exists(dirName.c_str())) { + SkDebugf("could not read dir %s\n", dirName.c_str()); + return SkString(); + } + return dirName; +} + +static bool make_one_out_dir(const char* outDirStr) { + SkString outDir = make_filepath(0, outDirStr, ""); + if (!sk_exists(outDir.c_str())) { + if (!sk_mkdir(outDir.c_str())) { + SkDebugf("could not create dir %s\n", outDir.c_str()); + return false; + } + } + return true; +} + +static bool make_out_dirs() { + SkString outDir = make_filepath(0, OUT_DIR, ""); + if (!sk_exists(outDir.c_str())) { + if (!sk_mkdir(outDir.c_str())) { + SkDebugf("could not create dir %s\n", outDir.c_str()); + return false; + } + } + return make_one_out_dir(outOldDir) + && make_one_out_dir(outOpDir) + && make_one_out_dir(outSkpDir) + && make_one_out_dir(outDiffDir) + && make_one_out_dir(outStatusDir); +} + +static SkString make_png_name(const char* filename) { SkString pngName = SkString(filename); pngName.remove(pngName.size() - 3, 3); pngName.append("png"); return pngName; } -static void testOne(const SkString& filename) { - if (filename == SkString("http___migracioncolombia_gov_co.skp") - || filename == SkString("http___miuki_info.skp") - ) { - return; +static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { + const int kRowCount = 3; + const int kThreshold = 3; + int width = SkTMin(gr.width(), sk.width()); + if (width < kRowCount) { + return true; } -#if DEBUG_SHOW_TEST_NAME - SkString testName(filename); - const char http[] = "http"; - if (testName.startsWith(http)) { - testName.remove(0, sizeof(http) - 1); + int height = SkTMin(gr.height(), sk.height()); + if (height < kRowCount) { + return true; } - while (testName.startsWith("_")) { - testName.remove(0, 1); + int errorTotal = 0; + SkTArray<int, true> errorRows; + errorRows.push_back_n(width * kRowCount); + SkAutoLockPixels autoGr(gr); + SkAutoLockPixels autoSk(sk); + for (int y = 0; y < height; ++y) { + SkPMColor* grRow = gr.getAddr32(0, y); + SkPMColor* skRow = sk.getAddr32(0, y); + int* base = &errorRows[0]; + int* cOut = &errorRows[y % kRowCount]; + for (int x = 0; x < width; ++x) { + SkPMColor grColor = grRow[x]; + SkPMColor skColor = skRow[x]; + int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); + int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); + int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); + int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); + if (error < kThreshold || x < 2) { + continue; + } + if (base[x - 2] < kThreshold + || base[width + x - 2] < kThreshold + || base[width * 2 + x - 2] < kThreshold + || base[x - 1] < kThreshold + || base[width + x - 1] < kThreshold + || base[width * 2 + x - 1] < kThreshold + || base[x] < kThreshold + || base[width + x] < kThreshold + || base[width * 2 + x] < kThreshold) { + continue; + } + errorTotal += error; + } } - const char dotSkp[] = ".skp"; - if (testName.endsWith(dotSkp)) { - size_t len = testName.size(); - testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); + return errorTotal; +} + +static bool addError(TestState* data, const TestResult& testResult) { + bool foundSmaller = false; + int dCount = data->fFoundCount; + int pixelError = testResult.fPixelError; + if (data->fFoundCount < kMaxFiles) { + data->fError[dCount] = pixelError; + strcpy(data->fFilesFound[dCount], testResult.fFilename); + data->fDirsFound[dCount] = testResult.fDirNo; + ++data->fFoundCount; + } else if (pixelError > data->fSmallestError) { + int smallest = SK_MaxS32; + int smallestIndex = 0; + for (int index = 0; index < kMaxFiles; ++index) { + if (smallest > data->fError[index]) { + smallest = data->fError[index]; + smallestIndex = index; + } + } + data->fError[smallestIndex] = pixelError; + strcpy(data->fFilesFound[smallestIndex], testResult.fFilename); + data->fDirsFound[smallestIndex] = testResult.fDirNo; + data->fSmallestError = SK_MaxS32; + for (int index = 0; index < kMaxFiles; ++index) { + if (data->fSmallestError > data->fError[index]) { + data->fSmallestError = data->fError[index]; + } + } + SkDebugf("*%d*", data->fSmallestError); + foundSmaller = true; } - testName.prepend("skp"); - testName.append("1"); - strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); -#endif - SkString path = make_filepath(pictDir, filename); - SkFILEStream stream(path.c_str()); - if (!stream.isValid()) { - return; + return foundSmaller; +} + + + +static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { + canvas->save(); + int pWidth = pic->width(); + int pHeight = pic->height(); + const int maxDimension = 1000; + const int slices = 3; + int xInterval = SkTMax(pWidth - maxDimension, 0) / (slices - 1); + int yInterval = SkTMax(pHeight - maxDimension, 0) / (slices - 1); + SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)), + SkIntToScalar(SkTMin(maxDimension, pHeight))}; + canvas->clipRect(rect); + SkMSec start = SkTime::GetMSecs(); + for (int x = 0; x < slices; ++x) { + for (int y = 0; y < slices; ++y) { + pic->draw(canvas); + canvas->translate(0, SkIntToScalar(yInterval)); + } + canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices)); } - SkPicture* pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); - if (!pic) { - SkDebugf("unable to decode %s\n", filename.c_str()); - return; + SkMSec end = SkTime::GetMSecs(); + canvas->restore(); + return end - start; +} + +static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { + canvas->clear(SK_ColorWHITE); + if (scale != 1) { + canvas->save(); + canvas->scale(1.0f / scale, 1.0f / scale); } - int width = pic->width(); - int height = pic->height(); - - SkBitmap bitmap; - int scale = 1; - do { - bitmap.setConfig(SkBitmap::kARGB_8888_Config, (width + scale - 1) / scale, - (height + scale - 1) / scale); - bool success = bitmap.allocPixels(); - bitmap.eraseColor(SK_ColorWHITE); - if (success) { - break; - } - SkDebugf("-%d-", scale); - } while ((scale *= 2) < 32); - if (scale >= 32) { - SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", filename.c_str(), - width, height); - return; + pic->draw(canvas); + if (scale != 1) { + canvas->restore(); + } +} + +static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { + SkString outFile = make_filepath(0, outDir, pngName); + if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, + SkImageEncoder::kPNG_Type, 100)) { + SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName, + bitmap.width(), bitmap.height()); } - SkCanvas canvas(bitmap); - canvas.scale(1.0f / scale, 1.0f / scale); - SkString pngName = make_png_name(filename); - for (int i = 0; i < 2; ++i) { - bool useOp = i ? true : false; - canvas.setAllowSimplifyClip(useOp); - pic->draw(&canvas); - SkString outFile = make_filepath(useOp ? outSkpClipDir : outOldClipDir, pngName); - if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, - 100)) { - SkDebugf("unable to encode %s (width=%d height=%d)\n", pngName.c_str(), - bitmap.width(), bitmap.height()); +} + +void TestResult::testOne() { + SkPicture* pic = NULL; + { + #if DEBUG_SHOW_TEST_NAME + if (fTestStep == kCompareBits) { + SkString testName(fFilename); + const char http[] = "http"; + if (testName.startsWith(http)) { + testName.remove(0, sizeof(http) - 1); + } + while (testName.startsWith("_")) { + testName.remove(0, 1); + } + const char dotSkp[] = ".skp"; + if (testName.endsWith(dotSkp)) { + size_t len = testName.size(); + testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1); + } + testName.prepend("skp"); + testName.append("1"); + strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH); + } else if (fTestStep == kEncodeFiles) { + strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); + } + #endif + SkString path = make_filepath(fDirNo, IN_DIR, fFilename); + SkFILEStream stream(path.c_str()); + if (!stream.isValid()) { + SkDebugf("invalid stream %s\n", path.c_str()); + goto finish; + } + SkPicture* pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); + if (!pic) { + SkDebugf("unable to decode %s\n", fFilename); + goto finish; + } + int width = pic->width(); + int height = pic->height(); + SkBitmap oldBitmap, opBitmap; + int scale = 1; + do { + int dimX = (width + scale - 1) / scale; + int dimY = (height + scale - 1) / scale; + oldBitmap.setConfig(SkBitmap::kARGB_8888_Config, dimX, dimY); + opBitmap.setConfig(SkBitmap::kARGB_8888_Config, dimX, dimY); + bool success = oldBitmap.allocPixels() && opBitmap.allocPixels(); + if (success) { + break; + } + SkDebugf("-%d-", scale); + } while ((scale *= 2) < 256); + if (scale >= 256) { + SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", fFilename, + width, height); + return; + } + oldBitmap.eraseColor(SK_ColorWHITE); + SkCanvas oldCanvas(oldBitmap); + oldCanvas.setAllowSimplifyClip(false); + opBitmap.eraseColor(SK_ColorWHITE); + SkCanvas opCanvas(opBitmap); + opCanvas.setAllowSimplifyClip(true); + drawPict(pic, &oldCanvas, fScaleOversized ? scale : 1); + drawPict(pic, &opCanvas, fScaleOversized ? scale : 1); + if (fTestStep == kCompareBits) { + fPixelError = similarBits(oldBitmap, opBitmap); + int oldTime = timePict(pic, &oldCanvas); + int opTime = timePict(pic, &opCanvas); + fTime = oldTime - opTime; + } else if (fTestStep == kEncodeFiles) { + SkString pngStr = make_png_name(fFilename); + const char* pngName = pngStr.c_str(); + writePict(oldBitmap, outOldDir, pngName); + writePict(opBitmap, outOpDir, pngName); } } +finish: SkDELETE(pic); } -const char* tryFixed[] = { - 0 -}; +static SkString makeStatusString(int dirNo) { + SkString statName; + statName.printf("stats%d.txt", dirNo); + SkString statusFile = make_filepath(0, outStatusDir, statName.c_str()); + return statusFile; +} -size_t tryFixedCount = sizeof(tryFixed) / sizeof(tryFixed[0]); - -const char* skipOver[] = { - "http___carpetplanet_ru.skp", // cubic/cubic intersect - "http___carrot_is.skp", // bridgeOp() SkASSERT(unsortable || !current->done()); - -/*!*/"http___dotsrc_org.skp", // asserts in png decode - "http___frauen_magazin_com.skp", // bridgeOp() SkASSERT(unsortable || !current->done()); - "http___i_gino_com.skp", // unexpected cubic/quad coincidence - // {61, 857, 61, 789.06897, 116.068977, 734, 184, 734} - // {184, 734, 133.051727, 734, 97.0258636, 770.025879} - "http___ilkoora_com.skp", // assert wind sum != min32 from markDoneBinary / findNextOp #28k -/*!*/"http___migracioncolombia_gov_co.skp", // crashes on picture decode - "http___mm4everfriends_com.skp", // bumpSpan/addTCoincident (from calcPartialCoincidentWinding) - "http___mtrk_uz.skp", // checkEnds() assert #36.3k - "http___pchappy_com_au.skp", // bridgeOp() assert unsortable || ! empty #37.2k - "http___sciality_com.skp", // bridgeOp() SkASSERT(unsortable || !current->done()); #32.4k -/*!*/"http___sozialticker_com.skp", // asserts in png decode - "http___sudoestenegocios_com.skp", // assert fT < 1 in addTCoincident - "http___thesuburbanite_com.skp", // bridgeOp() SkASSERT(unsortable || !current->done()); - - "http___fluentin3months_com.skp", // calcCommonCoincidentWinding from calcPartialCoincidentWinding #38.3k - "http___teachersbadi_blogspot_in.skp", // calcCommonCoincidentWinding from calcPartialCoincidentWinding #53.4k - "http___wsms_ru.skp", // assert wind sum != min32 from markDoneBinary / findNextOp #49.5k - "http___voycer_de.skp", // calcCommonCoincidentWinding from calcPartialCoincidentWinding #47k - "http___77hz_jp.skp", // addTCancel from calcCoincidentWinding #47.1k - - "http___hostloco_com.skp", // t < 0 AddIntersectsT -/*!*/"http___oggicronaca_it.skp", // asserts in png decode - "http___sergeychunkevich_com.skp", // t < 0 AddIntersectsT - "http___tracksflow_com.skp", // assert otherEnd >= 0 from nextChase - "http___autobutler_dk.skp", // t < 0 AddIntersectsT - "http___onlinecollege_org.skp", // bridgeOp() assert unsortable || ! empty #100.1k - "http___national_com_au.skp", // bridgeOp() assert unsortable || ! empty #110.2k -/*!*/"http___anitadongre_com.skp", // exceptionally large width and height - "http___rentacheat_com.skp", // bridgeOp() assert unsortable || ! empty #110.8k -/*!*/"http___gruesse_de.skp", // asserts in png decode -/*!*/"http___crn_in.png", // width=1250047 - "http___breakmystyle_com.skp", // assert qPt == lPt in quad intersection - "http___naoxrane_ru.skp", // assert t4+...t0 == 0 in quartic roots #128.3k - "http___tcmevents_org.skp", // assert in addTCoincident (from calcPartialCoincidentWinding) #143.3k -/*!*/"http___listbuildingcashsecrets_com.skp", // asserts in png decode #152.7k -/*!*/"http___skyscraperpage_com.skp", // asserts in png decode #155.5k - "http___mlk_com.skp", // bridgeOp() assert unsortable || ! empty #158.7k - "http___sd_graphic_net.skp", // bridgeOp() assert unsortable || ! empty #163.3k - "http___kopepasah_com.skp", // checkEnds() assert #188.2k -/*!*/"http___darkreloaded_com.skp", // asserts in png decode #188.4k - "http___redbullskatearcade_es.skp", // bridgeOp() assert unsortable || ! empty #192.5k - "http___partainasdemo250_org.skp", // bridgeOp() assert unsortable || ! empty #200.2k - -// these failures are from the new 10k set - "http___www_freerepublic_com_.skp", // assert in opangle < - "http___www_lavoixdunord_fr_.skp", // bridgeOp() assert unsortable || ! empty - "http___www_booking_com_.skp", // bridgeOp() assert unsortable || ! empty - "http___www_fj_p_com_.skp", // markWinding assert from findChaseOp - "http___www_leadpages_net_.skp", // assert in opangle < - "http___www_despegar_com_mx_.skp", // bridgeOp() assert unsortable || ! empty -}; +class PreParser { +public: + PreParser(int dirNo) + : fDirNo(dirNo) + , fIndex(0) { + SkString statusPath = makeStatusString(dirNo); + if (!sk_exists(statusPath.c_str())) { + return; + } + SkFILEStream reader; + reader.setPath(statusPath.c_str()); + while (fetch(reader, &fResults.push_back())) + ; + fResults.pop_back(); + } + + bool fetch(SkFILEStream& reader, TestResult* result) { + char c; + int i = 0; + result->init(fDirNo); + result->fPixelError = 0; + result->fTime = 0; + do { + bool readOne = reader.read(&c, 1) != 0; + if (!readOne) { + SkASSERT(i == 0); + return false; + } + if (c == ' ') { + result->fFilename[i++] = '\0'; + break; + } + result->fFilename[i++] = c; + SkASSERT(i < kMaxLength); + } while (true); + do { + SkAssertResult(reader.read(&c, 1)); + if (c == ' ') { + break; + } + SkASSERT(c >= '0' && c <= '9'); + result->fPixelError = result->fPixelError * 10 + (c - '0'); + } while (true); + bool minus = false; + do { + SkAssertResult(reader.read(&c, 1)); + if (c == '\n') { + break; + } + if (c == '-') { + minus = true; + continue; + } + SkASSERT(c >= '0' && c <= '9'); + result->fTime = result->fTime * 10 + (c - '0'); + } while (true); + if (minus) { + result->fTime = -result->fTime; + } + return true; + } -size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]); + bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) { + if (fIndex < fResults.count()) { + *result = fResults[fIndex++]; + SkASSERT(filename.equals(result->fFilename)); + SkString outStr(result->status()); + stream->write(outStr.c_str(), outStr.size()); + return true; + } + return false; + } -static void PathOpsSkpClipTest(skiatest::Reporter* reporter) { - SkOSFile::Iter iter(pictDir, "skp"); +private: + int fDirNo; + int fIndex; + SkTArray<TestResult, true> fResults; +}; + +static bool doOneDir(TestState* state) { + int dirNo = state->fResult.fDirNo; + skiatest::Reporter* reporter = state->fReporter; + SkString dirName = make_in_dir_name(dirNo); + SkASSERT(dirName.size()); + SkOSFile::Iter iter(dirName.c_str(), "skp"); SkString filename; int testCount = 0; + PreParser preParser(dirNo); + SkFILEWStream statusStream(makeStatusString(dirNo).c_str()); while (iter.next(&filename)) { - SkString pngName = make_png_name(filename); - SkString oldPng = make_filepath(outOldClipDir, pngName); - SkString newPng = make_filepath(outSkpClipDir, pngName); - if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) { - reporter->bumpTestCount(); - continue; - } - for (size_t index = 0; index < skipOverCount; ++index) { - if (skipOver[index] && strcmp(filename.c_str(), skipOver[index]) == 0) { - reporter->bumpTestCount(); + for (size_t index = 0; index < skipOverSeptCount; ++index) { + if (skipOverSept[index].directory == dirNo + && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) { goto skipOver; } } - testOne(filename); + if (preParser.match(filename, &statusStream, &state->fResult)) { + addError(state, state->fResult); + ++testCount; + goto checkEarlyExit; + } + if (state->fSmallestError > 5000000) { + return false; + } + { + TestResult& result = state->fResult; + result.test(dirNo, filename); + SkString outStr(result.status()); + statusStream.write(outStr.c_str(), outStr.size()); + statusStream.flush(); + if (1) { + SkDebugf("%s", outStr.c_str()); + } + bool noMatch = addError(state, state->fResult); + if (noMatch) { + state->clearSmallCount(); + } else if (state->bumpSmallCount()) { + return false; + } + } + ++testCount; if (reporter->verbose()) { SkDebugf("."); if (++testCount % 100 == 0) { @@ -209,85 +575,112 @@ static void PathOpsSkpClipTest(skiatest::Reporter* reporter) { } } skipOver: - reporter->bumpTestCount(); + if (reporter->verbose()) { + static int threadTestCount; + SkDebugf("."); + sk_atomic_inc(&threadTestCount); + if (threadTestCount % 100 == 0) { + SkDebugf("%d\n", threadTestCount); + } + } +checkEarlyExit: + if (1 && testCount == 20) { + return true; + } } + return true; +} + +static bool initTest() { +#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC + SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); + SK_CONF_SET("images.png.suppressDecoderWarnings", true); +#endif + return make_out_dirs(); } -static void bumpCount(skiatest::Reporter* reporter, bool skipping) { +static void encodeFound(skiatest::Reporter* reporter, TestState& state) { if (reporter->verbose()) { - static int threadTestCount; - if (!skipping) { - SkDebugf("."); + for (int index = 0; index < state.fFoundCount; ++index) { + SkDebugf("%d %s %d\n", state.fDirsFound[index], state.fFilesFound[index], + state.fError[index]); } - sk_atomic_inc(&threadTestCount); - if (!skipping && threadTestCount % 100 == 0) { - SkDebugf("%d\n", threadTestCount); + } + for (int index = 0; index < state.fFoundCount; ++index) { + TestResult::Test(state.fDirsFound[index], state.fFilesFound[index], kEncodeFiles); + if (state.fReporter->verbose()) SkDebugf("+"); + } +} + +static void PathOpsSkpClipTest(skiatest::Reporter* reporter) { + if (!initTest()) { + return; + } + SkTArray<TestResult, true> errors; + TestState state; + state.init(0, reporter); + for (int dirNo = 1; dirNo <= 100; ++dirNo) { + if (reporter->verbose()) { + SkDebugf("dirNo=%d\n", dirNo); } - if (skipping && threadTestCount % 10000 == 0) { - SkDebugf("%d\n", threadTestCount); + state.fResult.fDirNo = dirNo; + if (!doOneDir(&state)) { + break; } } + encodeFound(reporter, state); } -static void testSkpClipMain(PathOpsThreadState* data) { - SkString str(data->fSerialNo); - testOne(str); - bumpCount(data->fReporter, false); - data->fReporter->bumpTestCount(); +static void testSkpClipMain(TestState* data) { + (void) doOneDir(data); } static void PathOpsSkpClipThreadedTest(skiatest::Reporter* reporter) { - int threadCount = initializeTests(reporter, "skpClipThreadedTest"); - PathOpsThreadedTestRunner testRunner(reporter, threadCount); - SkOSFile::Iter iter(pictDir, "skp"); - SkString filename; - while (iter.next(&filename)) { - SkString pngName = make_png_name(filename); - SkString oldPng = make_filepath(outOldClipDir, pngName); - SkString newPng = make_filepath(outSkpClipDir, pngName); - if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) { - bumpCount(reporter, true); - continue; - } - for (size_t index = 0; index < skipOverCount; ++index) { - if (skipOver[index] && strcmp(filename.c_str(), skipOver[index]) == 0) { - bumpCount(reporter, true); - goto skipOver; - } - } - *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable, - (&testSkpClipMain, filename.c_str(), &testRunner)); -skipOver: - ; + if (!initTest()) { + return; + } + int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1; + TestRunner testRunner(reporter, threadCount); + for (int dirNo = 1; dirNo <= 100; ++dirNo) { + *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnable, + (&testSkpClipMain, dirNo, &testRunner)); } testRunner.render(); -} - -static void PathOpsSkpClipFixedTest(skiatest::Reporter* reporter) { - for (size_t index = 0; index < tryFixedCount; ) { - SkString filename(tryFixed[index]); - testOne(filename); - ++index; - if (reporter->verbose()) { - SkDebugf("."); - if (index % 100 == 0) { - SkDebugf("\n"); - } + TestState state; + state.init(0, reporter); + for (int dirNo = 1; dirNo <= 100; ++dirNo) { + TestState& testState = testRunner.fRunnables[dirNo - 1]->fState; + for (int inner = 0; inner < testState.fFoundCount; ++inner) { + TestResult& testResult = testState.fResult; + SkASSERT(testResult.fDirNo == dirNo); + testResult.fPixelError = testState.fError[inner]; + strcpy(testResult.fFilename, testState.fFilesFound[inner]); + addError(&state, testResult); } - reporter->bumpTestCount(); } + encodeFound(reporter, state); } static void PathOpsSkpClipOneOffTest(skiatest::Reporter* reporter) { - SkString filename("http___78_cn_.skp"); - testOne(filename); + if (!initTest()) { + return; + } + const int testIndex = 43 - 41; + int dirNo = skipOverSept[testIndex].directory; + SkAssertResult(make_in_dir_name(dirNo).size()); + SkString filename(skipOverSept[testIndex].filename); + TestResult state; + state.test(dirNo, filename); + if (reporter->verbose()) { + SkDebugf("%s", state.status().c_str()); + } + state.fTestStep = kEncodeFiles; + state.testOne(); } #include "TestClassDef.h" DEFINE_TESTCLASS_SHORT(PathOpsSkpClipTest) -DEFINE_TESTCLASS_SHORT(PathOpsSkpClipFixedTest) - DEFINE_TESTCLASS_SHORT(PathOpsSkpClipOneOffTest) DEFINE_TESTCLASS_SHORT(PathOpsSkpClipThreadedTest) diff --git a/tests/PathOpsSkpTest.cpp b/tests/PathOpsSkpTest.cpp index b0feff7ef1..7eb0a54ec3 100755 --- a/tests/PathOpsSkpTest.cpp +++ b/tests/PathOpsSkpTest.cpp @@ -508,8 +508,6 @@ static void skpmtrk_uz27(skiatest::Reporter* reporter) { testPathOp(reporter, path, pathB, kIntersect_PathOp); } -#define TRY_BROKEN_TESTS 0 -#if TRY_BROKEN_TESTS static void skpfrauen_magazin_com83(skiatest::Reporter* reporter) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -534,6 +532,8 @@ static void skpfrauen_magazin_com83(skiatest::Reporter* reporter) { testPathOp(reporter, path, pathB, kIntersect_PathOp); } +#define TRY_BROKEN_TESTS 0 +#if TRY_BROKEN_TESTS static void skpi_gino_com16(skiatest::Reporter* reporter) { SkPath path; path.setFillType(SkPath::kEvenOdd_FillType); @@ -614,6 +614,7 @@ static void skpsciality_com161(skiatest::Reporter* reporter) { pathB.close(); testPathOp(reporter, path, pathB, kIntersect_PathOp); } +#endif static void skpsudoestenegocios_com186(skiatest::Reporter* reporter) { SkPath path; @@ -665,18 +666,933 @@ static void skpthesuburbanite_com213(skiatest::Reporter* reporter) { pathB.close(); testPathOp(reporter, path, pathB, kIntersect_PathOp); } + +static void skphostloco_com11(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(6.66133815e-16f, 648); + path.lineTo(25.8522835f, 648); + path.quadTo(27.5087376f, 647.999634f, 28.6807098f, 646.82843f); + path.quadTo(29.8518829f, 645.656433f, 29.8522835f, 644); + path.lineTo(29.8522835f, 467); + path.quadTo(29.8518829f, 465.343536f, 28.6807098f, 464.17157f); + path.quadTo(27.5087376f, 463.000397f, 25.8522835f, 463); + path.lineTo(2.22044605e-16f, 463); + path.lineTo(6.66133815e-16f, 648); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(0, 463); + pathB.lineTo(30, 463); + pathB.lineTo(30, 648); + pathB.lineTo(0, 648); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpsergeychunkevich_com8(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(0, 926); + path.lineTo(0, 0); + path.lineTo(1265, 0); + path.lineTo(1265, 926); + path.lineTo(0, 926); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(37, 374); + pathB.lineTo(37, 535); + pathB.cubicTo(37, 536.65686f, 35.6568565f, 538, 34, 538); + pathB.lineTo(1.02866934e-14f, 538); + pathB.lineTo(6.12303177e-17f, 371); + pathB.lineTo(34, 371); + pathB.cubicTo(35.6568565f, 371, 37, 372.34314f, 37, 374); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skptracksflow_com9(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(16, 56); + path.lineTo(32, 56); + path.lineTo(32, 72); + path.lineTo(16, 72); + path.lineTo(16, 56); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kEvenOdd_FillType); + pathB.moveTo(31.65625f, 70.0555649f); + pathB.lineTo(31.65625f, 70.0554962f); + pathB.lineTo(26.9727192f, 65.3615341f); + pathB.cubicTo(27.6210003f, 64.4029694f, 28.0048752f, 63.2470932f, 28.0048752f, 62.0027809f); + pathB.cubicTo(28.0048752f, 58.6875305f, 25.3199062f, 56, 22.0046558f, 56); + pathB.cubicTo(18.6894073f, 56, 16.0031872f, 58.6875305f, 16.0031872f, 62.0027809f); + pathB.cubicTo(16.0031872f, 65.3180008f, 18.6913433f, 68.0055618f, 22.0066261f, 68.0055618f); + pathB.cubicTo(23.2509995f, 68.0055618f, 24.4072189f, 67.6187515f, 25.3657818f, 66.9704056f); + pathB.lineTo(30.0599365f, 71.65625f); + pathB.lineTo(30.0600014f, 71.65625f); + pathB.cubicTo(30.2668133f, 71.875f, 30.5524693f, 71.9992828f, 30.868f, 71.9992828f); + pathB.cubicTo(31.4994049f, 71.9992828f, 32.0014687f, 71.4909363f, 32.0014687f, 70.8595276f); + pathB.cubicTo(32.0015335f, 70.5439072f, 31.875f, 70.2623444f, 31.65625f, 70.0555649f); + pathB.close(); + pathB.moveTo(18.0054054f, 62.0027809f); + pathB.cubicTo(18.0054054f, 59.7925949f, 19.7970943f, 58.0009079f, 22.0072823f, 58.0009079f); + pathB.cubicTo(24.2174377f, 58.0009079f, 26.0091248f, 59.7925949f, 26.0091248f, 62.0027809f); + pathB.cubicTo(26.0091248f, 64.2129364f, 24.2174377f, 66.0046234f, 22.0072803f, 66.0046234f); + pathB.cubicTo(19.7970943f, 66.0045929f, 18.0054054f, 64.2129059f, 18.0054054f, 62.0027809f); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpautobutler_dk29(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(0, 926); + path.lineTo(0, 0); + path.lineTo(1265, 0); + path.lineTo(1265, 926); + path.lineTo(0, 926); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(21, 162); + pathB.lineTo(21, 301); + pathB.lineTo(8.57224448e-15f, 301); + pathB.lineTo(6.12303177e-17f, 162); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skponlinecollege_org144(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(179, 407); + path.cubicTo(177.34314f, 407, 176, 408.34314f, 176, 410); + path.lineTo(176, 436); + path.cubicTo(176, 437.65686f, 177.34314f, 439, 179, 439); + path.lineTo(337.002289f, 439); + path.cubicTo(338.105835f, 438.998779f, 339, 438.103821f, 339, 437); + path.lineTo(339, 409); + path.cubicTo(339, 407.896362f, 338.10611f, 407.001526f, 337.002838f, 407); + path.lineTo(179, 407); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(179, 408); + pathB.lineTo(337, 408); + pathB.cubicTo(338.65686f, 408, 340, 408.895416f, 340, 410); + pathB.lineTo(340, 436); + pathB.cubicTo(340, 437.65686f, 338.65686f, 439, 337, 439); + pathB.lineTo(179, 439); + pathB.cubicTo(177.895432f, 439, 177, 437.65686f, 177, 436); + pathB.lineTo(177, 410); + pathB.cubicTo(177, 408.895416f, 177.895432f, 408, 179, 408); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpnational_com_au81(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(807, 817); + path.quadTo(806.585876f, 817.000122f, 806.292908f, 817.292908f); + path.quadTo(806.000122f, 817.585876f, 806, 818); + path.lineTo(806, 881); + path.lineTo(1111, 881); + path.lineTo(1111, 818); + path.quadTo(1110.99988f, 817.585876f, 1110.70715f, 817.292908f); + path.quadTo(1110.41406f, 817.000122f, 1110, 817); + path.lineTo(807, 817); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(807, 817); + pathB.lineTo(1110, 817); + pathB.cubicTo(1110.55225f, 817, 1111, 817.447693f, 1111, 818); + pathB.lineTo(1111, 880); + pathB.lineTo(806, 880); + pathB.lineTo(806, 818); + pathB.cubicTo(806, 817.447693f, 806.447693f, 817, 807, 817); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skprentacheat_com30(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(967, 263); + path.quadTo(966.585876f, 263.000092f, 966.292908f, 263.292908f); + path.quadTo(966.000122f, 263.585876f, 966, 264); + path.lineTo(966, 301); + path.lineTo(1214, 301); + path.lineTo(1214, 264); + path.quadTo(1213.99988f, 263.585876f, 1213.70715f, 263.292908f); + path.quadTo(1213.41406f, 263.000092f, 1213, 263); + path.lineTo(967, 263); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(967, 263); + pathB.lineTo(1213, 263); + pathB.cubicTo(1213.55225f, 263, 1214, 263.447723f, 1214, 264); + pathB.lineTo(1214, 300); + pathB.lineTo(966, 300); + pathB.lineTo(966, 264); + pathB.cubicTo(966, 263.447723f, 966.447693f, 263, 967, 263); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpbreakmystyle_com10(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(271.032867f, -5.32907052e-15f); + path.lineTo(56.9671326f, -5.16253706e-15f); + path.quadTo(52.7835083f, 3.69968891f, 48.7416f, 7.74160004f); + path.quadTo(1, 55.4831848f, 1, 123); + path.quadTo(1, 190.516815f, 48.7416f, 238.258392f); + path.quadTo(96.4831848f, 286, 164, 286); + path.quadTo(231.516815f, 286, 279.258392f, 238.258392f); + path.quadTo(327, 190.516815f, 327, 123); + path.quadTo(327, 55.4831848f, 279.258392f, 7.74160004f); + path.quadTo(275.216431f, 3.69964004f, 271.032867f, -5.32907052e-15f); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(327, 123); + pathB.quadTo(327, 190.516815f, 279.258392f, 238.258392f); + pathB.quadTo(231.516815f, 286, 164, 286); + pathB.quadTo(96.4831848f, 286, 48.7416f, 238.258392f); + pathB.quadTo(1, 190.516815f, 1, 123); + pathB.quadTo(1, 55.4831848f, 48.7416f, 7.74160004f); + pathB.quadTo(96.4831848f, -40, 164, -40); + pathB.quadTo(231.516815f, -40, 279.258392f, 7.74160004f); + pathB.quadTo(327, 55.4831848f, 327, 123); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpsd_graphic_net104(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(475.421448f, 836.985962f); + path.lineTo(461.280975f, 841.990662f); + path.cubicTo(466.80899f, 857.609802f, 458.62854f, 874.752991f, 443.009399f, 880.281006f); + path.cubicTo(435.199829f, 883.044983f, 427.009247f, 882.381897f, 420.080048f, 879.075378f); + path.lineTo(413.620056f, 892.613037f); + path.quadTo(430.419983f, 900.629761f, 447.96701f, 894.43811f); + path.quadTo(448.00708f, 894.42395f, 448.014038f, 894.421509f); + path.quadTo(448.043976f, 894.410889f, 448.061066f, 894.404846f); + path.quadTo(465.596313f, 888.179932f, 473.613037f, 871.379944f); + path.quadTo(477.351227f, 863.546143f, 478, 855.549866f); + path.lineTo(478, 848.804321f); + path.quadTo(477.528076f, 842.93811f, 475.421448f, 836.985962f); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(405.592621f, 909.435547f); + pathB.lineTo(390.578583f, 867.014099f); + pathB.lineTo(433, 852.000061f); + pathB.lineTo(490.435486f, 879.40741f); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +#if TRY_BROKEN_TESTS +/* this cubic/quad pair + c = 430,280 430,278.895416 473.876068,278 528,278 + q = 430,280 430.009796,277.101196 458.703552,275.050262 + only intersect at the shared point (430,280) + they sort backwards because the tangent from pt[0] to control pt[1] + c' = (0.00000000000000000, -1.1045837402343750) + q' = (0.0097961425781250000, -2.8988037109375000)
+ suggests that the quad is counterclockwise of the cubic, when the reverse is true
+ the angle code is fooled because the control pt[1] of both the quad and cubic
+ is far away from cubic cntl [2] and quad pt [2].
+ Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent
+ Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl?
+ */ +static void skpnaoxrane_ru23(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(458.703552f, 275.050262f); + path.quadTo(487.41687f, 273.000702f, 528, 273); + path.lineTo(529, 273); + path.quadTo(530.242371f, 273.000305f, 531.121338f, 273.878693f); + path.quadTo(531.999695f, 274.75766f, 532, 276); + path.lineTo(532, 378); + path.quadTo(531.990173f, 380.898804f, 503.296448f, 382.949738f); + path.quadTo(474.58313f, 384.999298f, 434, 385); + path.lineTo(433, 385); + path.quadTo(431.75766f, 384.999695f, 430.878693f, 384.121307f); + path.quadTo(430.000305f, 383.24234f, 430, 382); + path.lineTo(430, 280); + path.quadTo(430.009796f, 277.101196f, 458.703552f, 275.050262f); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(528, 278); + pathB.lineTo(529, 278); + pathB.cubicTo(530.65686f, 278, 532, 278, 532, 278); + pathB.lineTo(532, 378); + pathB.cubicTo(532, 379.104584f, 488.123932f, 380, 434, 380); + pathB.lineTo(433, 380); + pathB.cubicTo(431.34314f, 380, 430, 380, 430, 380); + pathB.lineTo(430, 280); + pathB.cubicTo(430, 278.895416f, 473.876068f, 278, 528, 278); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +/* didn't investigate thoroughly, but looks to be missorting quad and cubic + {{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}} + {{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}} + decision maker is case 14 leftLessThanRight + */ +static void skptcmevents_org23(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(465.503998f, 546); + path.lineTo(347, 546); + path.lineTo(347, 632); + path.lineTo(469.104248f, 632); + path.quadTo(470.79007f, 627.638672f, 471.833496f, 621.036255f); + path.quadTo(474.902588f, 601.562866f, 470.591064f, 574.024353f); + path.lineTo(468.507751f, 560.724426f); + path.quadTo(467.275146f, 552.856262f, 465.84668f, 547.288391f); + path.quadTo(465.670349f, 546.601501f, 465.503998f, 546); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(363.052246f, 542.495361f); + pathB.lineTo(463.779907f, 542.671143f); + pathB.cubicTo(464.829529f, 542.672974f, 466.946289f, 550.755676f, 468.507751f, 560.724426f); + pathB.lineTo(470.591064f, 574.024353f); + pathB.cubicTo(476.26178f, 610.226624f, 471.498932f, 639.557922f, 459.953003f, 639.537781f); + pathB.lineTo(368.727936f, 639.378601f); + pathB.cubicTo(351.933868f, 639.349304f, 337.053741f, 631.244324f, 335.492249f, 621.275574f); + pathB.lineTo(325.968597f, 560.475708f); + pathB.cubicTo(324.407104f, 550.506958f, 341.01001f, 542.456909f, 363.052246f, 542.495361f); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpredbullskatearcade_es16(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(936.765625f, 458.965302f); + path.cubicTo(937.028442f, 453.863251f, 933.145813f, 449.864502f, 928.093445f, 450.033905f); + path.lineTo(661.882263f, 458.958862f); + path.lineTo(661.875366f, 458.959106f); + path.cubicTo(656.828369f, 459.13205f, 652.525085f, 463.399719f, 652.258545f, 468.496124f); + path.lineTo(652.258179f, 468.503662f); + path.lineTo(649.021729f, 531.322754f); + path.cubicTo(648.75885f, 536.424805f, 652.641479f, 540.423523f, 657.693848f, 540.25415f); + path.lineTo(923.905029f, 531.329163f); + path.cubicTo(928.955017f, 531.159851f, 933.262268f, 526.890442f, 933.528809f, 521.791565f); + path.lineTo(933.529175f, 521.784363f); + path.lineTo(936.765625f, 458.965302f); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(661.882263f, 458.958862f); + pathB.lineTo(928.093445f, 450.033905f); + pathB.cubicTo(929.103882f, 450, 929.709961f, 454.108612f, 929.447144f, 459.210663f); + pathB.lineTo(926.210693f, 522.029724f); + pathB.cubicTo(926.079224f, 524.58075f, 925.153442f, 526.676208f, 924.143066f, 526.710083f); + pathB.lineTo(657.931885f, 535.635071f); + pathB.cubicTo(652.879456f, 535.804443f, 648.890259f, 533.873779f, 649.021729f, 531.322754f); + pathB.lineTo(652.258179f, 468.503662f); + pathB.cubicTo(652.520996f, 463.401611f, 656.829834f, 459.128235f, 661.882263f, 458.958862f); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpfinanzasdigital_com9(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(156, 126); + path.quadTo(154.343552f, 126.000397f, 153.17157f, 127.17157f); + path.quadTo(152.000397f, 128.343552f, 152, 130); + path.lineTo(152, 174); + path.lineTo(1114, 174); + path.lineTo(1114, 130); + path.quadTo(1113.99963f, 128.343552f, 1112.82837f, 127.17157f); + path.quadTo(1111.65649f, 126.000397f, 1110, 126); + path.lineTo(156, 126); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(156, 126); + pathB.lineTo(1110, 126); + pathB.cubicTo(1111.65686f, 126, 1113, 127.790863f, 1113, 130); + pathB.lineTo(1113, 174); + pathB.lineTo(153, 174); + pathB.lineTo(153, 130); + pathB.cubicTo(153, 127.790863f, 154.34314f, 126, 156, 126); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} +#endif + +static void skppartainasdemo250_org56(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(182.000015f, 645); + path.lineTo(182, 640); + path.cubicTo(174.322327f, 640, 166.644669f, 637.071045f, 160.786804f, 631.213196f); + path.cubicTo(149.071075f, 619.497437f, 149.071075f, 600.502563f, 160.786804f, 588.786804f); + path.lineTo(157.251266f, 585.251221f); + path.quadTo(147, 595.502502f, 147.000015f, 610); + path.quadTo(147, 624.482605f, 157.230255f, 634.727722f); + path.quadTo(157.251251f, 634.748779f, 157.251282f, 634.748779f); + path.quadTo(157.282852f, 634.780334f, 157.272263f, 634.769775f); + path.quadTo(167.517334f, 645, 182.000015f, 645); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(182, 659.497498f); + pathB.lineTo(206.748749f, 634.748718f); + pathB.lineTo(182.000015f, 610); + pathB.lineTo(132.502533f, 610); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpmlk_com326(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(154, 670); + path.cubicTo(151.238571f, 670, 149, 672.238586f, 149, 675); + path.lineTo(149, 710.001465f); + path.cubicTo(149.000809f, 712.209961f, 150.791367f, 714, 153, 714); + path.lineTo(189, 714); + path.cubicTo(191.209137f, 714, 193, 712.209167f, 193, 710); + path.lineTo(193, 675); + path.cubicTo(193, 672.238586f, 190.761429f, 670, 188, 670); + path.lineTo(154, 670); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(154, 671); + pathB.lineTo(188, 671); + pathB.cubicTo(190.761429f, 671, 193, 672.790833f, 193, 675); + pathB.lineTo(193, 710); + pathB.cubicTo(193, 712.761414f, 190.761429f, 715, 188, 715); + pathB.lineTo(154, 715); + pathB.cubicTo(151.238571f, 715, 149, 712.761414f, 149, 710); + pathB.lineTo(149, 675); + pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpcyclist_friends_gr52(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(50, 182); + path.lineTo(1215, 182); + path.lineTo(1215, 202); + path.quadTo(1214.99951f, 204.070572f, 1213.53552f, 205.535538f); + path.quadTo(1212.07056f, 206.999496f, 1210, 207); + path.lineTo(55, 207); + path.quadTo(52.9294319f, 206.999496f, 51.4644661f, 205.535538f); + path.quadTo(50.0004997f, 204.070572f, 50, 202); + path.lineTo(50, 182); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(50, 183); + pathB.lineTo(1215, 183); + pathB.lineTo(1215, 202); + pathB.cubicTo(1215, 204.761429f, 1212.76147f, 207, 1210, 207); + pathB.lineTo(55, 207); + pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202); + pathB.lineTo(50, 183); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +/* cubic ends just above opp line */ +static void skpwww_fj_p_com_22(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(172, 201); + path.lineTo(172, 202); + path.lineTo(220, 202); + path.cubicTo(221.65686f, 202, 223, 200.65686f, 223, 199); + path.cubicTo(223, 200.104568f, 221.65686f, 201, 220, 201); + path.lineTo(172, 201); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(161, 202); + pathB.lineTo(161, 199); + pathB.lineTo(223, 199.000015f); + pathB.lineTo(223, 202); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +#define TRY_SEPT_BROKEN_TESTS 0 +#if TRY_SEPT_BROKEN_TESTS +// pair of lines are not quite coincident, so sorting line/cubic fails (i think) +static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(806, 57); + path.cubicTo(806, 55.3431473f, 807.34314f, 54, 809, 54); + path.lineTo(930, 54); + path.cubicTo(931.65686f, 54, 933, 55.3431473f, 933, 57); + path.lineTo(933, 91); + path.cubicTo(933, 92.6568527f, 931.65686f, 94, 930, 94); + path.lineTo(809, 94); + path.cubicTo(807.34314f, 94, 806, 92.6568527f, 806, 91); + path.lineTo(806, 57); + path.close(); + path.moveTo(808, 58); + path.cubicTo(808, 56.8954315f, 808.895447f, 56, 810, 56); + path.lineTo(929, 56); + path.cubicTo(930.104553f, 56, 931, 56.8954315f, 931, 58); + path.lineTo(931, 90); + path.cubicTo(931, 91.1045685f, 930.104553f, 92, 929, 92); + path.lineTo(810, 92); + path.cubicTo(808.895447f, 92, 808, 91.1045685f, 808, 90); + path.lineTo(808, 58); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(806, 54); + pathB.lineTo(808, 56); + pathB.lineTo(935.02002f, 56.0200005f); + pathB.lineTo(933, 54); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +// pair of curves have nearly the same initial tangent but are sorting by +// that alone sorts them incorrectly. Need to detect that tangents are nearly +// identical and not reliable by themselves +static void skppptv_com_62(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(173, 5342); + path.quadTo(171.343536f, 5342.00049f, 170.17157f, 5343.17139f); + path.quadTo(169.000397f, 5344.34375f, 169, 5346); + path.lineTo(169, 5372); + path.lineTo(234, 5372); + path.lineTo(234, 5346); + path.quadTo(233.999603f, 5344.34375f, 232.82843f, 5343.17139f); + path.quadTo(231.656464f, 5342.00049f, 230, 5342); + path.lineTo(173, 5342); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(173, 5342); + pathB.lineTo(230, 5342); + pathB.cubicTo(231.65686f, 5342, 233, 5343.79102f, 233, 5346); + pathB.lineTo(233, 5372); + pathB.lineTo(169, 5372); + pathB.lineTo(169, 5346); + pathB.cubicTo(169, 5343.79102f, 170.790863f, 5342, 173, 5342); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +// nearly identical to lavoixdunord -- to not-quite-coincident lines +static void skpwww_booking_com_68(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(90, 187); + path.cubicTo(90, 185.34314f, 91.3431473f, 184, 93, 184); + path.lineTo(588, 184); + path.cubicTo(589.65686f, 184, 591, 185.34314f, 591, 187); + path.lineTo(591, 218); + path.cubicTo(591, 219.65686f, 589.65686f, 221, 588, 221); + path.lineTo(93, 221); + path.cubicTo(91.3431473f, 221, 90, 219.65686f, 90, 218); + path.lineTo(90, 187); + path.close(); + path.moveTo(92, 188); + path.cubicTo(92, 186.895432f, 92.8954315f, 186, 94, 186); + path.lineTo(587, 186); + path.cubicTo(588.104553f, 186, 589, 186.895432f, 589, 188); + path.lineTo(589, 217); + path.cubicTo(589, 218.104568f, 588.104553f, 219, 587, 219); + path.lineTo(94, 219); + path.cubicTo(92.8954315f, 219, 92, 218.104568f, 92, 217); + path.lineTo(92, 188); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(90, 184); + pathB.lineTo(92, 186); + pathB.lineTo(593.02002f, 186.020004f); + pathB.lineTo(591, 184); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +// visually looks like lavoixdunord and www_booking_com +static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(635, 1788); + path.cubicTo(635, 1786.34314f, 636.34314f, 1785, 638, 1785); + path.lineTo(832, 1785); + path.cubicTo(833.65686f, 1785, 835, 1786.34314f, 835, 1788); + path.lineTo(835, 1812); + path.cubicTo(835, 1813.65686f, 833.65686f, 1815, 832, 1815); + path.lineTo(638, 1815); + path.cubicTo(636.34314f, 1815, 635, 1813.65686f, 635, 1812); + path.lineTo(635, 1788); + path.close(); + path.moveTo(637, 1789); + path.cubicTo(637, 1787.89539f, 637.895447f, 1787, 639, 1787); + path.lineTo(831, 1787); + path.cubicTo(832.104553f, 1787, 833, 1787.89539f, 833, 1789); + path.lineTo(833, 1811); + path.cubicTo(833, 1812.10461f, 832.104553f, 1813, 831, 1813); + path.lineTo(639, 1813); + path.cubicTo(637.895447f, 1813, 637, 1812.10461f, 637, 1811); + path.lineTo(637, 1789); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(835, 1785); + pathB.lineTo(833, 1787); + pathB.lineTo(832.97998f, 1817.02002f); + pathB.lineTo(835, 1815); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} +#endif + +static void skpwww_joomla_org_23(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(320, 347); + path.cubicTo(320, 344.238586f, 322.238586f, 342, 325, 342); + path.lineTo(416, 342); + path.cubicTo(418.761414f, 342, 421, 344.238586f, 421, 347); + path.cubicTo(421, 344.790863f, 418.761414f, 343, 416, 343); + path.lineTo(325, 343); + path.cubicTo(322.238586f, 343, 320, 344.790863f, 320, 347); + path.close(); + path.moveTo(320, 378); + path.cubicTo(320, 380.761414f, 322.238586f, 383, 325, 383); + path.lineTo(416, 383); + path.cubicTo(418.761414f, 383, 421, 380.761414f, 421, 378); + path.cubicTo(421, 380.209137f, 418.761414f, 382, 416, 382); + path.lineTo(325, 382); + path.cubicTo(322.238586f, 382, 320, 380.209137f, 320, 378); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(320, 383); + pathB.lineTo(320, 378); + pathB.lineTo(421, 378.000031f); + pathB.lineTo(421, 383); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpwww_macrumors_com_131(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(136, 14089); + path.lineTo(136, 14056); + path.lineTo(778, 14056); + path.lineTo(778, 14089); + path.quadTo(777.999573f, 14090.6562f, 776.82843f, 14091.8281f); + path.quadTo(775.656433f, 14093, 774, 14093); + path.lineTo(140, 14093); + path.quadTo(138.343552f, 14093, 137.17157f, 14091.8281f); + path.quadTo(136.000397f, 14090.6562f, 136, 14089); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kInverseWinding_FillType); + pathB.moveTo(136, 14057); + pathB.lineTo(778, 14057); + pathB.lineTo(778, 14089); + pathB.cubicTo(778, 14091.209f, 776.209167f, 14093, 774, 14093); + pathB.lineTo(140, 14093); + pathB.cubicTo(137.790863f, 14093, 136, 14091.209f, 136, 14089); + pathB.lineTo(136, 14057); + pathB.close(); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpwww_leadpages_net_84(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(377.1716f, 5910.17139f); + path.cubicTo(376.447723f, 5910.89551f, 376, 5911.89551f, 376, 5913); + path.lineTo(376, 5972); + path.cubicTo(376, 5974.20898f, 377.790863f, 5976, 380, 5976); + path.cubicTo(378.34314f, 5976, 377, 5974.20898f, 377, 5972); + path.lineTo(377, 5913); + path.cubicTo(377, 5912.17139f, 377.335785f, 5911.42188f, 377.878693f, 5910.87891f); + path.lineTo(377.1716f, 5910.17139f); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(376, 5909); + pathB.lineTo(378.481873f, 5909); + pathB.lineTo(379.999878f, 5976); + pathB.lineTo(376, 5976); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +static void skpwww_briian_com_34(skiatest::Reporter* reporter) { + SkPath path; + path.setFillType(SkPath::kEvenOdd_FillType); + path.moveTo(843, 216); + path.cubicTo(843, 213.238571f, 845.238586f, 211, 848, 211); + path.lineTo(1191, 211); + path.cubicTo(1193.76147f, 211, 1196, 213.238571f, 1196, 216); + path.lineTo(1196, 779); + path.cubicTo(1196, 781.761414f, 1193.76147f, 784, 1191, 784); + path.lineTo(848, 784); + path.cubicTo(845.238586f, 784, 843, 781.761414f, 843, 779); + path.lineTo(843, 216); + path.close(); + path.moveTo(844, 217); + path.cubicTo(844, 214.238571f, 846.238586f, 212, 849, 212); + path.lineTo(1190, 212); + path.cubicTo(1192.76147f, 212, 1195, 214.238571f, 1195, 217); + path.lineTo(1195, 778); + path.cubicTo(1195, 779.65686f, 1192.76147f, 781, 1190, 781); + path.lineTo(849, 781); + path.cubicTo(846.238586f, 781, 844, 779.65686f, 844, 778); + path.lineTo(844, 217); + path.close(); + SkPath pathB; + pathB.setFillType(SkPath::kWinding_FillType); + pathB.moveTo(843, 784); + pathB.lineTo(843, 779); + pathB.lineTo(1196, 779.000061f); + pathB.lineTo(1196, 784); + testPathOp(reporter, path, pathB, kIntersect_PathOp); +} + +
+static void skpwww_sciality_com_100(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(162, 468);
+ path.cubicTo(159.790863f, 468, 158, 469.790863f, 158, 472);
+ path.lineTo(158, 528);
+ path.cubicTo(158, 530.209106f, 159.790863f, 532, 162, 532);
+ path.lineTo(275, 532);
+ path.cubicTo(277.209137f, 532, 279, 530.209106f, 279, 528);
+ path.lineTo(279, 472);
+ path.cubicTo(279, 469.790863f, 277.209137f, 468, 275, 468);
+ path.lineTo(162, 468);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(275, 468);
+ pathB.cubicTo(276.65686f, 468, 278, 469.34314f, 278, 471);
+ pathB.lineTo(278, 529);
+ pathB.cubicTo(278, 530.65686f, 276.65686f, 532, 275, 532);
+ pathB.lineTo(161, 532);
+ pathB.cubicTo(159.34314f, 532, 158, 530.65686f, 158, 529);
+ pathB.lineTo(158, 471);
+ pathB.cubicTo(158, 469.34314f, 159.34314f, 468, 161, 468);
+ pathB.lineTo(275, 468);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+#if TRY_SEPT_BROKEN_TESTS +static void skpwww_sciality_com_101(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(162, 468);
+ path.cubicTo(159.790863f, 468, 158, 469.790863f, 158, 472);
+ path.lineTo(158, 528);
+ path.cubicTo(158, 530.209106f, 159.790863f, 532, 162, 532);
+ path.lineTo(275.009186f, 532);
+ path.cubicTo(276.661774f, 531.994995f, 278, 530.653748f, 278, 529);
+ path.lineTo(278, 471);
+ path.cubicTo(278, 469.346375f, 276.662079f, 468.005249f, 275.009705f, 468);
+ path.lineTo(162, 468);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kInverseWinding_FillType);
+ pathB.moveTo(161, 469);
+ pathB.lineTo(275, 469);
+ pathB.cubicTo(276.65686f, 469, 278, 469.895416f, 278, 471);
+ pathB.lineTo(278, 529);
+ pathB.cubicTo(278, 530.65686f, 276.65686f, 532, 275, 532);
+ pathB.lineTo(161, 532);
+ pathB.cubicTo(159.34314f, 532, 158, 530.65686f, 158, 529);
+ pathB.lineTo(158, 471);
+ pathB.cubicTo(158, 469.895416f, 159.34314f, 469, 161, 469);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
#endif +static void skpwww_meb_gov_tr_5(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(137.34314f, 145.34314f);
+ path.quadTo(139.687088f, 143.000793f, 143, 143);
+ path.lineTo(242, 143);
+ path.quadTo(245.312912f, 143.000793f, 247.65686f, 145.34314f);
+ path.quadTo(249.999207f, 147.687088f, 250, 151);
+ path.lineTo(250, 177);
+ path.lineTo(135, 177);
+ path.lineTo(135, 151);
+ path.quadTo(135.000793f, 147.687088f, 137.34314f, 145.34314f);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(135, 143);
+ pathB.lineTo(250, 143);
+ pathB.lineTo(250, 177);
+ pathB.lineTo(135, 177);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+#if TRY_SEPT_BROKEN_TESTS +static void skpwww_meb_gov_tr_6(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(143, 143);
+ path.quadTo(139.687088f, 143.000793f, 137.34314f, 145.34314f);
+ path.quadTo(135.000793f, 147.687088f, 135, 151);
+ path.lineTo(135, 177);
+ path.lineTo(250, 177);
+ path.lineTo(250, 151);
+ path.quadTo(249.999207f, 147.687088f, 247.65686f, 145.34314f);
+ path.quadTo(245.312912f, 143.000793f, 242, 143);
+ path.lineTo(143, 143);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kInverseWinding_FillType);
+ pathB.moveTo(143, 143);
+ pathB.lineTo(242, 143);
+ pathB.cubicTo(245.865997f, 143, 249, 146.581726f, 249, 151);
+ pathB.lineTo(249, 177);
+ pathB.lineTo(135, 177);
+ pathB.lineTo(135, 151);
+ pathB.cubicTo(135, 146.581726f, 138.581726f, 143, 143, 143);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+#endif + +static void skpgithub_io_25(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(1001.87866f, 14.8786793f);
+ path.quadTo(1002.75745f, 14.0001001f, 1004, 14);
+ path.lineTo(1105, 14);
+ path.quadTo(1106.24255f, 14.0001001f, 1107.12134f, 14.8786793f);
+ path.quadTo(1107.99988f, 15.7574596f, 1108, 17);
+ path.lineTo(1108, 41);
+ path.quadTo(1107.99988f, 42.2425423f, 1107.12134f, 43.1213188f);
+ path.quadTo(1106.24255f, 43.9999008f, 1105, 44);
+ path.lineTo(1004, 44);
+ path.quadTo(1002.75745f, 43.9999008f, 1001.87866f, 43.1213188f);
+ path.quadTo(1001.00012f, 42.2425423f, 1001, 41);
+ path.lineTo(1001, 17);
+ path.quadTo(1001.00012f, 15.7574596f, 1001.87866f, 14.8786793f);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kInverseWinding_FillType);
+ pathB.moveTo(1005, 16);
+ pathB.lineTo(1104, 16);
+ pathB.cubicTo(1105.10461f, 16, 1106, 16.8954296f, 1106, 18);
+ pathB.lineTo(1106, 40);
+ pathB.cubicTo(1106, 41.1045685f, 1105.10461f, 42, 1104, 42);
+ pathB.lineTo(1005, 42);
+ pathB.cubicTo(1003.89545f, 42, 1003, 41.1045685f, 1003, 40);
+ pathB.lineTo(1003, 18);
+ pathB.cubicTo(1003, 16.8954296f, 1003.89545f, 16, 1005, 16);
+ pathB.close();
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpgithub_io_26(skiatest::Reporter* reporter) {
+ SkPath path;
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ path.moveTo(1001.87866f, 14.8786793f);
+ path.quadTo(1002.75745f, 14.0001001f, 1004, 14);
+ path.lineTo(1105, 14);
+ path.quadTo(1106.24255f, 14.0001001f, 1107.12134f, 14.8786793f);
+ path.quadTo(1107.99988f, 15.7574596f, 1108, 17);
+ path.lineTo(1108, 41);
+ path.quadTo(1107.99988f, 42.2425423f, 1107.12134f, 43.1213188f);
+ path.quadTo(1106.24255f, 43.9999008f, 1105, 44);
+ path.lineTo(1004, 44);
+ path.quadTo(1002.75745f, 43.9999008f, 1001.87866f, 43.1213188f);
+ path.quadTo(1001.00012f, 42.2425423f, 1001, 41);
+ path.lineTo(1001, 17);
+ path.quadTo(1001.00012f, 15.7574596f, 1001.87866f, 14.8786793f);
+ path.close();
+ path.moveTo(1003, 18);
+ path.cubicTo(1003, 16.8954296f, 1003.89545f, 16, 1005, 16);
+ path.lineTo(1104, 16);
+ path.cubicTo(1105.10461f, 16, 1106, 16.8954296f, 1106, 18);
+ path.lineTo(1106, 40);
+ path.cubicTo(1106, 41.1045685f, 1105.10461f, 42, 1104, 42);
+ path.lineTo(1005, 42);
+ path.cubicTo(1003.89545f, 42, 1003, 41.1045685f, 1003, 40);
+ path.lineTo(1003, 18);
+ path.close();
+ SkPath pathB;
+ pathB.setFillType(SkPath::kWinding_FillType);
+ pathB.moveTo(1108, 14);
+ pathB.lineTo(1106, 16);
+ pathB.lineTo(1105.97998f, 46.0200005f);
+ pathB.lineTo(1108, 44);
+ testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+ static void (*firstTest)(skiatest::Reporter* ) = 0; static struct TestDesc tests[] = { +#if TRY_SEPT_BROKEN_TESTS + TEST(skpwww_meb_gov_tr_6), + TEST(skpwww_sciality_com_101), + TEST(skpwww_booking_com_68), // similar to lavoixdunord + TEST(skpwww_despegar_com_mx_272), // similar to lavoixdunord + TEST(skpwww_lavoixdunord_fr_11), // not quite coincident, sorting line/cubic fails + TEST(skppptv_com_62), // cubic have nearly identical tangents, sort incorrectly +#endif #if TRY_BROKEN_TESTS TEST(skppchappy_com_au102), TEST(skpsciality_com161), - TEST(skpsudoestenegocios_com186), - TEST(skpfrauen_magazin_com83), TEST(skpi_gino_com16), + TEST(skpnaoxrane_ru23), // see test for failure evaluation + TEST(skptcmevents_org23), // see test for (partial) failure evaluation + TEST(skpredbullskatearcade_es16), // cubic have nearly identical tangents, sort incorrectly + TEST(skpfinanzasdigital_com9), // cubic/quad tangents too close to sort #endif + TEST(skpgithub_io_26), + TEST(skpgithub_io_25), + TEST(skpwww_meb_gov_tr_5), + TEST(skpwww_sciality_com_100), + TEST(skpwww_joomla_org_23), + TEST(skpwww_macrumors_com_131), + TEST(skpwww_briian_com_34), + TEST(skpwww_leadpages_net_84), + TEST(skpwww_fj_p_com_22), + TEST(skppartainasdemo250_org56), + TEST(skpsd_graphic_net104), + TEST(skpbreakmystyle_com10), + TEST(skpnational_com_au81), + TEST(skprentacheat_com30), + TEST(skptracksflow_com9), + TEST(skpautobutler_dk29), + TEST(skponlinecollege_org144), + TEST(skphostloco_com11), + TEST(skpsergeychunkevich_com8), + TEST(skpmlk_com326), + TEST(skpcyclist_friends_gr52), + TEST(skpfrauen_magazin_com83), + TEST(skpthesuburbanite_com213), + TEST(skpsudoestenegocios_com186), TEST(skpmtrk_uz27), TEST(skpilkoora_com37), TEST(skpmm4everfriends_com43), diff --git a/tests/PathOpsThreadedCommon.h b/tests/PathOpsThreadedCommon.h index ee9339065f..a638cd2fdf 100644 --- a/tests/PathOpsThreadedCommon.h +++ b/tests/PathOpsThreadedCommon.h @@ -68,6 +68,15 @@ public: fTestFun = testFun; } + PathOpsThreadedRunnable(void (*testFun)(PathOpsThreadState*), int dirNo, const char* str, + PathOpsThreadedTestRunner* runner) { + SkASSERT(strlen(str) < sizeof(fState.fSerialNo) - 1); + fState.fA = dirNo; + strcpy(fState.fSerialNo, str); + fState.fReporter = runner->fReporter; + fTestFun = testFun; + } + virtual void run() SK_OVERRIDE { SkBitmap bitmap; fState.fBitmap = &bitmap; diff --git a/tests/PathOpsTypesTest.cpp b/tests/PathOpsTypesTest.cpp new file mode 100755 index 0000000000..6fd6e10e7d --- /dev/null +++ b/tests/PathOpsTypesTest.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "PathOpsTestCommon.h" +#include "Test.h" +
+static const double roughlyTests[][2] = {
+ {5.0402503619650929e-005, 4.3178054475078825e-005}
+};
+
+static const size_t roughlyTestsCount = SK_ARRAY_COUNT(roughlyTests); +
+static void PathOpsRoughlyTest(skiatest::Reporter* reporter) { + for (size_t index = 0; index < roughlyTestsCount; ++index) { + bool equal = RoughlyEqualUlps(roughlyTests[index][0], roughlyTests[index][1]); + REPORTER_ASSERT(reporter, equal); + } +}
+
+#include "TestClassDef.h" +DEFINE_TESTCLASS_SHORT(PathOpsRoughlyTest) diff --git a/tests/SkpSkGrTest.cpp b/tests/SkpSkGrTest.cpp new file mode 100755 index 0000000000..d26659f5c3 --- /dev/null +++ b/tests/SkpSkGrTest.cpp @@ -0,0 +1,759 @@ +#if !SK_SUPPORT_GPU +#error "GPU support required" +#endif + +#include "GrContext.h" +#include "GrContextFactory.h" +#include "GrRenderTarget.h" +#include "SkGpuDevice.h" +#include "gl/GrGLDefines.h" + +#include "SkBitmap.h" +#include "SkColor.h" +#include "SkDevice.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkImageEncoder.h" +#include "SkStream.h" +#include "SkOSFile.h" +#include "SkPicture.h" +#include "SkRTConf.h" +#include "SkRunnable.h" +#include "SkString.h" +#include "SkTArray.h" +#include "SkTDArray.h" +#include "SkThreadPool.h" +#include "SkTime.h" +#include "Test.h" + +#ifdef SK_BUILD_FOR_WIN + #define PATH_SLASH "\\" + #define IN_DIR "D:\\9-30-13\\" + #define OUT_DIR "D:\\skpSkGr\\11\\" + #define LINE_FEED "\r\n" +#else + #define PATH_SLASH "/" + #define IN_DIR "/usr/local/google/home/caryclark" PATH_SLASH "9-30-13-skp" + #define OUT_DIR "/media/01CD75512A7F9EE0/4" PATH_SLASH + #define LINE_FEED \n" +#endif + +#define PATH_STR_SIZE 512 + +static const struct { + int directory; + const char* filename; +} skipOverSkGr[] = { + {1, "http___accuweather_com_.skp"}, // Couldn't convert bitmap to texture.http___absoku072_com_ +}; + +static const size_t skipOverSkGrCount = 0; // SK_ARRAY_COUNT(skipOverSkGr); + +///////////////////////////////////////// + +class SkpSkGrThreadedRunnable; + +enum TestStep { + kCompareBits, + kEncodeFiles, +}; + +enum { + kMaxLength = 128, + kMaxFiles = 128, +}; + +struct TestResult { + void init(int dirNo) { + fDirNo = dirNo; + sk_bzero(fFilename, sizeof(fFilename)); + fTestStep = kCompareBits; + fScaleOversized = true; + } + + SkString status() { + SkString outStr; + outStr.printf("%s %d %d%s", fFilename, fPixelError, fTime, LINE_FEED); + return outStr; + } + + static void Test(int dirNo, const char* filename, TestStep testStep, bool verbose) { + TestResult test; + test.init(dirNo); + test.fTestStep = testStep; + strcpy(test.fFilename, filename); + test.testOne(); + if (verbose) { + SkDebugf("%s", test.status().c_str()); + } + } + + void test(int dirNo, const SkString& filename) { + init(dirNo); + strcpy(fFilename, filename.c_str()); + testOne(); + } + + void testOne(); + + char fFilename[kMaxLength]; + TestStep fTestStep; + int fDirNo; + int fPixelError; + int fTime; + bool fScaleOversized; +}; + +struct SkpSkGrThreadState { + void init(int dirNo) { + fResult.init(dirNo); + fFoundCount = 0; + fSmallestError = 0; + sk_bzero(fFilesFound, sizeof(fFilesFound)); + sk_bzero(fDirsFound, sizeof(fDirsFound)); + sk_bzero(fError, sizeof(fError)); + } + + char fFilesFound[kMaxFiles][kMaxLength]; + int fDirsFound[kMaxFiles]; + int fError[kMaxFiles]; + int fFoundCount; + int fSmallestError; + skiatest::Reporter* fReporter; + TestResult fResult; +}; + +struct SkpSkGrThreadedTestRunner { + SkpSkGrThreadedTestRunner(skiatest::Reporter* reporter, int threadCount) + : fNumThreads(threadCount) + , fReporter(reporter) { + } + + ~SkpSkGrThreadedTestRunner(); + void render(); + int fNumThreads; + SkTDArray<SkpSkGrThreadedRunnable*> fRunnables; + skiatest::Reporter* fReporter; +}; + +class SkpSkGrThreadedRunnable : public SkRunnable { +public: + SkpSkGrThreadedRunnable(void (*testFun)(SkpSkGrThreadState*), int dirNo, const char* str, + SkpSkGrThreadedTestRunner* runner) { + SkASSERT(strlen(str) < sizeof(fState.fResult.fFilename) - 1); + fState.init(dirNo); + strcpy(fState.fResult.fFilename, str); + fState.fReporter = runner->fReporter; + fTestFun = testFun; + } + + virtual void run() SK_OVERRIDE { + SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); + (*fTestFun)(&fState); + } + + SkpSkGrThreadState fState; + void (*fTestFun)(SkpSkGrThreadState*); +}; + +SkpSkGrThreadedTestRunner::~SkpSkGrThreadedTestRunner() { + for (int index = 0; index < fRunnables.count(); index++) { + SkDELETE(fRunnables[index]); + } +} + +void SkpSkGrThreadedTestRunner::render() { + SkThreadPool pool(fNumThreads); + for (int index = 0; index < fRunnables.count(); ++ index) { + pool.add(fRunnables[index]); + } +} + +//////////////////////////////////////////////// + +static const char outGrDir[] = OUT_DIR "grTest"; +static const char outSkDir[] = OUT_DIR "skTest"; +static const char outSkpDir[] = OUT_DIR "skpTest"; +static const char outDiffDir[] = OUT_DIR "outTest"; +static const char outStatusDir[] = OUT_DIR "statusTest"; + +static SkString make_filepath(int dirIndex, const char* dir, const char* name) { + SkString path(dir); + if (dirIndex) { + path.appendf("%d", dirIndex); + } + path.append(PATH_SLASH); + path.append(name); + return path; +} + +static SkString make_in_dir_name(int dirIndex) { + SkString dirName(IN_DIR); + dirName.appendf("%d", dirIndex); + if (!sk_exists(dirName.c_str())) { + SkDebugf("could not read dir %s\n", dirName.c_str()); + return SkString(); + } + return dirName; +} + +static bool make_out_dirs() { + SkString outDir = make_filepath(0, OUT_DIR, ""); + if (!sk_exists(outDir.c_str())) { + if (!sk_mkdir(outDir.c_str())) { + SkDebugf("could not create dir %s\n", outDir.c_str()); + return false; + } + } + SkString grDir = make_filepath(0, outGrDir, ""); + if (!sk_exists(grDir.c_str())) { + if (!sk_mkdir(grDir.c_str())) { + SkDebugf("could not create dir %s\n", grDir.c_str()); + return false; + } + } + SkString skDir = make_filepath(0, outSkDir, ""); + if (!sk_exists(skDir.c_str())) { + if (!sk_mkdir(skDir.c_str())) { + SkDebugf("could not create dir %s\n", skDir.c_str()); + return false; + } + } + SkString skpDir = make_filepath(0, outSkpDir, ""); + if (!sk_exists(skpDir.c_str())) { + if (!sk_mkdir(skpDir.c_str())) { + SkDebugf("could not create dir %s\n", skpDir.c_str()); + return false; + } + } + SkString diffDir = make_filepath(0, outDiffDir, ""); + if (!sk_exists(diffDir.c_str())) { + if (!sk_mkdir(diffDir.c_str())) { + SkDebugf("could not create dir %s\n", diffDir.c_str()); + return false; + } + } + SkString statusDir = make_filepath(0, outStatusDir, ""); + if (!sk_exists(statusDir.c_str())) { + if (!sk_mkdir(statusDir.c_str())) { + SkDebugf("could not create dir %s\n", statusDir.c_str()); + return false; + } + } + return true; +} + +static SkString make_png_name(const char* filename) { + SkString pngName = SkString(filename); + pngName.remove(pngName.size() - 3, 3); + pngName.append("png"); + return pngName; +} + +typedef GrContextFactory::GLContextType GLContextType; +#ifdef SK_BUILD_FOR_WIN +static const GLContextType kAngle = GrContextFactory::kANGLE_GLContextType; +#else +static const GLContextType kNative = GrContextFactory::kNative_GLContextType; +#endif + +static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { + const int kRowCount = 3; + const int kThreshold = 3; + int width = SkTMin(gr.width(), sk.width()); + if (width < kRowCount) { + return true; + } + int height = SkTMin(gr.height(), sk.height()); + if (height < kRowCount) { + return true; + } + int errorTotal = 0; + SkTArray<char, true> errorRows; + errorRows.push_back_n(width * kRowCount); + SkAutoLockPixels autoGr(gr); + SkAutoLockPixels autoSk(sk); + char* base = &errorRows[0]; + for (int y = 0; y < height; ++y) { + SkPMColor* grRow = gr.getAddr32(0, y); + SkPMColor* skRow = sk.getAddr32(0, y); + char* cOut = &errorRows[(y % kRowCount) * width]; + for (int x = 0; x < width; ++x) { + SkPMColor grColor = grRow[x]; + SkPMColor skColor = skRow[x]; + int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); + int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); + int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); + int error = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); + if ((cOut[x] = error >= kThreshold) && x >= 2 + && base[x - 2] && base[width + x - 2] && base[width * 2 + x - 2] + && base[x - 1] && base[width + x - 1] && base[width * 2 + x - 1] + && base[x - 0] && base[width + x - 0] && base[width * 2 + x - 0]) { + errorTotal += error; + } + } + } + return errorTotal; +} + +static bool addError(SkpSkGrThreadState* data) { + bool foundSmaller = false; + int dCount = data->fFoundCount; + int pixelError = data->fResult.fPixelError; + if (data->fFoundCount < kMaxFiles) { + data->fError[dCount] = pixelError; + strcpy(data->fFilesFound[dCount], data->fResult.fFilename); + data->fDirsFound[dCount] = data->fResult.fDirNo; + ++data->fFoundCount; + } else if (pixelError > data->fSmallestError) { + int smallest = SK_MaxS32; + int smallestIndex = 0; + for (int index = 0; index < kMaxFiles; ++index) { + if (smallest > data->fError[index]) { + smallest = data->fError[index]; + smallestIndex = index; + } + } + data->fError[smallestIndex] = pixelError; + strcpy(data->fFilesFound[smallestIndex], data->fResult.fFilename); + data->fDirsFound[smallestIndex] = data->fResult.fDirNo; + data->fSmallestError = SK_MaxS32; + for (int index = 0; index < kMaxFiles; ++index) { + if (data->fSmallestError > data->fError[index]) { + data->fSmallestError = data->fError[index]; + } + } + SkDebugf("*%d*", data->fSmallestError); + foundSmaller = true; + } + return foundSmaller; +} + +static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) { + canvas->save(); + int pWidth = pic->width(); + int pHeight = pic->height(); + const int maxDimension = 1000; + const int slices = 3; + int xInterval = SkTMax(pWidth - maxDimension, 0) / (slices - 1); + int yInterval = SkTMax(pHeight - maxDimension, 0) / (slices - 1); + SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)), + SkIntToScalar(SkTMin(maxDimension, pHeight))}; + canvas->clipRect(rect); + SkMSec start = SkTime::GetMSecs(); + for (int x = 0; x < slices; ++x) { + for (int y = 0; y < slices; ++y) { + pic->draw(canvas); + canvas->translate(0, SkIntToScalar(yInterval)); + } + canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices)); + } + SkMSec end = SkTime::GetMSecs(); + canvas->restore(); + return end - start; +} + +static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) { + canvas->clear(SK_ColorWHITE); + if (scale != 1) { + canvas->save(); + canvas->scale(1.0f / scale, 1.0f / scale); + } + pic->draw(canvas); + if (scale != 1) { + canvas->restore(); + } +} + +static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) { + SkString outFile = make_filepath(0, outDir, pngName); + if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, + SkImageEncoder::kPNG_Type, 100)) { + SkDebugf("unable to encode gr %s (width=%d height=%d)br \n", pngName, + bitmap.width(), bitmap.height()); + } +} + +void TestResult::testOne() { + SkPicture* pic = NULL; + { + SkString d; + d.printf(" {%d, \"%s\"},", fDirNo, fFilename); + SkString path = make_filepath(fDirNo, IN_DIR, fFilename); + SkFILEStream stream(path.c_str()); + if (!stream.isValid()) { + SkDebugf("invalid stream %s\n", path.c_str()); + goto finish; + } + if (fTestStep == kEncodeFiles) { + size_t length = stream.getLength(); + SkTArray<char, true> bytes; + bytes.push_back_n(length); + stream.read(&bytes[0], length); + stream.rewind(); + SkString wPath = make_filepath(0, outSkpDir, fFilename); + SkFILEWStream wStream(wPath.c_str()); + wStream.write(&bytes[0], length); + wStream.flush(); + } + pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory); + if (!pic) { + SkDebugf("unable to decode %s\n", fFilename); + goto finish; + } + int pWidth = pic->width(); + int pHeight = pic->height(); + int pLargerWH = SkTMax(pWidth, pHeight); + GrContextFactory contextFactory; +#ifdef SK_BUILD_FOR_WIN + GrContext* context = contextFactory.get(kAngle); +#else + GrContext* context = contextFactory.get(kNative); +#endif + if (NULL == context) { + SkDebugf("unable to allocate context for %s\n", fFilename); + goto finish; + } + int maxWH = context->getMaxRenderTargetSize(); + int scale = 1; + while (pLargerWH / scale > maxWH) { + scale *= 2; + } + SkBitmap bitmap; + SkIPoint dim; + do { + dim.fX = (pWidth + scale - 1) / scale; + dim.fY = (pHeight + scale - 1) / scale; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, dim.fX, dim.fY); + bool success = bitmap.allocPixels(); + if (success) { + break; + } + SkDebugf("-%d-", scale); + } while ((scale *= 2) < 256); + if (scale >= 256) { + SkDebugf("unable to allocate bitmap for %s (w=%d h=%d) (sw=%d sh=%d)\n", + fFilename, pWidth, pHeight, dim.fX, dim.fY); + goto finish; + } + SkCanvas skCanvas(bitmap); + drawPict(pic, &skCanvas, fScaleOversized ? scale : 1); + GrTextureDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kRenderTarget_GrTextureFlagBit; + desc.fWidth = dim.fX; + desc.fHeight = dim.fY; + desc.fSampleCnt = 0; + SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); + if (!texture) { + SkDebugf("unable to allocate texture for %s (w=%d h=%d)\n", fFilename, + dim.fX, dim.fY); + goto finish; + } + SkGpuDevice grDevice(context, texture.get()); + SkCanvas grCanvas(&grDevice); + drawPict(pic, &grCanvas, fScaleOversized ? scale : 1); + const SkBitmap& grBitmap = grDevice.accessBitmap(false); + if (fTestStep == kCompareBits) { + fPixelError = similarBits(grBitmap, bitmap); + int skTime = timePict(pic, &skCanvas); + int grTime = timePict(pic, &grCanvas); + fTime = skTime - grTime; + } else if (fTestStep == kEncodeFiles) { + SkString pngStr = make_png_name(fFilename); + const char* pngName = pngStr.c_str(); + writePict(grBitmap, outGrDir, pngName); + writePict(bitmap, outSkDir, pngName); + } + } +finish: + SkDELETE(pic); +} + +static SkString makeStatusString(int dirNo) { + SkString statName; + statName.printf("stats%d.txt", dirNo); + SkString statusFile = make_filepath(0, outStatusDir, statName.c_str()); + return statusFile; +} + +class PreParser { +public: + PreParser(int dirNo) + : fDirNo(dirNo) + , fIndex(0) + , fStatusPath(makeStatusString(dirNo)) { + if (!sk_exists(fStatusPath.c_str())) { + return; + } + SkFILEStream reader; + reader.setPath(fStatusPath.c_str()); + while (fetch(reader, &fResults.push_back())) + ; + fResults.pop_back(); + } + + bool fetch(SkFILEStream& reader, TestResult* result) { + char c; + int i = 0; + result->init(fDirNo); + result->fPixelError = 0; + result->fTime = 0; + do { + bool readOne = reader.read(&c, 1) != 0; + if (!readOne) { + SkASSERT(i == 0); + return false; + } + if (c == ' ') { + result->fFilename[i++] = '\0'; + break; + } + result->fFilename[i++] = c; + SkASSERT(i < kMaxLength); + } while (true); + do { + SkAssertResult(reader.read(&c, 1) != 0); + if (c == ' ') { + break; + } + SkASSERT(c >= '0' && c <= '9'); + result->fPixelError = result->fPixelError * 10 + (c - '0'); + } while (true); + bool minus = false; + do { + if (reader.read(&c, 1) == 0) { + break; + } + if (c == '\r' && reader.read(&c, 1) == 0) { + break; + } + if (c == '\n') { + break; + } + if (c == '-') { + minus = true; + continue; + } + SkASSERT(c >= '0' && c <= '9'); + result->fTime = result->fTime * 10 + (c - '0'); + } while (true); + if (minus) { + result->fTime = -result->fTime; + } + return true; + } + + bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) { + if (fIndex < fResults.count()) { + *result = fResults[fIndex++]; + SkASSERT(filename.equals(result->fFilename)); + SkString outStr(result->status()); + stream->write(outStr.c_str(), outStr.size()); + stream->flush(); + return true; + } + return false; + } + +private: + int fDirNo; + int fIndex; + SkTArray<TestResult, true> fResults; + SkString fStatusPath; +}; + +static bool initTest() { +#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC + SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); + SK_CONF_SET("images.png.suppressDecoderWarnings", true); +#endif + return make_out_dirs(); +} + +static void SkpSkGrTest(skiatest::Reporter* reporter) { + SkTArray<TestResult, true> errors; + if (!initTest()) { + return; + } + SkpSkGrThreadState state; + state.init(0); + int smallCount = 0; + for (int dirNo = 1; dirNo <= 100; ++dirNo) { + SkString pictDir = make_in_dir_name(dirNo); + SkASSERT(pictDir.size()); + if (reporter->verbose()) { + SkDebugf("dirNo=%d\n", dirNo); + } + SkOSFile::Iter iter(pictDir.c_str(), "skp"); + SkString filename; + int testCount = 0; + PreParser preParser(dirNo); + SkFILEWStream statusStream(makeStatusString(dirNo).c_str()); + while (iter.next(&filename)) { + for (size_t index = 0; index < skipOverSkGrCount; ++index) { + if (skipOverSkGr[index].directory == dirNo + && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) { + goto skipOver; + } + } + if (preParser.match(filename, &statusStream, &state.fResult)) { + addError(&state); + ++testCount; + goto checkEarlyExit; + } + if (state.fSmallestError > 5000000) { + goto breakOut; + } + { + TestResult& result = state.fResult; + result.test(dirNo, filename); + SkString outStr(result.status()); + statusStream.write(outStr.c_str(), outStr.size()); + statusStream.flush(); + if (1) { + SkDebugf("%s", outStr.c_str()); + } + bool noMatch = addError(&state); + if (noMatch) { + smallCount = 0; + } else if (++smallCount > 10000) { + goto breakOut; + } + } + ++testCount; + if (reporter->verbose()) { + if (testCount % 100 == 0) { + SkDebugf("#%d\n", testCount); + } + } + skipOver: + reporter->bumpTestCount(); + checkEarlyExit: + if (1 && testCount == 20) { + break; + } + } + } +breakOut: + if (reporter->verbose()) { + for (int index = 0; index < state.fFoundCount; ++index) { + SkDebugf("%d %s %d\n", state.fDirsFound[index], state.fFilesFound[index], + state.fError[index]); + } + } + for (int index = 0; index < state.fFoundCount; ++index) { + TestResult::Test(state.fDirsFound[index], state.fFilesFound[index], kEncodeFiles, + reporter->verbose()); + if (reporter->verbose()) SkDebugf("+"); + } +} + +static void bumpCount(skiatest::Reporter* reporter, bool skipping) { + if (reporter->verbose()) { + static int threadTestCount; + sk_atomic_inc(&threadTestCount); + if (!skipping && threadTestCount % 100 == 0) { + SkDebugf("#%d\n", threadTestCount); + } + if (skipping && threadTestCount % 10000 == 0) { + SkDebugf("#%d\n", threadTestCount); + } + } +} + +static void testSkGrMain(SkpSkGrThreadState* data) { + data->fResult.testOne(); + bumpCount(data->fReporter, false); + data->fReporter->bumpTestCount(); +} + +static void SkpSkGrThreadedTest(skiatest::Reporter* reporter) { + if (!initTest()) { + return; + } + int threadCount = reporter->allowThreaded() ? 3 : 1; + SkpSkGrThreadedTestRunner testRunner(reporter, threadCount); + for (int dirIndex = 1; dirIndex <= 100; ++dirIndex) { + SkString pictDir = make_in_dir_name(dirIndex); + if (pictDir.size() == 0) { + continue; + } + SkOSFile::Iter iter(pictDir.c_str(), "skp"); + SkString filename; + while (iter.next(&filename)) { + SkString pngName = make_png_name(filename.c_str()); + SkString oldPng = make_filepath(dirIndex, outSkDir, pngName.c_str()); + SkString newPng = make_filepath(dirIndex, outGrDir, pngName.c_str()); + if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) { + bumpCount(reporter, true); + continue; + } + for (size_t index = 0; index < skipOverSkGrCount; ++index) { + if (skipOverSkGr[index].directory == dirIndex + && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) { + bumpCount(reporter, true); + goto skipOver; + } + } + *testRunner.fRunnables.append() = SkNEW_ARGS(SkpSkGrThreadedRunnable, + (&testSkGrMain, dirIndex, filename.c_str(), &testRunner)); + skipOver: + ; + } + } + testRunner.render(); + SkpSkGrThreadState& max = testRunner.fRunnables[0]->fState; + for (int dirIndex = 2; dirIndex <= 100; ++dirIndex) { + SkpSkGrThreadState& state = testRunner.fRunnables[dirIndex - 1]->fState; + for (int index = 0; index < state.fFoundCount; ++index) { + int maxIdx = max.fFoundCount; + if (maxIdx < kMaxFiles) { + max.fError[maxIdx] = state.fError[index]; + strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]); + max.fDirsFound[maxIdx] = state.fDirsFound[index]; + ++max.fFoundCount; + continue; + } + for (maxIdx = 0; maxIdx < max.fFoundCount; ++maxIdx) { + if (max.fError[maxIdx] < state.fError[index]) { + max.fError[maxIdx] = state.fError[index]; + strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]); + max.fDirsFound[maxIdx] = state.fDirsFound[index]; + break; + } + } + } + } + TestResult encoder; + encoder.fTestStep = kEncodeFiles; + for (int index = 0; index < max.fFoundCount; ++index) { + encoder.fDirNo = max.fDirsFound[index]; + strcpy(encoder.fFilename, max.fFilesFound[index]); + encoder.testOne(); + SkDebugf("+"); + } +} + +static void SkpSkGrOneOffTest(skiatest::Reporter* reporter) { + if (!initTest()) { + return; + } + int testIndex = 166; + int dirIndex = skipOverSkGr[testIndex - 166].directory; + SkString pictDir = make_in_dir_name(dirIndex); + if (pictDir.size() == 0) { + return; + } + SkString filename(skipOverSkGr[testIndex - 166].filename); + TestResult::Test(dirIndex, filename.c_str(), kCompareBits, reporter->verbose()); + TestResult::Test(dirIndex, filename.c_str(), kEncodeFiles, reporter->verbose()); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS_SHORT(SkpSkGrTest) + +DEFINE_TESTCLASS_SHORT(SkpSkGrOneOffTest) + +DEFINE_TESTCLASS_SHORT(SkpSkGrThreadedTest) |