aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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;