• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkPathTypes.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRegion.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkStream.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTypes.h"
22 #include "include/private/base/SkDebug.h"
23 #include "include/private/base/SkFloatBits.h"
24 #include "include/private/base/SkMutex.h"
25 #include "include/private/base/SkTDArray.h"
26 #include "include/utils/SkParsePath.h"
27 #include "src/core/SkPathPriv.h"
28 #include "src/pathops/SkPathOpsDebug.h"
29 #include "tests/PathOpsDebug.h"
30 #include "tests/PathOpsExtendedTest.h"
31 #include "tests/PathOpsThreadedCommon.h"
32 #include "tests/Test.h"
33 
34 #include <algorithm>
35 #include <cstdint>
36 #include <cstdio>
37 #include <cstdlib>
38 #include <string>
39 #include <vector>
40 
41 std::vector<std::string> gUniqueNames;
42 
43 #ifdef SK_BUILD_FOR_MAC
44 #include <sys/sysctl.h>
45 #endif
46 
47 // std::to_string isn't implemented on android
48 #include <sstream>
49 
50 template <typename T>
std_to_string(T value)51 std::string std_to_string(T value)
52 {
53     std::ostringstream os ;
54     os << value ;
55     return os.str() ;
56 }
57 
58 bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
59              SkDEBUGPARAMS(bool skipAssert)
60              SkDEBUGPARAMS(const char* testName));
61 
62 bool SimplifyDebug(const SkPath& one, SkPath* result
63                    SkDEBUGPARAMS(bool skipAssert)
64                    SkDEBUGPARAMS(const char* testName));
65 
66 static const char marker[] =
67     "</div>\n"
68     "\n"
69     "<script type=\"text/javascript\">\n"
70     "\n"
71     "var testDivs = [\n";
72 
73 static const char* opStrs[] = {
74     "kDifference_SkPathOp",
75     "kIntersect_SkPathOp",
76     "kUnion_SkPathOp",
77     "kXOR_PathOp",
78     "kReverseDifference_SkPathOp",
79 };
80 
81 static const char* opSuffixes[] = {
82     "d",
83     "i",
84     "u",
85     "o",
86     "r",
87 };
88 
89 enum class ExpectSuccess {
90     kNo,
91     kYes,
92     kFlaky
93 };
94 
95 enum class SkipAssert {
96     kNo,
97     kYes
98 };
99 
100 enum class ExpectMatch {
101     kNo,
102     kYes,
103     kFlaky
104 };
105 
showOp(const SkPathOp op)106 void showOp(const SkPathOp op) {
107     switch (op) {
108         case kDifference_SkPathOp:
109             SkDebugf("op difference\n");
110             break;
111         case kIntersect_SkPathOp:
112             SkDebugf("op intersect\n");
113             break;
114         case kUnion_SkPathOp:
115             SkDebugf("op union\n");
116             break;
117         case kXOR_SkPathOp:
118             SkDebugf("op xor\n");
119             break;
120         case kReverseDifference_SkPathOp:
121             SkDebugf("op reverse difference\n");
122             break;
123         default:
124             SkASSERT(0);
125     }
126 }
127 
128 const int kBitWidth = 64;
129 const int kBitHeight = 64;
130 
scaleMatrix(const SkPath & one,const SkPath & two,SkMatrix & scale)131 static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
132     SkRect larger = one.getBounds();
133     larger.join(two.getBounds());
134     SkScalar largerWidth = larger.width();
135     if (largerWidth < 4) {
136         largerWidth = 4;
137     }
138     SkScalar largerHeight = larger.height();
139     if (largerHeight < 4) {
140         largerHeight = 4;
141     }
142     SkScalar hScale = (kBitWidth - 2) / largerWidth;
143     SkScalar vScale = (kBitHeight - 2) / largerHeight;
144     scale.reset();
145     scale.preScale(hScale, vScale);
146     larger.fLeft *= hScale;
147     larger.fRight *= hScale;
148     larger.fTop *= vScale;
149     larger.fBottom *= vScale;
150     SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
151             : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
152     SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
153             : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
154     scale.postTranslate(dx, dy);
155 }
156 
pathsDrawTheSame(SkBitmap & bits,const SkPath & scaledOne,const SkPath & scaledTwo,int & error2x2)157 static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
158         int& error2x2) {
159     if (bits.width() == 0) {
160         bits.allocN32Pixels(kBitWidth * 2, kBitHeight);
161     }
162     SkCanvas canvas(bits);
163     canvas.drawColor(SK_ColorWHITE);
164     SkPaint paint;
165     canvas.save();
166     const SkRect& bounds1 = scaledOne.getBounds();
167     canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
168     canvas.drawPath(scaledOne, paint);
169     canvas.restore();
170     canvas.save();
171     canvas.translate(-bounds1.fLeft + 1 + kBitWidth, -bounds1.fTop + 1);
172     canvas.drawPath(scaledTwo, paint);
173     canvas.restore();
174     int errors2 = 0;
175     int errors = 0;
176     for (int y = 0; y < kBitHeight - 1; ++y) {
177         uint32_t* addr1 = bits.getAddr32(0, y);
178         uint32_t* addr2 = bits.getAddr32(0, y + 1);
179         uint32_t* addr3 = bits.getAddr32(kBitWidth, y);
180         uint32_t* addr4 = bits.getAddr32(kBitWidth, y + 1);
181         for (int x = 0; x < kBitWidth - 1; ++x) {
182             // count 2x2 blocks
183             bool err = addr1[x] != addr3[x];
184             if (err) {
185                 errors2 += addr1[x + 1] != addr3[x + 1]
186                         && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
187                 errors++;
188             }
189         }
190     }
191     error2x2 = errors2;
192     return errors;
193 }
194 
pathsDrawTheSame(const SkPath & one,const SkPath & two,SkBitmap & bits,SkPath & scaledOne,SkPath & scaledTwo,int & error2x2)195 static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
196         SkPath& scaledTwo, int& error2x2) {
197     SkMatrix scale;
198     scaleMatrix(one, two, scale);
199     one.transform(scale, &scaledOne);
200     two.transform(scale, &scaledTwo);
201     return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
202 }
203 
drawAsciiPaths(const SkPath & one,const SkPath & two,bool drawPaths)204 bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
205     if (!drawPaths) {
206         return true;
207     }
208     const SkRect& bounds1 = one.getBounds();
209     const SkRect& bounds2 = two.getBounds();
210     SkRect larger = bounds1;
211     larger.join(bounds2);
212     SkBitmap bits;
213     char out[256];
214     int bitWidth = SkScalarCeilToInt(larger.width()) + 2;
215     if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
216         return false;
217     }
218     int bitHeight = SkScalarCeilToInt(larger.height()) + 2;
219     if (bitHeight >= (int) sizeof(out)) {
220         return false;
221     }
222     bits.allocN32Pixels(bitWidth * 2, bitHeight);
223     SkCanvas canvas(bits);
224     canvas.drawColor(SK_ColorWHITE);
225     SkPaint paint;
226     canvas.save();
227     canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
228     canvas.drawPath(one, paint);
229     canvas.restore();
230     canvas.save();
231     canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
232     canvas.drawPath(two, paint);
233     canvas.restore();
234     for (int y = 0; y < bitHeight; ++y) {
235         uint32_t* addr1 = bits.getAddr32(0, y);
236         int x;
237         char* outPtr = out;
238         for (x = 0; x < bitWidth; ++x) {
239             *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
240         }
241         *outPtr++ = '|';
242         for (x = bitWidth; x < bitWidth * 2; ++x) {
243             *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
244         }
245         *outPtr++ = '\0';
246         SkDebugf("%s\n", out);
247     }
248     return true;
249 }
250 
comparePaths(skiatest::Reporter * reporter,const char * filename,const SkPath & one,const SkPath & two,SkBitmap & bitmap)251 int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
252         const SkPath& two, SkBitmap& bitmap) {
253     int errors2x2;
254     SkPath scaledOne, scaledTwo;
255     (void) pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
256     if (errors2x2 == 0) {
257         return 0;
258     }
259     const int MAX_ERRORS = 9;
260     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
261 }
262 
263 static SkTDArray<SkPathOp> gTestOp;
264 
showPathOpPath(const char * testName,const SkPath & one,const SkPath & two,const SkPath & a,const SkPath & b,const SkPath & scaledOne,const SkPath & scaledTwo,const SkPathOp shapeOp,const SkMatrix & scale)265 static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
266         const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
267         const SkPathOp shapeOp, const SkMatrix& scale) {
268     SkASSERT((unsigned) shapeOp < std::size(opStrs));
269     if (!testName) {
270         testName = "xOp";
271     }
272     SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n",
273         testName, opSuffixes[shapeOp]);
274     *gTestOp.append() = shapeOp;
275     SkDebugf("    SkPath path, pathB;\n");
276     SkPathOpsDebug::ShowOnePath(a, "path", false);
277     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
278     SkDebugf("    testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
279     SkDebugf("}\n");
280     drawAsciiPaths(scaledOne, scaledTwo, true);
281 }
282 
comparePaths(skiatest::Reporter * reporter,const char * testName,const SkPath & one,const SkPath & scaledOne,const SkPath & two,const SkPath & scaledTwo,SkBitmap & bitmap,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const SkMatrix & scale,ExpectMatch expectMatch)283 static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one,
284         const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
285         const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale,
286         ExpectMatch expectMatch) {
287     static SkMutex& compareDebugOut3 = *(new SkMutex);
288     int errors2x2;
289     const int MAX_ERRORS = 8;
290     (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
291     if (ExpectMatch::kNo == expectMatch) {
292         if (errors2x2 < MAX_ERRORS) {
293             REPORTER_ASSERT(reporter, 0);
294         }
295         return 0;
296     }
297     if (errors2x2 == 0) {
298         return 0;
299     }
300     if (ExpectMatch::kYes == expectMatch && errors2x2 >= MAX_ERRORS) {
301         SkAutoMutexExclusive autoM(compareDebugOut3);
302         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
303         SkDebugf("\n/*");
304         REPORTER_ASSERT(reporter, 0);
305         SkDebugf(" */\n");
306     }
307     return errors2x2 >= MAX_ERRORS ? errors2x2 : 0;
308 }
309 
310 // Default values for when reporter->verbose() is false.
311 static int sTestNumber = 55;
312 static const char* sTestName = "pathOpTest";
313 
appendTestName(const char * nameSuffix,std::string & out)314 static void appendTestName(const char* nameSuffix, std::string& out) {
315     out += sTestName;
316     out += std_to_string(sTestNumber);
317     ++sTestNumber;
318     if (nameSuffix) {
319         out.append(nameSuffix);
320     }
321 }
322 
appendTest(const char * pathStr,const char * pathPrefix,const char * nameSuffix,const char * testFunction,bool twoPaths,std::string & out)323 static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
324                        const char* testFunction, bool twoPaths, std::string& out) {
325 #if 0
326     out.append("\n<div id=\"");
327     appendTestName(nameSuffix, out);
328     out.append("\">\n");
329     if (pathPrefix) {
330         out.append(pathPrefix);
331     }
332     out.append(pathStr);
333     out.append("</div>\n\n");
334 
335     out.append(marker);
336     out.append("    ");
337     appendTestName(nameSuffix, out);
338     out.append(",\n\n\n");
339 #endif
340     out.append("static void ");
341     appendTestName(nameSuffix, out);
342     out.append("(skiatest::Reporter* reporter) {\n    SkPath path");
343     if (twoPaths) {
344         out.append(", pathB");
345     }
346     out.append(";\n");
347     if (pathPrefix) {
348         out.append(pathPrefix);
349     }
350     out += pathStr;
351     out += "    ";
352     out += testFunction;
353 #if 0
354     out.append("static void (*firstTest)() = ");
355     appendTestName(nameSuffix, out);
356     out.append(";\n\n");
357 
358     out.append("static struct {\n");
359     out.append("    void (*fun)();\n");
360     out.append("    const char* str;\n");
361     out.append("} tests[] = {\n");
362     out.append("    TEST(");
363     appendTestName(nameSuffix, out);
364     out.append("),\n");
365 #endif
366 }
367 
markTestFlakyForPathKit()368 void markTestFlakyForPathKit() {
369     if (PathOpsDebug::gJson) {
370         SkASSERT(!PathOpsDebug::gMarkJsonFlaky);
371         PathOpsDebug::gMarkJsonFlaky = true;
372     }
373 }
374 
testSimplify(SkPath & path,bool useXor,SkPath & out,PathOpsThreadState & state,const char * pathStr)375 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
376                   const char* pathStr) {
377     static SkMutex& simplifyDebugOut = *(new SkMutex);
378     SkPathFillType fillType = useXor ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding;
379     path.setFillType(fillType);
380     state.fReporter->bumpTestCount();
381     if (!Simplify(path, &out)) {
382         SkDebugf("%s did not expect failure\n", __FUNCTION__);
383         REPORTER_ASSERT(state.fReporter, 0);
384         return false;
385     }
386     if (!state.fReporter->verbose()) {
387         return true;
388     }
389     int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap);
390     if (result) {
391         SkAutoMutexExclusive autoM(simplifyDebugOut);
392         std::string str;
393         const char* pathPrefix = nullptr;
394         const char* nameSuffix = nullptr;
395         if (fillType == SkPathFillType::kEvenOdd) {
396             pathPrefix = "    path.setFillType(SkPathFillType::kEvenOdd);\n";
397             nameSuffix = "x";
398         }
399         const char testFunction[] = "testSimplify(reporter, path);";
400         appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, str);
401         SkDebugf("%s", str.c_str());
402         REPORTER_ASSERT(state.fReporter, 0);
403     }
404     state.fReporter->bumpTestCount();
405     return result == 0;
406 }
407 
json_status(ExpectSuccess expectSuccess,ExpectMatch expectMatch,bool opSucceeded)408 static void json_status(ExpectSuccess expectSuccess, ExpectMatch expectMatch, bool opSucceeded) {
409     fprintf(PathOpsDebug::gOut, "  \"expectSuccess\": \"%s\",\n",
410             ExpectSuccess::kNo == expectSuccess ? "no" :
411             ExpectSuccess::kYes == expectSuccess ? "yes" : "flaky");
412     if (PathOpsDebug::gMarkJsonFlaky) {
413         expectMatch = ExpectMatch::kFlaky;
414         PathOpsDebug::gMarkJsonFlaky = false;
415     }
416     fprintf(PathOpsDebug::gOut, "  \"expectMatch\": \"%s\",\n",
417             ExpectMatch::kNo == expectMatch ? "no" :
418             ExpectMatch::kYes == expectMatch ? "yes" : "flaky");
419     fprintf(PathOpsDebug::gOut, "  \"succeeded\": %s,\n", opSucceeded ? "true" : "false");
420 }
421 
json_path_out(const SkPath & path,const char * pathName,const char * fillTypeName,bool lastField)422 static void json_path_out(const SkPath& path, const char* pathName, const char* fillTypeName,
423         bool lastField) {
424     char const * const gFillTypeStrs[] = {
425         "Winding",
426         "EvenOdd",
427         "InverseWinding",
428         "InverseEvenOdd",
429     };
430     if (PathOpsDebug::gOutputSVG) {
431         SkString svg = SkParsePath::ToSVGString(path);
432         fprintf(PathOpsDebug::gOut, "  \"%s\": \"%s\",\n", pathName, svg.c_str());
433     } else {
434                                  // MOVE, LINE, QUAD, CONIC, CUBIC, CLOSE
435         const int verbConst[] =  {     0,    1,    2,     3,     4,     5 };
436         const int pointIndex[] = {     0,    1,    1,     1,     1,     0 };
437         const int pointCount[] = {     1,    2,    3,     3,     4,     0 };
438         fprintf(PathOpsDebug::gOut, "  \"%s\": [", pathName);
439         bool first = true;
440         for (auto [verb, points, w] : SkPathPriv::Iterate(path)) {
441             if (first) {
442                 first = false;
443             } else {
444                 fprintf(PathOpsDebug::gOut, ",\n    ");
445             }
446             int verbIndex = (int) verb;
447             fprintf(PathOpsDebug::gOut, "[%d", verbConst[verbIndex]);
448             for (int i = pointIndex[verbIndex]; i < pointCount[verbIndex]; ++i) {
449                 fprintf(PathOpsDebug::gOut, ", \"0x%08x\", \"0x%08x\"",
450                         SkFloat2Bits(points[i].fX), SkFloat2Bits(points[i].fY));
451             }
452             if (SkPathVerb::kConic == verb) {
453                 fprintf(PathOpsDebug::gOut, ", \"0x%08x\"", SkFloat2Bits(*w));
454             }
455             fprintf(PathOpsDebug::gOut, "]");
456         }
457         fprintf(PathOpsDebug::gOut, "],\n");
458     }
459     fprintf(PathOpsDebug::gOut, "  \"fillType%s\": \"k%s_FillType\"%s", fillTypeName,
460             gFillTypeStrs[(int) path.getFillType()], lastField ? "\n}" : ",\n");
461 }
462 
check_for_duplicate_names(const char * testName)463 static bool check_for_duplicate_names(const char* testName) {
464     if (PathOpsDebug::gCheckForDuplicateNames) {
465         if (gUniqueNames.end() != std::find(gUniqueNames.begin(), gUniqueNames.end(),
466                 std::string(testName))) {
467             SkDebugf("%s", "");  // convenience for setting breakpoints
468         }
469         gUniqueNames.push_back(std::string(testName));
470         return true;
471     }
472     return false;
473 }
474 
inner_simplify(skiatest::Reporter * reporter,const SkPath & path,const char * filename,ExpectSuccess expectSuccess,SkipAssert skipAssert,ExpectMatch expectMatch)475 static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
476         ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) {
477     if (PathOpsDebug::gJson) {
478         if (check_for_duplicate_names(filename)) {
479             return true;
480         }
481         if (!PathOpsDebug::gOutFirst) {
482             fprintf(PathOpsDebug::gOut, ",\n");
483         }
484         PathOpsDebug::gOutFirst = false;
485         fprintf(PathOpsDebug::gOut, "\"%s\": {\n", filename);
486         json_path_out(path, "path", "", false);
487     }
488     SkPath out;
489     if (!SimplifyDebug(path, &out  SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
490             SkDEBUGPARAMS(sTestName))) {
491         if (ExpectSuccess::kYes == expectSuccess) {
492             SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
493             REPORTER_ASSERT(reporter, 0);
494         }
495         if (PathOpsDebug::gJson) {
496             json_status(expectSuccess, expectMatch, false);
497             fprintf(PathOpsDebug::gOut, "  \"out\": \"\"\n}");
498         }
499         return false;
500     } else {
501         if (ExpectSuccess::kNo == expectSuccess) {
502             SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename);
503             REPORTER_ASSERT(reporter, 0);
504         }
505         if (PathOpsDebug::gJson) {
506             json_status(expectSuccess, expectMatch, true);
507             json_path_out(out, "out", "Out", true);
508         }
509     }
510     SkBitmap bitmap;
511     int errors = comparePaths(reporter, filename, path, out, bitmap);
512     if (ExpectMatch::kNo == expectMatch) {
513         if (!errors) {
514             SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
515             REPORTER_ASSERT(reporter, 0);
516             return false;
517         }
518     } else if (ExpectMatch::kYes == expectMatch && errors) {
519         REPORTER_ASSERT(reporter, 0);
520     }
521     reporter->bumpTestCount();
522     return errors == 0;
523 }
524 
testSimplify(skiatest::Reporter * reporter,const SkPath & path,const char * filename)525 bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
526     return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo,
527             ExpectMatch::kYes);
528 }
529 
testSimplifyFuzz(skiatest::Reporter * reporter,const SkPath & path,const char * filename)530 bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
531     return inner_simplify(reporter, path, filename, ExpectSuccess::kFlaky, SkipAssert::kYes,
532             ExpectMatch::kFlaky);
533 }
534 
testSimplifyCheck(skiatest::Reporter * reporter,const SkPath & path,const char * filename,bool checkFail)535 bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
536         bool checkFail) {
537     return inner_simplify(reporter, path, filename, checkFail ?
538             ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
539 }
540 
testSimplifyFail(skiatest::Reporter * reporter,const SkPath & path,const char * filename)541 bool testSimplifyFail(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
542     return inner_simplify(reporter, path, filename,
543             ExpectSuccess::kNo, SkipAssert::kYes, ExpectMatch::kNo);
544 }
545 
innerPathOp(skiatest::Reporter * reporter,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const char * testName,ExpectSuccess expectSuccess,SkipAssert skipAssert,ExpectMatch expectMatch)546 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
547         const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess,
548         SkipAssert skipAssert, ExpectMatch expectMatch) {
549     if (PathOpsDebug::gJson) {
550         if (check_for_duplicate_names(testName)) {
551             return true;
552         }
553         if (!PathOpsDebug::gOutFirst) {
554             fprintf(PathOpsDebug::gOut, ",\n");
555         }
556         PathOpsDebug::gOutFirst = false;
557         fprintf(PathOpsDebug::gOut, "\"%s\": {\n", testName);
558         json_path_out(a, "p1", "1", false);
559         json_path_out(b, "p2", "2", false);
560         fprintf(PathOpsDebug::gOut, "  \"op\": \"%s\",\n", opStrs[shapeOp]);
561     }
562     SkPath out;
563     if (!OpDebug(a, b, shapeOp, &out  SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
564             SkDEBUGPARAMS(testName))) {
565         if (ExpectSuccess::kYes == expectSuccess) {
566             SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName);
567             REPORTER_ASSERT(reporter, 0);
568         }
569         if (PathOpsDebug::gJson) {
570             json_status(expectSuccess, expectMatch, false);
571             fprintf(PathOpsDebug::gOut, "  \"out\": \"\"\n}");
572         }
573         return false;
574     } else {
575         if (ExpectSuccess::kNo == expectSuccess) {
576                 SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName);
577                 REPORTER_ASSERT(reporter, 0);
578         }
579         if (PathOpsDebug::gJson) {
580             json_status(expectSuccess, expectMatch, true);
581             json_path_out(out, "out", "Out", true);
582         }
583     }
584     if (!reporter->verbose()) {
585         return true;
586     }
587     SkPath pathOut, scaledPathOut;
588     SkRegion rgnA, rgnB, openClip, rgnOut;
589     openClip.setRect({-16000, -16000, 16000, 16000});
590     rgnA.setPath(a, openClip);
591     rgnB.setPath(b, openClip);
592     rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
593     rgnOut.getBoundaryPath(&pathOut);
594 
595     SkMatrix scale;
596     scaleMatrix(a, b, scale);
597     SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
598     SkPath scaledA, scaledB;
599     scaledA.addPath(a, scale);
600     scaledA.setFillType(a.getFillType());
601     scaledB.addPath(b, scale);
602     scaledB.setFillType(b.getFillType());
603     scaledRgnA.setPath(scaledA, openClip);
604     scaledRgnB.setPath(scaledB, openClip);
605     scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
606     scaledRgnOut.getBoundaryPath(&scaledPathOut);
607     SkBitmap bitmap;
608     SkPath scaledOut;
609     scaledOut.addPath(out, scale);
610     scaledOut.setFillType(out.getFillType());
611     int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
612             a, b, shapeOp, scale, expectMatch);
613     reporter->bumpTestCount();
614     return result == 0;
615 }
616 
testPathOp(skiatest::Reporter * reporter,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const char * testName)617 bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
618         const SkPathOp shapeOp, const char* testName) {
619     return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo,
620             ExpectMatch::kYes);
621 }
622 
testPathOpCheck(skiatest::Reporter * reporter,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const char * testName,bool checkFail)623 bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
624         const SkPathOp shapeOp, const char* testName, bool checkFail) {
625     return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ?
626             ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
627 }
628 
testPathOpFuzz(skiatest::Reporter * reporter,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const char * testName)629 bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
630         const SkPathOp shapeOp, const char* testName) {
631     return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes,
632             ExpectMatch::kFlaky);
633 }
634 
testPathOpFail(skiatest::Reporter * reporter,const SkPath & a,const SkPath & b,const SkPathOp shapeOp,const char * testName)635 bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
636                  const SkPathOp shapeOp, const char* testName) {
637     SkPath orig;
638     orig.lineTo(54, 43);
639     SkPath out = orig;
640     if (Op(a, b, shapeOp, &out) ) {
641         SkDebugf("%s test is expected to fail\n", __FUNCTION__);
642         REPORTER_ASSERT(reporter, 0);
643         return false;
644     }
645     SkASSERT(out == orig);
646     return true;
647 }
648 
initializeTests(skiatest::Reporter * reporter,const char * test)649 void initializeTests(skiatest::Reporter* reporter, const char* test) {
650     static SkMutex& mu = *(new SkMutex);
651     if (reporter->verbose()) {
652         SkAutoMutexExclusive lock(mu);
653         sTestName = test;
654         size_t testNameSize = strlen(test);
655         SkFILEStream inFile("../../experimental/Intersection/op.htm");
656         if (inFile.isValid()) {
657             SkTDArray<char> inData;
658             inData.resize((int) inFile.getLength());
659             size_t inLen = inData.size();
660             inFile.read(inData.begin(), inLen);
661             inFile.close();
662             char* insert = strstr(inData.begin(), marker);
663             if (insert) {
664                 insert += sizeof(marker) - 1;
665                 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
666                 sTestNumber = atoi(numLoc) + 1;
667             }
668         }
669     }
670 }
671 
outputProgress(const char * pathStr,SkPathFillType pathFillType)672 void PathOpsThreadState::outputProgress(const char* pathStr, SkPathFillType pathFillType) {
673     const char testFunction[] = "testSimplify(path);";
674     const char* pathPrefix = nullptr;
675     const char* nameSuffix = nullptr;
676     if (pathFillType == SkPathFillType::kEvenOdd) {
677         pathPrefix = "    path.setFillType(SkPathFillType::kEvenOdd);\n";
678         nameSuffix = "x";
679     }
680     appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr);
681 }
682 
outputProgress(const char * pathStr,SkPathOp op)683 void PathOpsThreadState::outputProgress(const char* pathStr, SkPathOp op) {
684     const char testFunction[] = "testOp(path);";
685     SkASSERT((size_t) op < std::size(opSuffixes));
686     const char* nameSuffix = opSuffixes[op];
687     appendTest(pathStr, nullptr, nameSuffix, testFunction, true, fPathStr);
688 }
689 
RunTestSet(skiatest::Reporter * reporter,TestDesc tests[],size_t count,void (* firstTest)(skiatest::Reporter *,const char * filename),void (* skipTest)(skiatest::Reporter *,const char * filename),void (* stopTest)(skiatest::Reporter *,const char * filename),bool reverse)690 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
691                 void (*firstTest)(skiatest::Reporter* , const char* filename),
692                 void (*skipTest)(skiatest::Reporter* , const char* filename),
693                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
694     size_t index;
695     if (firstTest) {
696         index = count - 1;
697         while (index > 0 && tests[index].fun != firstTest) {
698             --index;
699         }
700         (*tests[index].fun)(reporter, tests[index].str);
701         if (tests[index].fun == stopTest) {
702             return;
703         }
704     }
705     index = reverse ? count - 1 : 0;
706     size_t last = reverse ? 0 : count - 1;
707     bool foundSkip = !skipTest;
708     do {
709         if (tests[index].fun == skipTest) {
710             foundSkip = true;
711         }
712         if (foundSkip && tests[index].fun != firstTest) {
713              (*tests[index].fun)(reporter, tests[index].str);
714         }
715         if (tests[index].fun == stopTest || index == last) {
716             break;
717         }
718         index += reverse ? -1 : 1;
719     } while (true);
720 }
721