remove scaling from pathops

PathOps added a cheat some time ago to reduce
fuzzer bugs by scaling down very large paths,
with the hope that it would make the math more
sane.

This had the side-effect of causing small edges
to disappear altogether if the bounds is large
enough.

Removing the scaling causes a single regression to
one fuzz-generated bug. That path succeeeded with
scale by eliminating the troublesome tiny contour.

Eliminating the scale may fix the CCPR-related bug
discovered by Flutter, or at least uncover the next
bug.

I would expect more fuzzer bugs to appear with
this change; paths with large and small values will
no longer have the small values removed.

R=csmartdalton@google.com,reed@google.com,bsalomon@google.com

Bug: skia:8290
Change-Id: I3bfdb101c568e9cfa324858685eac1f9c368c291
Reviewed-on: https://skia-review.googlesource.com/150465
Commit-Queue: Cary Clark <caryclark@skia.org>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Cary Clark 2018-08-30 12:58:23 -04:00 committed by Skia Commit-Bot
parent bc297beebb
commit 5de5233463
12 changed files with 1263 additions and 438 deletions

View File

@ -1,4 +1,40 @@
{
"crbug_526025": {
"p1": [[0, "0x43b40000", "0xcf000000"],
[4, "0x4e0d628f", "0xceffffff", "0x4e800003", "0xcec6b143", "0x4e800002", "0xce7ffffc"],
[4, "0x4e800002", "0xcde53aee", "0x4e0d6292", "0xc307820e", "0x44627d00", "0x437ffff2"],
[1, "0x444bf3bc", "0x4460537e"],
[1, "0x43553abd", "0x440f3cbd"],
[1, "0x42000000", "0x41800000"],
[1, "0x42c80000", "0x44000000"],
[1, "0x43553abd", "0x440f3cbd"],
[1, "0x43b40000", "0x44800000"],
[1, "0x43b40000", "0x45816000"]],
"fillType1": "kEvenOdd_FillType",
"p2": [[0, "0x42fe0000", "0x43a08000"],
[1, "0x45d5c000", "0x43870000"],
[1, "0xd0a00000", "0x4cbebc20"],
[1, "0x451f7000", "0x42800000"],
[1, "0x42fe0000", "0x43a08000"],
[5]],
"fillType2": "kWinding_FillType",
"op": "kUnion_SkPathOp",
"expectSuccess": "flaky",
"expectMatch": "flaky",
"succeeded": true,
"out": [[0, "0x4e800002", "0xce7ffffc"],
[4, "0x4e800003", "0xcec6b143", "0x4e0d628f", "0xceffffff", "0x43b40000", "0xcf000000"],
[1, "0x43b40000", "0x439425f8"],
[1, "0x43a0dee5", "0x43962d2c"],
[1, "0x42000000", "0x41800000"],
[1, "0x424da04e", "0x4297477e"],
[1, "0x43031abc", "0x43a0484d"],
[1, "0x4460205e", "0x428f689e"],
[1, "0x44627d00", "0x437ffff2"],
[4, "0x4e0d6292", "0xc307820e", "0x4e800002", "0xcde53aee", "0x4e800002", "0xce7ffffc"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
"bug8228": {
"p1": [[0, "0x41fd5557", "0x4292aaab"],
[1, "0x41fd5557", "0x41555556"],
@ -96,12 +132,9 @@
"expectSuccess": "flaky",
"expectMatch": "flaky",
"succeeded": true,
"out": [[0, "0xeee3ef57", "0xef6300f8"],
[2, "0xeeee9c6e", "0xef609993", "0x00000000", "0x6e5a5a1b"],
"out": [[0, "0x00000000", "0x00000000"],
[2, "0xef646464", "0xefefefef", "0x00000000", "0x6e5a5a1b"],
[1, "0x00000000", "0x00000000"],
[2, "0xe56c206c", "0x646c5f40", "0x6c80885e", "0x00000000"],
[1, "0x00000000", "0x00000000"],
[2, "0xeeda2c5a", "0xef6533a7", "0xeee3ef57", "0xef6300f8"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
@ -122,7 +155,7 @@
"expectMatch": "flaky",
"succeeded": true,
"out": [[0, "0x00000000", "0x6e5a5a1b"],
[2, "0xeeee9c6e", "0xef609993", "0xeee3ef57", "0xef6300f8"],
[1, "0xeee3ef57", "0xef6300f8"],
[2, "0xeeda2c5a", "0xef6533a7", "0x00000000", "0x00000000"],
[1, "0x00000000", "0x6e5a5a1b"],
[5],
@ -155,7 +188,7 @@
[1, "0x00000000", "0x00000000"],
[2, "0xe56c206c", "0x646c5f40", "0x6c80885e", "0x00000000"],
[1, "0x00000000", "0x00000000"],
[2, "0xeeda2c5a", "0xef6533a7", "0xeee3ef57", "0xef6300f8"],
[1, "0xeee3ef57", "0xef6300f8"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
@ -1258,36 +1291,6 @@
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
"crbug_526025": {
"p1": [[0, "0x43b40000", "0xcf000000"],
[4, "0x4e0d628f", "0xceffffff", "0x4e800003", "0xcec6b143", "0x4e800002", "0xce7ffffc"],
[4, "0x4e800002", "0xcde53aee", "0x4e0d6292", "0xc307820e", "0x44627d00", "0x437ffff2"],
[1, "0x444bf3bc", "0x4460537e"],
[1, "0x43553abd", "0x440f3cbd"],
[1, "0x42000000", "0x41800000"],
[1, "0x42c80000", "0x44000000"],
[1, "0x43553abd", "0x440f3cbd"],
[1, "0x43b40000", "0x44800000"],
[1, "0x43b40000", "0x45816000"]],
"fillType1": "kEvenOdd_FillType",
"p2": [[0, "0x42fe0000", "0x43a08000"],
[1, "0x45d5c000", "0x43870000"],
[1, "0xd0a00000", "0x4cbebc20"],
[1, "0x451f7000", "0x42800000"],
[1, "0x42fe0000", "0x43a08000"],
[5]],
"fillType2": "kWinding_FillType",
"op": "kUnion_SkPathOp",
"expectSuccess": "yes",
"expectMatch": "yes",
"succeeded": true,
"out": [[0, "0x4e800002", "0xce7ffffc"],
[4, "0x4e800003", "0xcec6b143", "0x4e0d628f", "0xceffffff", "0x00000000", "0xcf000000"],
[1, "0x00000000", "0x45816000"],
[4, "0x4e0d6292", "0x00000000", "0x4e800002", "0xcde53aee", "0x4e800002", "0xce7ffffc"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
"fuzz38": {
"p1": [[0, "0x42c8ae14", "0x4397a7f0"],
[1, "0xccbebc20", "0x4397a7f0"],

View File

@ -1,4 +1,107 @@
{
"bug8290": {
"path": [[0, "0xcf1c7651", "0xcf1c7650"],
[1, "0x4f1c7653", "0xcf1c7650"],
[1, "0x4f1c7653", "0x4f1c7654"],
[1, "0xcf1c7651", "0x4f1c7654"],
[1, "0xcf1c7651", "0xcf1c7650"],
[5],
[0, "0x433a0000", "0x44388800"],
[1, "0x445fb000", "0x44388800"],
[1, "0x445fb000", "0x4438c802"],
[1, "0x433a0000", "0x4438c802"],
[1, "0x433a0000", "0x44388800"],
[5],
[0, "0x433a0000", "0x44565002"],
[1, "0x445fb000", "0x44565002"],
[1, "0x445fb000", "0x44569000"],
[1, "0x433a0000", "0x44569000"],
[1, "0x433a0000", "0x44565002"],
[5],
[0, "0x433a0000", "0x44741804"],
[1, "0x445fb000", "0x44741804"],
[1, "0x445fb000", "0x44745806"],
[1, "0x433a0000", "0x44745806"],
[1, "0x433a0000", "0x44741804"],
[5],
[0, "0x433a0000", "0x4488f003"],
[1, "0x445fb000", "0x4488f003"],
[1, "0x445fb000", "0x44891004"],
[1, "0x433a0000", "0x44891004"],
[1, "0x433a0000", "0x4488f003"],
[5],
[0, "0x433a0000", "0x4497d404"],
[1, "0x445fb000", "0x4497d404"],
[1, "0x445fb000", "0x4497f405"],
[1, "0x433a0000", "0x4497f405"],
[1, "0x433a0000", "0x4497d404"],
[5],
[0, "0x433a0000", "0x44a6b805"],
[1, "0x445fb000", "0x44a6b805"],
[1, "0x445fb000", "0x44a6d806"],
[1, "0x433a0000", "0x44a6d806"],
[1, "0x433a0000", "0x44a6b805"],
[5],
[0, "0x433a0000", "0x44b59c06"],
[1, "0x445fb000", "0x44b59c06"],
[1, "0x445fb000", "0x44b5bc07"],
[1, "0x433a0000", "0x44b5bc07"],
[1, "0x433a0000", "0x44b59c06"],
[5]],
"fillType": "kEvenOdd_FillType",
"expectSuccess": "yes",
"expectMatch": "yes",
"succeeded": true,
"out": [[0, "0x4f1c7653", "0xcf1c7650"],
[1, "0xcf1c7651", "0xcf1c7650"],
[1, "0xcf1c7651", "0x4f1c7654"],
[1, "0x4f1c7653", "0x4f1c7654"],
[1, "0x4f1c7653", "0xcf1c7650"],
[5],
[0, "0x445fb000", "0x44388800"],
[1, "0x433a0000", "0x44388800"],
[1, "0x433a0000", "0x4438c802"],
[1, "0x445fb000", "0x4438c802"],
[1, "0x445fb000", "0x44388800"],
[5],
[0, "0x445fb000", "0x44565002"],
[1, "0x433a0000", "0x44565002"],
[1, "0x433a0000", "0x44569000"],
[1, "0x445fb000", "0x44569000"],
[1, "0x445fb000", "0x44565002"],
[5],
[0, "0x445fb000", "0x44741804"],
[1, "0x433a0000", "0x44741804"],
[1, "0x433a0000", "0x44745806"],
[1, "0x445fb000", "0x44745806"],
[1, "0x445fb000", "0x44741804"],
[5],
[0, "0x445fb000", "0x4488f003"],
[1, "0x433a0000", "0x4488f003"],
[1, "0x433a0000", "0x44891004"],
[1, "0x445fb000", "0x44891004"],
[1, "0x445fb000", "0x4488f003"],
[5],
[0, "0x445fb000", "0x4497d404"],
[1, "0x433a0000", "0x4497d404"],
[1, "0x433a0000", "0x4497f405"],
[1, "0x445fb000", "0x4497f405"],
[1, "0x445fb000", "0x4497d404"],
[5],
[0, "0x445fb000", "0x44a6b805"],
[1, "0x433a0000", "0x44a6b805"],
[1, "0x433a0000", "0x44a6d806"],
[1, "0x445fb000", "0x44a6d806"],
[1, "0x445fb000", "0x44a6b805"],
[5],
[0, "0x445fb000", "0x44b59c06"],
[1, "0x433a0000", "0x44b59c06"],
[1, "0x433a0000", "0x44b5bc07"],
[1, "0x445fb000", "0x44b5bc07"],
[1, "0x445fb000", "0x44b59c06"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},
"bug8249": {
"path": [[0, "0x43310000", "0x43810000"],
[1, "0x43480000", "0x43868000"],
@ -5869,11 +5972,9 @@
"out": [[0, "0x42f60000", "0x44160000"],
[1, "0x00000000", "0x44160000"],
[1, "0x00000000", "0x00000000"],
[1, "0x42f60000", "0x00000000"],
[1, "0x4c00002a", "0x00000000"],
[1, "0x4c00002a", "0x44160000"],
[1, "0x4bfffff0", "0x44160000"],
[1, "0x42f60000", "0x44160000"],
[5]],
"fillTypeOut": "kEvenOdd_FillType"
},

View File

@ -896,7 +896,11 @@ bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, in
bool success = markWinding(spanStart, winding);
SkOpSpanBase* last = nullptr;
SkOpSegment* other = this;
int safetyNet = 100000;
while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
if (!--safetyNet) {
return false;
}
if (spanStart->windSum() != SK_MinS32) {
// SkASSERT(spanStart->windSum() == winding); // FIXME: is this assert too aggressive?
SkASSERT(!last);

View File

@ -13,28 +13,6 @@
#include "SkPathWriter.h"
#include "SkTSort.h"
SkScalar ScaleFactor(const SkPath& path) {
static const SkScalar twoTo10 = 1024.f;
SkScalar largest = 0;
const SkScalar* oneBounds = &path.getBounds().fLeft;
for (int index = 0; index < 4; ++index) {
largest = SkTMax(largest, SkScalarAbs(oneBounds[index]));
}
SkScalar scale = twoTo10;
SkScalar next;
while ((next = scale * twoTo10) < largest) {
scale = next;
}
return scale == twoTo10 ? SK_Scalar1 : scale;
}
void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled) {
SkMatrix matrix;
matrix.setScale(scale, scale);
*scaled = path;
scaled->transform(matrix);
}
const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
bool* sortablePtr) {
// find first angle, initialize winding to computed fWindSum

View File

@ -26,7 +26,5 @@ bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* );
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkDEBUGPARAMS(bool skipAssert)
SkDEBUGPARAMS(const char* testName));
SkScalar ScaleFactor(const SkPath& path);
void ScalePath(const SkPath& path, SkScalar scale, SkPath* scaled);
#endif

View File

@ -294,18 +294,8 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
SkOpGlobalState globalState(contourList, &allocator
SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
SkOpCoincidence coincidence(&globalState);
SkScalar scaleFactor = SkTMax(ScaleFactor(one), ScaleFactor(two));
SkPath scaledOne, scaledTwo;
const SkPath* minuend, * subtrahend;
if (scaleFactor > SK_Scalar1) {
ScalePath(one, 1.f / scaleFactor, &scaledOne);
minuend = &scaledOne;
ScalePath(two, 1.f / scaleFactor, &scaledTwo);
subtrahend = &scaledTwo;
} else {
minuend = &one;
subtrahend = &two;
}
const SkPath* minuend = &one;
const SkPath* subtrahend = &two;
if (op == kReverseDifference_SkPathOp) {
using std::swap;
swap(minuend, subtrahend);
@ -375,9 +365,6 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
debugWorstState.debugDoYourWorst(&globalState);
}
#endif
if (scaleFactor > 1) {
ScalePath(*result, scaleFactor, result);
}
return true;
}

View File

@ -167,19 +167,10 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
SkPathOpsDebug::DumpSimplify(path, testName);
}
#endif
SkScalar scaleFactor = ScaleFactor(path);
SkPath scaledPath;
const SkPath* workingPath;
if (scaleFactor > SK_Scalar1) {
ScalePath(path, 1.f / scaleFactor, &scaledPath);
workingPath = &scaledPath;
} else {
workingPath = &path;
}
#if DEBUG_SORT
SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
#endif
SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
SkOpEdgeBuilder builder(path, contourList, &globalState);
if (!builder.finish()) {
return false;
}
@ -220,9 +211,6 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
return false;
}
wrapper.assemble(); // if some edges could not be resolved, assemble remaining
if (scaleFactor > 1) {
ScalePath(*result, scaleFactor, result);
}
return true;
}

View File

@ -279,7 +279,7 @@ private:
bool binarySearchCoin(SkTSect<OppCurve, TCurve>* , double tStart, double tStep, double* t,
double* oppT, SkTSpan<OppCurve, TCurve>** oppFirst);
SkTSpan<TCurve, OppCurve>* boundsMax() const;
SkTSpan<TCurve, OppCurve>* boundsMax();
bool coincidentCheck(SkTSect<OppCurve, TCurve>* sect2);
void coincidentForce(SkTSect<OppCurve, TCurve>* sect2, double start1s, double start1e);
bool coincidentHasT(double t);
@ -344,6 +344,7 @@ private:
int fActiveCount;
bool fRemovedStartT;
bool fRemovedEndT;
bool fHung;
SkDEBUGCODE(SkOpGlobalState* fDebugGlobalState);
SkDEBUGCODE(SkTSect<OppCurve, TCurve>* fOppSect);
PATH_OPS_DEBUG_T_SECT_CODE(int fID);
@ -875,6 +876,7 @@ SkTSect<TCurve, OppCurve>::SkTSect(const TCurve& c
, fCoincident(nullptr)
, fDeleted(nullptr)
, fActiveCount(0)
, fHung(false)
SkDEBUGPARAMS(fDebugGlobalState(debugGlobalState))
PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
@ -985,11 +987,16 @@ bool SkTSect<TCurve, OppCurve>::binarySearchCoin(SkTSect<OppCurve, TCurve>* sect
// so that each quad sect has a pointer to the largest, and can update it as spans
// are split
template<typename TCurve, typename OppCurve>
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::boundsMax() const {
SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::boundsMax() {
SkTSpan<TCurve, OppCurve>* test = fHead;
SkTSpan<TCurve, OppCurve>* largest = fHead;
bool lCollapsed = largest->fCollapsed;
int safetyNet = 10000;
while ((test = test->fNext)) {
if (!--safetyNet) {
fHung = true;
return nullptr;
}
bool tCollapsed = test->fCollapsed;
if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
largest->fBoundsMax < test->fBoundsMax)) {
@ -2197,12 +2204,18 @@ void SkTSect<TCurve, OppCurve>::BinarySearch(SkTSect<TCurve, OppCurve>* sect1,
// find the largest bounds
SkTSpan<TCurve, OppCurve>* largest1 = sect1->boundsMax();
if (!largest1) {
if (sect1->fHung) {
return;
}
break;
}
SkTSpan<OppCurve, TCurve>* largest2 = sect2->boundsMax();
// split it
if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
|| (!largest1->fCollapsed && largest2->fCollapsed)))) {
if (sect2->fHung) {
return;
}
if (largest1->fCollapsed) {
break;
}

View File

@ -53,16 +53,7 @@ bool TightBounds(const SkPath& path, SkRect* result) {
SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false)
SkDEBUGPARAMS(nullptr));
// turn path into list of segments
SkScalar scaleFactor = ScaleFactor(path);
SkPath scaledPath;
const SkPath* workingPath;
if (scaleFactor > SK_Scalar1) {
ScalePath(path, 1.f / scaleFactor, &scaledPath);
workingPath = &scaledPath;
} else {
workingPath = &path;
}
SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
SkOpEdgeBuilder builder(path, contourList, &globalState);
if (!builder.finish()) {
return false;
}
@ -75,10 +66,6 @@ bool TightBounds(const SkPath& path, SkRect* result) {
while ((current = current->next())) {
bounds.add(current->bounds());
}
if (scaleFactor > SK_Scalar1) {
bounds.set(bounds.left() * scaleFactor, bounds.top() * scaleFactor,
bounds.right() * scaleFactor, bounds.bottom() * scaleFactor);
}
*result = bounds;
if (!moveBounds.isEmpty()) {
result->join(moveBounds);

View File

@ -5170,6 +5170,9 @@ static void fuzz38(skiatest::Reporter* reporter, const char* filename) {
testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
}
// we currently don't produce meaningful intersections when a path has extremely large segments
// intersecting relatively small ones. This bug was reported as a fuzzer bug and wasn't expected
// to produce meaningful results
static void crbug_526025(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType((SkPath::FillType) 1);
@ -5195,7 +5198,7 @@ path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000)); // 127, 321
path.close();
SkPath path2(path);
testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
}
static void fuzzX_392(skiatest::Reporter* reporter, const char* filename) {
@ -9083,6 +9086,7 @@ static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
#define TEST(name) { name, #name }
static struct TestDesc tests[] = {
TEST(crbug_526025),
TEST(bug8228),
TEST(op_4),
TEST(op_1),
@ -9110,7 +9114,6 @@ static struct TestDesc tests[] = {
TEST(cubics_d),
TEST(dean2),
TEST(fuzzX_392),
TEST(crbug_526025),
TEST(fuzz38),
TEST(cubics44d),
TEST(cubics45u),

View File

@ -9298,11 +9298,71 @@ path.close();
testSimplify(reporter, path, filename);
}
static void bug8290(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(-1e+09, -1e+09);
path.lineTo(1e+09, -1e+09);
path.lineTo(1e+09, 1e+09);
path.lineTo(-1e+09, 1e+09);
path.lineTo(-1e+09, -1e+09);
path.close();
path.moveTo(0, 45);
path.lineTo(270, 45);
path.lineTo(270, 45.381f);
path.lineTo(0, 45.381f);
path.lineTo(0, 45);
path.close();
path.moveTo(0, 90.381f);
path.lineTo(270, 90.381f);
path.lineTo(270, 90.7619f);
path.lineTo(0, 90.7619f);
path.lineTo(0, 90.381f);
path.close();
path.moveTo(0, 135.762f);
path.lineTo(270, 135.762f);
path.lineTo(270, 136.143f);
path.lineTo(0, 136.143f);
path.lineTo(0, 135.762f);
path.close();
path.moveTo(0, 181.143f);
path.lineTo(270, 181.143f);
path.lineTo(270, 181.524f);
path.lineTo(0, 181.524f);
path.lineTo(0, 181.143f);
path.close();
path.moveTo(0, 226.524f);
path.lineTo(270, 226.524f);
path.lineTo(270, 226.905f);
path.lineTo(0, 226.905f);
path.lineTo(0, 226.524f);
path.close();
path.moveTo(0, 271.905f);
path.lineTo(270, 271.905f);
path.lineTo(270, 272.286f);
path.lineTo(0, 272.286f);
path.lineTo(0, 271.905f);
path.close();
path.moveTo(0, 317.286f);
path.lineTo(270, 317.286f);
path.lineTo(270, 317.667f);
path.lineTo(0, 317.667f);
path.lineTo(0, 317.286f);
path.close();
SkMatrix matrix = SkMatrix::MakeAll(
2.625, 0, 186,
0, 2.625, 620,
0, 0, 1);
path.transform(matrix);
testSimplify(reporter, path, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = nullptr;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = nullptr;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = nullptr;
static TestDesc tests[] = {
TEST(bug8290),
TEST(bug8249),
TEST(grshapearc),
TEST(coincubics),

File diff suppressed because it is too large Load Diff