aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-01 17:36:03 +0000
committerGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-01 17:36:03 +0000
commita2bbc6e19d5332e81784e582c290cc060f40c4c7 (patch)
tree507a82e41b5a59f261295718091f0f1491b3d894
parent045c3d330c6c14f090c2222ece08d82cb84fb3ea (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
-rw-r--r--Makefile1
-rw-r--r--gyp/most.gyp1
-rwxr-xr-xgyp/skpskgr_test.gyp46
-rw-r--r--src/pathops/SkAddIntersections.cpp4
-rw-r--r--src/pathops/SkDCubicIntersection.cpp71
-rw-r--r--src/pathops/SkDCubicLineIntersection.cpp2
-rw-r--r--src/pathops/SkDLineIntersection.cpp4
-rw-r--r--src/pathops/SkDQuadIntersection.cpp8
-rw-r--r--src/pathops/SkDQuadLineIntersection.cpp20
-rw-r--r--src/pathops/SkIntersectionHelper.h25
-rw-r--r--src/pathops/SkIntersections.cpp15
-rw-r--r--src/pathops/SkIntersections.h7
-rw-r--r--src/pathops/SkOpAngle.cpp7
-rw-r--r--src/pathops/SkOpContour.cpp57
-rw-r--r--src/pathops/SkOpContour.h8
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp15
-rw-r--r--src/pathops/SkOpSegment.cpp192
-rw-r--r--src/pathops/SkOpSegment.h4
-rw-r--r--src/pathops/SkPathOpsCommon.cpp35
-rw-r--r--src/pathops/SkPathOpsCommon.h5
-rw-r--r--src/pathops/SkPathOpsDebug.cpp7
-rw-r--r--src/pathops/SkPathOpsOp.cpp15
-rw-r--r--src/pathops/SkPathOpsPoint.h18
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp10
-rw-r--r--src/pathops/SkPathOpsTriangle.cpp22
-rw-r--r--src/pathops/SkPathOpsTypes.cpp20
-rw-r--r--src/pathops/SkPathOpsTypes.h5
-rw-r--r--src/pathops/SkQuarticRoot.cpp2
-rw-r--r--tests/PathOpsCubicIntersectionTest.cpp12
-rw-r--r--tests/PathOpsCubicLineIntersectionTest.cpp3
-rw-r--r--tests/PathOpsCubicQuadIntersectionTest.cpp24
-rw-r--r--tests/PathOpsDQuadTest.cpp15
-rw-r--r--tests/PathOpsDTriangleTest.cpp30
-rw-r--r--tests/PathOpsExtendedTest.cpp5
-rw-r--r--tests/PathOpsOpTest.cpp36
-rw-r--r--tests/PathOpsQuadIntersectionTest.cpp3
-rw-r--r--tests/PathOpsQuadLineIntersectionTest.cpp7
-rwxr-xr-xtests/PathOpsSkpClipTest.cpp807
-rwxr-xr-xtests/PathOpsSkpTest.cpp924
-rw-r--r--tests/PathOpsThreadedCommon.h9
-rwxr-xr-xtests/PathOpsTypesTest.cpp24
-rwxr-xr-xtests/SkpSkGrTest.cpp759
42 files changed, 2957 insertions, 327 deletions
diff --git a/Makefile b/Makefile
index aa54c1f6d1..9b98edcad3 100644
--- a/Makefile
+++ b/Makefile
@@ -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)