blob: 5aa1ac098c2ab5a21ce37a4c1a4c8c7a5a7e0194 [file] [log] [blame]
caryclark@google.com818b0cc2013-04-08 11:50:46 +00001/*
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 "PathOpsExtendedTest.h"
caryclark@google.com66089e42013-04-10 15:55:37 +00009#include "PathOpsThreadedCommon.h"
caryclark@google.com818b0cc2013-04-08 11:50:46 +000010#include "SkBitmap.h"
11#include "SkCanvas.h"
12#include "SkMatrix.h"
13#include "SkPaint.h"
14#include "SkStream.h"
15
16#ifdef SK_BUILD_FOR_MAC
17#include <sys/sysctl.h>
18#endif
19
caryclark@google.com818b0cc2013-04-08 11:50:46 +000020static const char marker[] =
21 "</div>\n"
22 "\n"
23 "<script type=\"text/javascript\">\n"
24 "\n"
25 "var testDivs = [\n";
26
27static const char* opStrs[] = {
28 "kDifference_PathOp",
29 "kIntersect_PathOp",
30 "kUnion_PathOp",
31 "kXor_PathOp",
32};
33
34static const char* opSuffixes[] = {
35 "d",
36 "i",
37 "u",
caryclark@google.com66089e42013-04-10 15:55:37 +000038 "o",
caryclark@google.com818b0cc2013-04-08 11:50:46 +000039};
40
41static bool gShowPath = false;
42static bool gComparePaths = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000043static bool gComparePathsAssert = true;
44static bool gPathStrAssert = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000045
caryclark@google.com66089e42013-04-10 15:55:37 +000046#if FORCE_RELEASE
caryclark@google.com818b0cc2013-04-08 11:50:46 +000047static bool gRunTestsInOneThread = false;
48#else
49static bool gRunTestsInOneThread = true;
50#endif
51
52static void showPathContour(SkPath::Iter& iter) {
53 uint8_t verb;
54 SkPoint pts[4];
55 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
56 switch (verb) {
57 case SkPath::kMove_Verb:
58 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", pts[0].fX, pts[0].fY);
59 continue;
60 case SkPath::kLine_Verb:
61 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", pts[1].fX, pts[1].fY);
62 break;
63 case SkPath::kQuad_Verb:
64 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
65 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
66 break;
67 case SkPath::kCubic_Verb:
68 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
69 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
70 break;
71 case SkPath::kClose_Verb:
72 SkDebugf("path.close();\n");
73 break;
74 default:
75 SkDEBUGFAIL("bad verb");
76 return;
77 }
78 }
79}
80
81void showPath(const SkPath& path, const char* str) {
82 SkDebugf("%s\n", !str ? "original:" : str);
83 showPath(path);
84}
85
86void showPath(const SkPath& path) {
87 SkPath::Iter iter(path, true);
88#define SUPPORT_RECT_CONTOUR_DETECTION 0
89#if SUPPORT_RECT_CONTOUR_DETECTION
90 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
91 if (rectCount > 0) {
92 SkTDArray<SkRect> rects;
93 SkTDArray<SkPath::Direction> directions;
94 rects.setCount(rectCount);
95 directions.setCount(rectCount);
96 path.rectContours(rects.begin(), directions.begin());
97 for (int contour = 0; contour < rectCount; ++contour) {
98 const SkRect& rect = rects[contour];
99 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
100 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
101 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
102 }
103 return;
104 }
105#endif
106 iter.setPath(path, true);
107 showPathContour(iter);
108}
109
110void showPathData(const SkPath& path) {
111 SkPath::Iter iter(path, true);
112 uint8_t verb;
113 SkPoint pts[4];
114 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
115 switch (verb) {
116 case SkPath::kMove_Verb:
117 continue;
118 case SkPath::kLine_Verb:
caryclark@google.com66089e42013-04-10 15:55:37 +0000119 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
120 pts[1].fX, pts[1].fY);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000121 break;
122 case SkPath::kQuad_Verb:
123 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000124 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000125 break;
126 case SkPath::kCubic_Verb:
127 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000128 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
129 pts[3].fX, pts[3].fY);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000130 break;
131 case SkPath::kClose_Verb:
132 break;
133 default:
134 SkDEBUGFAIL("bad verb");
135 return;
136 }
137 }
138}
139
140void showOp(const SkPathOp op) {
141 switch (op) {
142 case kDifference_PathOp:
143 SkDebugf("op difference\n");
144 break;
145 case kIntersect_PathOp:
146 SkDebugf("op intersect\n");
147 break;
148 case kUnion_PathOp:
149 SkDebugf("op union\n");
150 break;
151 case kXOR_PathOp:
152 SkDebugf("op xor\n");
153 break;
154 default:
155 SkASSERT(0);
156 }
157}
158
159static void showPath(const SkPath& path, const char* str, const SkMatrix& scale) {
160 SkPath scaled;
161 SkMatrix inverse;
162 bool success = scale.invert(&inverse);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000163 if (!success) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000164 SkASSERT(0);
165 }
166 path.transform(inverse, &scaled);
167 showPath(scaled, str);
168}
169
170const int bitWidth = 64;
171const int bitHeight = 64;
172
173static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
174 SkRect larger = one.getBounds();
175 larger.join(two.getBounds());
176 SkScalar largerWidth = larger.width();
177 if (largerWidth < 4) {
178 largerWidth = 4;
179 }
180 SkScalar largerHeight = larger.height();
181 if (largerHeight < 4) {
182 largerHeight = 4;
183 }
184 SkScalar hScale = (bitWidth - 2) / largerWidth;
185 SkScalar vScale = (bitHeight - 2) / largerHeight;
186 scale.reset();
187 scale.preScale(hScale, vScale);
188}
189
190static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
191 int& error2x2) {
192 if (bits.width() == 0) {
193 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
194 bits.allocPixels();
195 }
196 SkCanvas canvas(bits);
197 canvas.drawColor(SK_ColorWHITE);
198 SkPaint paint;
199 canvas.save();
200 const SkRect& bounds1 = scaledOne.getBounds();
201 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
202 canvas.drawPath(scaledOne, paint);
203 canvas.restore();
204 canvas.save();
205 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
206 canvas.drawPath(scaledTwo, paint);
207 canvas.restore();
208 int errors2 = 0;
209 int errors = 0;
210 for (int y = 0; y < bitHeight - 1; ++y) {
211 uint32_t* addr1 = bits.getAddr32(0, y);
212 uint32_t* addr2 = bits.getAddr32(0, y + 1);
213 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
214 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
215 for (int x = 0; x < bitWidth - 1; ++x) {
216 // count 2x2 blocks
217 bool err = addr1[x] != addr3[x];
218 if (err) {
219 errors2 += addr1[x + 1] != addr3[x + 1]
220 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
221 errors++;
222 }
223 }
224 }
225 if (errors2 >= 6 || errors > 160) {
226 SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
227 }
228 error2x2 = errors2;
229 return errors;
230}
231
232static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
233 SkPath& scaledTwo, int& error2x2) {
234 SkMatrix scale;
235 scaleMatrix(one, two, scale);
236 one.transform(scale, &scaledOne);
237 two.transform(scale, &scaledTwo);
238 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
239}
240
241bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
242 if (!drawPaths) {
243 return true;
244 }
245 const SkRect& bounds1 = one.getBounds();
246 const SkRect& bounds2 = two.getBounds();
247 SkRect larger = bounds1;
248 larger.join(bounds2);
249 SkBitmap bits;
250 char out[256];
251 int bitWidth = SkScalarCeil(larger.width()) + 2;
252 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
253 return false;
254 }
255 int bitHeight = SkScalarCeil(larger.height()) + 2;
256 if (bitHeight >= (int) sizeof(out)) {
257 return false;
258 }
259 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
260 bits.allocPixels();
261 SkCanvas canvas(bits);
262 canvas.drawColor(SK_ColorWHITE);
263 SkPaint paint;
264 canvas.save();
265 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
266 canvas.drawPath(one, paint);
267 canvas.restore();
268 canvas.save();
269 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
270 canvas.drawPath(two, paint);
271 canvas.restore();
272 for (int y = 0; y < bitHeight; ++y) {
273 uint32_t* addr1 = bits.getAddr32(0, y);
274 int x;
275 char* outPtr = out;
276 for (x = 0; x < bitWidth; ++x) {
277 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
278 }
279 *outPtr++ = '|';
280 for (x = bitWidth; x < bitWidth * 2; ++x) {
281 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
282 }
283 *outPtr++ = '\0';
284 SkDebugf("%s\n", out);
285 }
286 return true;
287}
288
289static void showSimplifiedPath(const SkPath& one, const SkPath& two,
290 const SkPath& scaledOne, const SkPath& scaledTwo) {
291 showPath(one, "original:");
292 showPath(two, "simplified:");
293 drawAsciiPaths(scaledOne, scaledTwo, true);
294}
295
296static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& two,
297 SkBitmap& bitmap) {
298 int errors2x2;
299 SkPath scaledOne, scaledTwo;
300 int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
301 if (errors2x2 == 0) {
302 return 0;
303 }
304 const int MAX_ERRORS = 9;
305 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
306 showSimplifiedPath(one, two, scaledOne, scaledTwo);
307 }
308 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
309 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
310 showSimplifiedPath(one, two, scaledOne, scaledTwo);
311 REPORTER_ASSERT(reporter, 0);
312 }
313 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
314}
315
316static void showPathOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
317 const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp,
318 const SkMatrix& scale) {
319 SkASSERT((unsigned) shapeOp < sizeof(opStrs) / sizeof(opStrs[0]));
320 showPath(a, "minuend:");
321 SkDebugf("op: %s\n", opStrs[shapeOp]);
322 showPath(b, "subtrahend:");
323 // the region often isn't very helpful since it approximates curves with a lot of line-tos
324 if (0) showPath(scaledOne, "region:", scale);
325 showPath(two, "op result:");
326 drawAsciiPaths(scaledOne, scaledTwo, true);
327}
328
329static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& scaledOne,
330 const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000331 const SkPath& a, const SkPath& b, const SkPathOp shapeOp,
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000332 const SkMatrix& scale) {
333 int errors2x2;
334 int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
335 if (errors2x2 == 0) {
336 return 0;
337 }
338 const int MAX_ERRORS = 8;
339 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
340 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
341 }
342 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
343 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
344 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
345 REPORTER_ASSERT(reporter, 0);
346 }
347 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
348}
349
caryclark@google.com66089e42013-04-10 15:55:37 +0000350static int testNumber;
351static const char* testName;
352
353static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
354 outFile.writeText(testName);
355 outFile.writeDecAsText(testNumber);
356 if (nameSuffix) {
357 outFile.writeText(nameSuffix);
358 }
359}
360
361static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
362 const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
363 outFile.writeText("<div id=\"");
364 writeTestName(nameSuffix, outFile);
365 outFile.writeText("\">\n");
366 if (pathPrefix) {
367 outFile.writeText(pathPrefix);
368 }
369 outFile.writeText(pathStr);
370 outFile.writeText("</div>\n\n");
371
372 outFile.writeText(marker);
373 outFile.writeText(" ");
374 writeTestName(nameSuffix, outFile);
375 outFile.writeText(",\n\n\n");
376
377 outFile.writeText("static void ");
378 writeTestName(nameSuffix, outFile);
379 outFile.writeText("() {\n SkPath path");
380 if (twoPaths) {
381 outFile.writeText(", pathB");
382 }
383 outFile.writeText(";\n");
384 if (pathPrefix) {
385 outFile.writeText(pathPrefix);
386 }
387 outFile.writeText(pathStr);
388 outFile.writeText(" ");
389 outFile.writeText(testFunction);
390 outFile.writeText("\n}\n\n");
391 outFile.writeText("static void (*firstTest)() = ");
392 writeTestName(nameSuffix, outFile);
393 outFile.writeText(";\n\n");
394
395 outFile.writeText("static struct {\n");
396 outFile.writeText(" void (*fun)();\n");
397 outFile.writeText(" const char* str;\n");
398 outFile.writeText("} tests[] = {\n");
399 outFile.writeText(" TEST(");
400 writeTestName(nameSuffix, outFile);
401 outFile.writeText("),\n");
402 outFile.flush();
403}
404
405bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
406 const char* pathStr) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000407 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
408 path.setFillType(fillType);
409 if (gShowPath) {
410 showPath(path);
411 }
412 Simplify(path, &out);
413 if (!gComparePaths) {
414 return true;
415 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000416 int result = comparePaths(state.fReporter, path, out, *state.fBitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000417 if (result && gPathStrAssert) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000418 char temp[8192];
419 sk_bzero(temp, sizeof(temp));
420 SkMemoryWStream stream(temp, sizeof(temp));
421 const char* pathPrefix = NULL;
422 const char* nameSuffix = NULL;
423 if (fillType == SkPath::kEvenOdd_FillType) {
424 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
425 nameSuffix = "x";
426 }
427 const char testFunction[] = "testSimplifyx(path);";
caryclark@google.com66089e42013-04-10 15:55:37 +0000428 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000429 SkDebugf(temp);
caryclark@google.com66089e42013-04-10 15:55:37 +0000430 REPORTER_ASSERT(state.fReporter, 0);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000431 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000432 state.fReporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000433 return result == 0;
434}
435
436bool testSimplify(skiatest::Reporter* reporter, const SkPath& path) {
437 SkPath out;
438 Simplify(path, &out);
439 SkBitmap bitmap;
440 int result = comparePaths(reporter, path, out, bitmap);
441 if (result && gPathStrAssert) {
442 REPORTER_ASSERT(reporter, 0);
443 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000444 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000445 return result == 0;
446}
447
448bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
449 const SkPathOp shapeOp) {
450#if FORCE_RELEASE == 0
451 showPathData(a);
452 showOp(shapeOp);
453 showPathData(b);
454#endif
455 SkPath out;
456 Op(a, b, shapeOp, &out);
457 SkPath pathOut, scaledPathOut;
458 SkRegion rgnA, rgnB, openClip, rgnOut;
459 openClip.setRect(-16000, -16000, 16000, 16000);
460 rgnA.setPath(a, openClip);
461 rgnB.setPath(b, openClip);
462 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
463 rgnOut.getBoundaryPath(&pathOut);
464
465 SkMatrix scale;
466 scaleMatrix(a, b, scale);
467 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
468 SkPath scaledA, scaledB;
469 scaledA.addPath(a, scale);
470 scaledA.setFillType(a.getFillType());
471 scaledB.addPath(b, scale);
472 scaledB.setFillType(b.getFillType());
473 scaledRgnA.setPath(scaledA, openClip);
474 scaledRgnB.setPath(scaledB, openClip);
475 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
476 scaledRgnOut.getBoundaryPath(&scaledPathOut);
477 SkBitmap bitmap;
478 SkPath scaledOut;
479 scaledOut.addPath(out, scale);
480 scaledOut.setFillType(out.getFillType());
481 int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b,
482 shapeOp, scale);
483 if (result && gPathStrAssert) {
484 REPORTER_ASSERT(reporter, 0);
485 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000486 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000487 return result == 0;
488}
489
490const int maxThreadsAllocated = 64;
491static int maxThreads = 1;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000492
caryclark@google.com66089e42013-04-10 15:55:37 +0000493int initializeTests(const char* test) {
494#ifdef SK_DEBUG
495 gDebugMaxWindSum = 4;
496 gDebugMaxWindValue = 4;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000497#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000498 testName = test;
caryclark@google.com66089e42013-04-10 15:55:37 +0000499 size_t testNameSize = strlen(test);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000500 if (!gRunTestsInOneThread) {
501 int threads = -1;
502#ifdef SK_BUILD_FOR_MAC
503 size_t size = sizeof(threads);
504 sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
505#endif
506 if (threads > 0) {
507 maxThreads = threads;
508 } else {
caryclark@google.com66089e42013-04-10 15:55:37 +0000509 maxThreads = 16;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000510 }
511 }
512 SkFILEStream inFile("../../experimental/Intersection/op.htm");
513 if (inFile.isValid()) {
514 SkTDArray<char> inData;
515 inData.setCount(inFile.getLength());
516 size_t inLen = inData.count();
517 inFile.read(inData.begin(), inLen);
518 inFile.setPath(NULL);
519 char* insert = strstr(inData.begin(), marker);
520 if (insert) {
521 insert += sizeof(marker) - 1;
522 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
523 testNumber = atoi(numLoc) + 1;
524 }
525 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000526 return maxThreads;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000527}
528
caryclark@google.com66089e42013-04-10 15:55:37 +0000529void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
530 const char testFunction[] = "testSimplify(path);";
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000531 const char* pathPrefix = NULL;
532 const char* nameSuffix = NULL;
533 if (pathFillType == SkPath::kEvenOdd_FillType) {
534 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
535 nameSuffix = "x";
536 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000537 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
538 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000539}
540
caryclark@google.com66089e42013-04-10 15:55:37 +0000541void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
542 const char testFunction[] = "testOp(path);";
543 SkASSERT(op < sizeof(opSuffixes) / sizeof(opSuffixes[0]));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000544 const char* nameSuffix = opSuffixes[op];
caryclark@google.com66089e42013-04-10 15:55:37 +0000545 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
546 outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000547}
548
549void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
550 void (*firstTest)(skiatest::Reporter* ),
551 void (*stopTest)(skiatest::Reporter* ), bool reverse) {
552 size_t index;
553 if (firstTest) {
554 index = count - 1;
555 while (index > 0 && tests[index].fun != firstTest) {
556 --index;
557 }
558#if FORCE_RELEASE == 0
559 SkDebugf("<div id=\"%s\">\n", tests[index].str);
560 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
561#endif
562 (*tests[index].fun)(reporter);
563 }
564 index = reverse ? count - 1 : 0;
565 size_t last = reverse ? 0 : count - 1;
566 do {
567 if (tests[index].fun != firstTest) {
568 #if FORCE_RELEASE == 0
569 SkDebugf("<div id=\"%s\">\n", tests[index].str);
570 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
571 #endif
572 (*tests[index].fun)(reporter);
573 }
574 if (tests[index].fun == stopTest) {
575 SkDebugf("lastTest\n");
576 }
577 if (index == last) {
578 break;
579 }
580 index += reverse ? -1 : 1;
581 } while (true);
582}