aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2016-10-18 07:59:44 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-10-18 07:59:44 -0700
commitb36a3cd137e2b6c328854015018594bb9967e493 (patch)
tree78e6c88a0915d3004381a359f9a517341fe4aa97
parentc5eceb00adaa05e50ed732985a2df7db17c80053 (diff)
break ambiguous angle sorting loop
A pair of cubics may be difficult to sort if the tangents suggest one sort but the midpoints suggest a different one. When in this gray area, and when the cumulative sort of all the angles fails to resolve, reverse the sort to break the tie. Before, when tiger8 was run through the signed distance field generated directly from the path data, the simplify call might hang since the angle could not be resolved. If the endless loop is detected, and if there is no tie to break, just fail instead. TBR=reed@google.com BUG=skia:5131 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2426753002 Review-Url: https://codereview.chromium.org/2426753002
-rw-r--r--gyp/pathops_unittest.gypi1
-rw-r--r--src/pathops/SkOpAngle.cpp28
-rw-r--r--src/pathops/SkOpAngle.h11
-rw-r--r--src/pathops/SkOpContour.h5
-rw-r--r--src/pathops/SkOpSegment.cpp7
-rw-r--r--src/pathops/SkOpSegment.h2
-rw-r--r--src/pathops/SkPathOpsCommon.cpp11
-rw-r--r--src/pathops/SkPathOpsDebug.h2
-rw-r--r--tests/PathOpsChalkboardTest.cpp181
-rw-r--r--tests/PathOpsSimplifyTest.cpp12
-rw-r--r--tools/pathops_sorter.htm13
11 files changed, 247 insertions, 26 deletions
diff --git a/gyp/pathops_unittest.gypi b/gyp/pathops_unittest.gypi
index baf0196d0b..0ff76fcb84 100644
--- a/gyp/pathops_unittest.gypi
+++ b/gyp/pathops_unittest.gypi
@@ -27,6 +27,7 @@
'../tests/PathOpsBuilderConicTest.cpp',
'../tests/PathOpsBuilderTest.cpp',
'../tests/PathOpsBuildUseTest.cpp',
+ '../tests/PathOpsChalkboardTest.cpp',
'../tests/PathOpsConicIntersectionTest.cpp',
'../tests/PathOpsConicLineIntersectionTest.cpp',
'../tests/PathOpsConicQuadIntersectionTest.cpp',
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index 820b5dceee..99f93dd544 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -320,7 +320,7 @@ recomputeSector:
return !fUnorderable;
}
-int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
+int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) {
const SkDVector* sweep = this->fPart.fSweep;
const SkDVector* tweep = rh->fPart.fSweep;
double s0xs1 = sweep[0].crossCheck(sweep[1]);
@@ -593,20 +593,20 @@ SkOpGlobalState* SkOpAngle::globalState() const {
// OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
// OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
-void SkOpAngle::insert(SkOpAngle* angle) {
+bool SkOpAngle::insert(SkOpAngle* angle) {
if (angle->fNext) {
if (loopCount() >= angle->loopCount()) {
if (!merge(angle)) {
- return;
+ return true;
}
} else if (fNext) {
if (!angle->merge(this)) {
- return;
+ return true;
}
} else {
angle->insert(this);
}
- return;
+ return true;
}
bool singleton = nullptr == fNext;
if (singleton) {
@@ -622,20 +622,27 @@ void SkOpAngle::insert(SkOpAngle* angle) {
angle->fNext = this;
}
debugValidateNext();
- return;
+ return true;
}
SkOpAngle* last = this;
+ bool flipAmbiguity = false;
do {
SkASSERT(last->fNext == next);
- if (angle->after(last)) {
+ if (angle->after(last) ^ (angle->tangentsAmbiguous() & flipAmbiguity)) {
last->fNext = angle;
angle->fNext = next;
debugValidateNext();
- return;
+ return true;
}
last = next;
+ if (last == this) {
+ FAIL_IF(flipAmbiguity);
+ // We're in a loop. If a sort was ambiguous, flip it to end the loop.
+ flipAmbiguity = true;
+ }
next = next->fNext;
} while (true);
+ return true;
}
SkOpSpanBase* SkOpAngle::lastMarked() const {
@@ -815,7 +822,7 @@ void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
fComputedEnd = fEnd = end;
SkASSERT(start != end);
fNext = nullptr;
- fComputeSector = fComputedSector = fCheckCoincidence = false;
+ fComputeSector = fComputedSector = fCheckCoincidence = fTangentsAmbiguous = false;
setSpans();
setSector();
SkDEBUGCODE(fID = start ? start->globalState()->nextAngleID() : -1);
@@ -966,7 +973,7 @@ SkOpSpan* SkOpAngle::starter() {
return fStart->starter(fEnd);
}
-bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
+bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) {
if (s0xt0 == 0) {
return false;
}
@@ -991,5 +998,6 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
double tDist = tweep[0].length() * m;
bool useS = fabs(sDist) < fabs(tDist);
double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
+ fTangentsAmbiguous = mFactor >= 50 && mFactor < 200;
return mFactor < 50; // empirically found limit
}
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index cbdadf1039..3cebfff717 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -64,7 +64,7 @@ public:
return fEnd;
}
- void insert(SkOpAngle* );
+ bool insert(SkOpAngle* );
SkOpSpanBase* lastMarked() const;
bool loopContains(const SkOpAngle* ) const;
int loopCount() const;
@@ -87,6 +87,10 @@ public:
SkOpSpan* starter();
+ bool tangentsAmbiguous() const {
+ return fTangentsAmbiguous;
+ }
+
bool unorderable() const {
return fUnorderable;
}
@@ -97,7 +101,7 @@ private:
bool checkCrossesZero() const;
bool checkParallel(SkOpAngle* );
bool computeSector();
- int convexHullOverlaps(const SkOpAngle* ) const;
+ int convexHullOverlaps(const SkOpAngle* );
bool endToSide(const SkOpAngle* rh, bool* inside) const;
bool endsIntersect(SkOpAngle* );
int findSector(SkPath::Verb verb, double x, double y) const;
@@ -109,7 +113,7 @@ private:
bool orderable(SkOpAngle* rh); // false == this < rh ; true == this > rh
void setSector();
void setSpans();
- bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+ bool tangentsDiverge(const SkOpAngle* rh, double s0xt0);
SkDCurve fOriginalCurvePart; // the curve from start to end
SkDCurveSweep fPart; // the curve from start to end offset as needed
@@ -127,6 +131,7 @@ private:
bool fComputeSector;
bool fComputedSector;
bool fCheckCoincidence;
+ bool fTangentsAmbiguous;
SkDEBUGCODE(int fID);
friend class PathOpsAngleTester;
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index dc07c53045..f6c879f16f 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -341,12 +341,13 @@ public:
fXor = isXor;
}
- void sortAngles() {
+ bool sortAngles() {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
do {
- segment->sortAngles();
+ FAIL_IF(!segment->sortAngles());
} while ((segment = segment->next()));
+ return true;
}
const SkPoint& start() const {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 2246f36ff2..6012d832bf 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -855,7 +855,7 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, in
SkOpSegment* other = this;
while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
if (spanStart->windSum() != SK_MinS32) {
- SkASSERT(spanStart->windSum() == winding);
+// SkASSERT(spanStart->windSum() == winding); // FIXME: is this assert too aggressive?
SkASSERT(!last);
break;
}
@@ -1459,7 +1459,7 @@ void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
SkASSERT(!DEBUG_LIMIT_WIND_SUM || SkTAbs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
}
-void SkOpSegment::sortAngles() {
+bool SkOpSegment::sortAngles() {
SkOpSpanBase* span = &this->fHead;
do {
SkOpAngle* fromAngle = span->fromAngle();
@@ -1477,7 +1477,7 @@ void SkOpSegment::sortAngles() {
span->debugID());
wroteAfterHeader = true;
#endif
- fromAngle->insert(toAngle);
+ FAIL_IF(!fromAngle->insert(toAngle));
} else if (!fromAngle) {
baseAngle = toAngle;
}
@@ -1527,6 +1527,7 @@ void SkOpSegment::sortAngles() {
SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
#endif
} while (!span->final() && (span = span->upCast()->next()));
+ return true;
}
bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index b6e7714018..e9618ceeb2 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -371,7 +371,7 @@ public:
int* maxWinding, int* sumWinding);
void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
- void sortAngles();
+ bool sortAngles();
bool spansNearby(const SkOpSpanBase* ref, const SkOpSpanBase* check) const;
static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index b4049bc5d9..a1cd7bbd63 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -235,11 +235,14 @@ static void move_nearby(SkOpContourHead* contourList DEBUG_COIN_DECLARE_PARAMS(
} while ((contour = contour->next()));
}
-static void sort_angles(SkOpContourHead* contourList) {
+static bool sort_angles(SkOpContourHead* contourList) {
SkOpContour* contour = contourList;
do {
- contour->sortAngles();
+ if (!contour->sortAngles()) {
+ return false;
+ }
} while ((contour = contour->next()));
+ return true;
}
bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence) {
@@ -327,7 +330,9 @@ bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidenc
}
} while (!overlaps.isEmpty());
calc_angles(contourList DEBUG_COIN_PARAMS());
- sort_angles(contourList);
+ if (!sort_angles(contourList)) {
+ return false;
+ }
#if DEBUG_COINCIDENCE_VERBOSE
coincidence->debugShowCoincidence();
#endif
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index f07d7d0524..4dc52721e6 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -22,7 +22,7 @@ class SkOpContourHead;
#define FORCE_RELEASE 1 // set force release to 1 for multiple thread -- no debugging
#endif
-#define DEBUG_UNDER_DEVELOPMENT 1
+#define DEBUG_UNDER_DEVELOPMENT 0
#define ONE_OFF_DEBUG 0
#define ONE_OFF_DEBUG_MATHEMATICA 0
diff --git a/tests/PathOpsChalkboardTest.cpp b/tests/PathOpsChalkboardTest.cpp
new file mode 100644
index 0000000000..134da6b89f
--- /dev/null
+++ b/tests/PathOpsChalkboardTest.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsThreadedCommon.h"
+
+#define TEST(name) { name, #name }
+
+static void chalkboard(skiatest::Reporter* reporter, uint64_t testlines) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+uint64_t i = 0;
+path.moveTo(SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1)); // 963.732f, 312.209f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470dde3), SkBits2Float(0x439c63d8)); // 963.467f, 312.78f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470dbd7), SkBits2Float(0x439c3e57), SkBits2Float(0x4470c893), SkBits2Float(0x439c69fd), SkBits2Float(0x4470cfcf), SkBits2Float(0x439c297a)); // 963.435f, 312.487f, 963.134f, 312.828f, 963.247f, 312.324f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470c46b), SkBits2Float(0x439c8149), SkBits2Float(0x4470b137), SkBits2Float(0x439c2938), SkBits2Float(0x4470b5f4), SkBits2Float(0x439ca99b)); // 963.069f, 313.01f, 962.769f, 312.322f, 962.843f, 313.325f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e842), SkBits2Float(0x439c8335), SkBits2Float(0x447125a2), SkBits2Float(0x439cce78), SkBits2Float(0x44715a2d), SkBits2Float(0x439c61ed)); // 963.629f, 313.025f, 964.588f, 313.613f, 965.409f, 312.765f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447150d5), SkBits2Float(0x439c945c)); // 965.263f, 313.159f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471546b), SkBits2Float(0x439c87f2), SkBits2Float(0x4471579e), SkBits2Float(0x439c8085), SkBits2Float(0x44715a8f), SkBits2Float(0x439c7c4c)); // 965.319f, 313.062f, 965.369f, 313.004f, 965.415f, 312.971f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715cbc), SkBits2Float(0x439c79dd)); // 965.449f, 312.952f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715dd3), SkBits2Float(0x439c7918)); // 965.466f, 312.946f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e56), SkBits2Float(0x439c78d6)); // 965.474f, 312.944f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5)); // 965.476f, 312.943f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5)); // 965.476f, 312.943f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e87), SkBits2Float(0x439c78b5)); // 965.477f, 312.943f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471a50e), SkBits2Float(0x439d05c3), SkBits2Float(0x4470fe77), SkBits2Float(0x439bb894), SkBits2Float(0x44710f9e), SkBits2Float(0x439bdb03)); // 966.579f, 314.045f, 963.976f, 311.442f, 964.244f, 311.711f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fae), SkBits2Float(0x439bdb24)); // 964.245f, 311.712f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fbe), SkBits2Float(0x439bdba7)); // 964.246f, 311.716f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fce), SkBits2Float(0x439be397)); // 964.247f, 311.778f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710eb7), SkBits2Float(0x439bedf5), SkBits2Float(0x44710978), SkBits2Float(0x439bf74d), SkBits2Float(0x447105e2), SkBits2Float(0x439c0064)); // 964.23f, 311.859f, 964.148f, 311.932f, 964.092f, 312.003f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fe86), SkBits2Float(0x439c1270), SkBits2Float(0x4470fd4f), SkBits2Float(0x439c2250), SkBits2Float(0x44712fde), SkBits2Float(0x439c33d9)); // 963.977f, 312.144f, 963.958f, 312.268f, 964.748f, 312.405f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fc48), SkBits2Float(0x439c3271)); // 963.942f, 312.394f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470ee13), SkBits2Float(0x439c4c2b), SkBits2Float(0x4471476b), SkBits2Float(0x439c5c0b), SkBits2Float(0x44711177), SkBits2Float(0x439c7a40)); // 963.72f, 312.595f, 965.116f, 312.719f, 964.273f, 312.955f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712685), SkBits2Float(0x439c7648)); // 964.602f, 312.924f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447126a6), SkBits2Float(0x439c7d31), SkBits2Float(0x44711d2d), SkBits2Float(0x439c8085), SkBits2Float(0x44711d1d), SkBits2Float(0x439c8790)); // 964.604f, 312.978f, 964.456f, 313.004f, 964.455f, 313.059f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712675), SkBits2Float(0x439c843c)); // 964.601f, 313.033f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713bd5), SkBits2Float(0x439c94e0), SkBits2Float(0x44713956), SkBits2Float(0x439ca065), SkBits2Float(0x44712b63), SkBits2Float(0x439cb357)); // 964.935f, 313.163f, 964.896f, 313.253f, 964.678f, 313.401f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af0), SkBits2Float(0x439cb5a5), SkBits2Float(0x44712459), SkBits2Float(0x439cab47), SkBits2Float(0x44711fad), SkBits2Float(0x439ca607)); // 964.421f, 313.419f, 964.568f, 313.338f, 964.495f, 313.297f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710f1a), SkBits2Float(0x439caf3e), SkBits2Float(0x4471325d), SkBits2Float(0x439cbb26), SkBits2Float(0x4471326e), SkBits2Float(0x439cc93a)); // 964.236f, 313.369f, 964.787f, 313.462f, 964.788f, 313.572f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712428), SkBits2Float(0x439cd501), SkBits2Float(0x44711ad0), SkBits2Float(0x439cca82), SkBits2Float(0x447113b6), SkBits2Float(0x439cc95b)); // 964.565f, 313.664f, 964.419f, 313.582f, 964.308f, 313.573f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712b95), SkBits2Float(0x439cf20f), SkBits2Float(0x4470f550), SkBits2Float(0x439d0790), SkBits2Float(0x4471426e), SkBits2Float(0x439d21ce)); // 964.681f, 313.891f, 963.833f, 314.059f, 965.038f, 314.264f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715072), SkBits2Float(0x439d241c), SkBits2Float(0x44715c6a), SkBits2Float(0x439d15a5), SkBits2Float(0x44716364), SkBits2Float(0x439d24c0)); // 965.257f, 314.282f, 965.444f, 314.169f, 965.553f, 314.287f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717b22), SkBits2Float(0x439d0791), SkBits2Float(0x44715cbc), SkBits2Float(0x439cf231), SkBits2Float(0x4471475c), SkBits2Float(0x439cda20)); // 965.924f, 314.059f, 965.449f, 313.892f, 965.115f, 313.704f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471477d), SkBits2Float(0x439ce12a)); // 965.117f, 313.759f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fc4a), SkBits2Float(0x439cd14b), SkBits2Float(0x44715810), SkBits2Float(0x439cd0e8), SkBits2Float(0x4471372b), SkBits2Float(0x439cb272)); // 963.942f, 313.635f, 965.376f, 313.632f, 964.862f, 313.394f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447155b2), SkBits2Float(0x439cb91a), SkBits2Float(0x44715581), SkBits2Float(0x439cc72e), SkBits2Float(0x447165f4), SkBits2Float(0x439ccbeb)); // 965.339f, 313.446f, 965.336f, 313.556f, 965.593f, 313.593f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44719e77), SkBits2Float(0x439ca2b4), SkBits2Float(0x44713979), SkBits2Float(0x439c993b), SkBits2Float(0x4471821d), SkBits2Float(0x439c7b47)); // 966.476f, 313.271f, 964.898f, 313.197f, 966.033f, 312.963f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471847b), SkBits2Float(0x439c7dd6)); // 966.07f, 312.983f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44718b96), SkBits2Float(0x439c77b1), SkBits2Float(0x44717d81), SkBits2Float(0x439c6ebb), SkBits2Float(0x44717667), SkBits2Float(0x439c66ab)); // 966.181f, 312.935f, 965.961f, 312.865f, 965.85f, 312.802f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44716cff), SkBits2Float(0x439c6a41), SkBits2Float(0x44716842), SkBits2Float(0x439c7315), SkBits2Float(0x44716159), SkBits2Float(0x439c793a)); // 965.703f, 312.83f, 965.629f, 312.899f, 965.521f, 312.947f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715a0d), SkBits2Float(0x439c712a), SkBits2Float(0x44713938), SkBits2Float(0x439c6f3e), SkBits2Float(0x44712b34), SkBits2Float(0x439c6d73)); // 965.407f, 312.884f, 964.894f, 312.869f, 964.675f, 312.855f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714c19), SkBits2Float(0x439c614a), SkBits2Float(0x44711af2), SkBits2Float(0x439c61ee), SkBits2Float(0x44712b34), SkBits2Float(0x439c518c)); // 965.189f, 312.76f, 964.421f, 312.765f, 964.675f, 312.637f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447149ab), SkBits2Float(0x439c499c), SkBits2Float(0x4471474d), SkBits2Float(0x439c5c0b), SkBits2Float(0x447157d0), SkBits2Float(0x439c6065)); // 965.151f, 312.575f, 965.114f, 312.719f, 965.372f, 312.753f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447142b1), SkBits2Float(0x439c4fa0)); // 965.042f, 312.622f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714053), SkBits2Float(0x439c3f1d), SkBits2Float(0x44716396), SkBits2Float(0x439c3c6d), SkBits2Float(0x447173f9), SkBits2Float(0x439c3292)); // 965.005f, 312.493f, 965.556f, 312.472f, 965.812f, 312.395f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715c7c), SkBits2Float(0x439c2628), SkBits2Float(0x44716397), SkBits2Float(0x439c3c4c), SkBits2Float(0x447142b1), SkBits2Float(0x439c3398)); // 965.445f, 312.298f, 965.556f, 312.471f, 965.042f, 312.403f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715572), SkBits2Float(0x439c2919), SkBits2Float(0x44715bd8), SkBits2Float(0x439c10a6), SkBits2Float(0x447159bb), SkBits2Float(0x439bf68a)); // 965.335f, 312.321f, 965.435f, 312.13f, 965.402f, 311.926f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715698), SkBits2Float(0x439be2f4)); // 965.353f, 311.773f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447153f8), SkBits2Float(0x439bd95a)); // 965.312f, 311.698f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471526f), SkBits2Float(0x439bd49e)); // 965.288f, 311.661f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471524e), SkBits2Float(0x439bd45c)); // 965.286f, 311.659f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471523e), SkBits2Float(0x439bd41a)); // 965.285f, 311.657f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717148), SkBits2Float(0x439c124f), SkBits2Float(0x44715ae2), SkBits2Float(0x439be562), SkBits2Float(0x447161cb), SkBits2Float(0x439bf335)); // 965.77f, 312.143f, 965.42f, 311.792f, 965.528f, 311.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356)); // 965.527f, 311.901f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356)); // 965.527f, 311.901f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44716169), SkBits2Float(0x439bf3b8)); // 965.522f, 311.904f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447160c5), SkBits2Float(0x439bf47d)); // 965.512f, 311.91f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715f7d), SkBits2Float(0x439bf627)); // 965.492f, 311.923f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447158f6), SkBits2Float(0x439bfeba), SkBits2Float(0x447152e1), SkBits2Float(0x439c0ac3), SkBits2Float(0x44714e15), SkBits2Float(0x439c1919)); // 965.39f, 311.99f, 965.295f, 312.084f, 965.22f, 312.196f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471548c), SkBits2Float(0x439c10c7), SkBits2Float(0x447151bb), SkBits2Float(0x439bd7f2), SkBits2Float(0x44715927), SkBits2Float(0x439be271)); // 965.321f, 312.131f, 965.277f, 311.687f, 965.393f, 311.769f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447156b8), SkBits2Float(0x439bd41b), SkBits2Float(0x44714c19), SkBits2Float(0x439bf356), SkBits2Float(0x44714b13), SkBits2Float(0x439c222f)); // 965.355f, 311.657f, 965.189f, 311.901f, 965.173f, 312.267f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713dd4), SkBits2Float(0x439c4aa2), SkBits2Float(0x44712ea9), SkBits2Float(0x439c2be9), SkBits2Float(0x44712344), SkBits2Float(0x439c0085)); // 964.966f, 312.583f, 964.729f, 312.343f, 964.551f, 312.004f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712605), SkBits2Float(0x439c2fa0)); // 964.594f, 312.372f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af3), SkBits2Float(0x439c7e9a), SkBits2Float(0x44710de4), SkBits2Float(0x439bf41b), SkBits2Float(0x4470fb65), SkBits2Float(0x439c20c7)); // 964.421f, 312.989f, 964.217f, 311.907f, 963.928f, 312.256f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fbb7), SkBits2Float(0x439c220f)); // 963.933f, 312.266f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470f5e4), SkBits2Float(0x439c2bc9), SkBits2Float(0x4470ef5d), SkBits2Float(0x439c9e59), SkBits2Float(0x4470e50f), SkBits2Float(0x439c85e6)); // 963.842f, 312.342f, 963.74f, 313.237f, 963.579f, 313.046f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e8f6), SkBits2Float(0x439c4e35), SkBits2Float(0x4470ee98), SkBits2Float(0x439c5333), SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1)); // 963.64f, 312.611f, 963.728f, 312.65f, 963.732f, 312.209f
+SkASSERT(64 == i);
+path.close();
+
+testSimplify(reporter, path, "chalkboard");
+}
+
+static void testChalkboard(PathOpsThreadState* data) {
+ uint64_t testlines = ((uint64_t) data->fB << 32) | (unsigned int) data->fA;
+ chalkboard(data->fReporter, testlines);
+}
+
+static void chalkboard_threaded(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT
+ return;
+#endif
+ initializeTests(reporter, "chalkboard");
+ PathOpsThreadedTestRunner testRunner(reporter);
+ SkRandom r;
+ for (int samples = 0; samples <= 64; ++samples) {
+ int testCount;
+ int bitCount = samples < 32 ? samples : 64 - samples;
+ int index1 = 63;
+ int index2 = 62;
+ switch (bitCount) {
+ case 0:
+ testCount = 1;
+ break;
+ case 1:
+ testCount = 64;
+ break;
+ case 2:
+ testCount = reporter->allowExtendedTest() ? 63 * 62 / 2 : 100;
+ break;
+ default:
+ testCount = reporter->allowExtendedTest() ? 10000 : 100;
+ break;
+ }
+ for (int test = 0; test < testCount; ++test) {
+ uint64_t testlines;
+ switch (bitCount) {
+ case 0:
+ testlines = 0;
+ break;
+ case 1:
+ testlines = 1LL << test;
+ break;
+ case 2:
+ if (reporter->allowExtendedTest()) {
+ SkASSERT(index1 >= 1);
+ SkASSERT(index2 >= 0);
+ testlines = 1LL << index1;
+ testlines |= 1LL << index2;
+ if (--index2 < 0) {
+ --index1;
+ index2 = index1 - 1;
+ }
+ break;
+ }
+ default:
+ testlines = 0;
+ for (int i = 0; i < bitCount; ++i) {
+ int bit;
+ do {
+ bit = r.nextRangeU(0, 64);
+ } while (testlines & (1LL << bit));
+ testlines |= 1LL << bit;
+ }
+ }
+ if (samples >= 32) {
+ testlines ^= 0xFFFFFFFFFFFFFFFFLL;
+ }
+ *testRunner.fRunnables.append() =
+ new PathOpsThreadedRunnable(&testChalkboard,
+ (int) (unsigned) (testlines & 0xFFFFFFFF),
+ (int) (unsigned) (testlines >> 32),
+ 0, 0, &testRunner);
+ }
+ }
+ testRunner.render();
+}
+
+static void chalkboard_1(skiatest::Reporter* reporter, const char* filename) {
+ uint64_t testlines = 0xFFFFFFFFFFFFFFFFLL;
+ chalkboard(reporter, testlines);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static TestDesc tests[] = {
+ TEST(chalkboard_1),
+ TEST(chalkboard_threaded),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+static bool runReverse = false;
+
+DEF_TEST(PathOpsChalkboard, reporter) {
+ RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 56b11ae6c1..52699ca6bf 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -5547,11 +5547,23 @@ path.cubicTo(SkBits2Float(0xc29e0000), SkBits2Float(0xc25c71c7), SkBits2Float(0x
testSimplify(reporter, path, filename);
}
+static void tiger8_393(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+ path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666)); // 92.6f, 427.3f
+path.cubicTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666), SkBits2Float(0x42b5cccd), SkBits2Float(0x43da1999), SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333)); // 92.6f, 427.3f, 90.9f, 436.2f, 92, 443.9f
+path.cubicTo(SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333), SkBits2Float(0x42b30000), SkBits2Float(0x43e17333), SkBits2Float(0x42cf999a), SkBits2Float(0x43e1b333)); // 92, 443.9f, 89.5f, 450.9f, 103.8f, 451.4f
+path.cubicTo(SkBits2Float(0x42ec3334), SkBits2Float(0x43e14ccd), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333)); // 118.1f, 450.6f, 115.6f, 443.9f, 115.6f, 443.9f
+path.cubicTo(SkBits2Float(0x42e7999a), SkBits2Float(0x43de8000), SkBits2Float(0x42ea6667), SkBits2Float(0x43db4000), SkBits2Float(0x42e60001), SkBits2Float(0x43d5a666)); // 115.8f, 445, 117.2f, 438.5f, 115, 427.3f
+ testSimplify(reporter, path, filename);
+}
+
static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
static TestDesc tests[] = {
+ TEST(tiger8_393),
TEST(bug5169),
TEST(testQuads73),
TEST(testQuads72),
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index d2d90da8bc..612a68492e 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -7,17 +7,24 @@
<div style="height:0">
<div id="cubics">
-{{{317, 711}, {322.522857666015625, 711}, {327, 715.4771728515625}, {327, 721}}},
-{{{324.071075439453125, 713.928955078125}, {324.4051513671875, 714.26300048828125}, {324.715667724609375, 714.62060546875}, {325, 714.9990234375}}},
+{{{103.800003f, 451.399994f}, {118.100006f, 450.600006f}, {115.600006f, 443.899994f}, {115.600006f, 443.899994f}}} id=3
+{{{115.600006f, 443.899994f}, {115.800003f, 445}, {117.200005f, 438.5f}, {115.000008f, 427.299988f}}} id=4
</div>
+<div id="cubics2">
+{{{115.6316070556640625, 443.999237060546875}, {115.9124092648639675, 444.4395003767372145}, {117.1065847217176383, 438.0244068281508589}, {115.0000076293945313, 427.29998779296875}}} id=44
+{{{115.6316070556640625, 443.999237060546875}, {115.619154389193497, 443.9797128116054523}, {115.6084986998821762, 443.9467041484157335}, {115.600006103515625, 443.899993896484375}}} id=43
+{{{115.6316070556640625, 443.999237060546875}, {115.8726462570580225, 444.8329011683850354}, {117.0719462895199854, 450.6575499937891891}, {103.8000106811523438, 451.4000244140625}}} id=31
+{{{115.6316070556640625, 443.999237060546875}, {115.6129357357566789, 443.9346599744848163}, {115.6000137329101563, 443.9000244140625}, {115.6000137329101563, 443.9000244140625}}} id=32
+</div>
</div>
<script type="text/javascript">
var testDivs = [
+ cubics2,
cubics
-];
+ ];
var decimal_places = 3;