blob: 675918ffdf6173d51b0d96b80a9e89a1bef163a3 [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.com818b0cc2013-04-08 11:50:46 +000046static void showPathContour(SkPath::Iter& iter) {
47 uint8_t verb;
48 SkPoint pts[4];
49 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
50 switch (verb) {
51 case SkPath::kMove_Verb:
52 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", pts[0].fX, pts[0].fY);
53 continue;
54 case SkPath::kLine_Verb:
55 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", pts[1].fX, pts[1].fY);
56 break;
57 case SkPath::kQuad_Verb:
58 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
59 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
60 break;
61 case SkPath::kCubic_Verb:
62 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
63 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
64 break;
65 case SkPath::kClose_Verb:
66 SkDebugf("path.close();\n");
67 break;
68 default:
69 SkDEBUGFAIL("bad verb");
70 return;
71 }
72 }
73}
74
75void showPath(const SkPath& path, const char* str) {
76 SkDebugf("%s\n", !str ? "original:" : str);
77 showPath(path);
78}
79
80void showPath(const SkPath& path) {
81 SkPath::Iter iter(path, true);
82#define SUPPORT_RECT_CONTOUR_DETECTION 0
83#if SUPPORT_RECT_CONTOUR_DETECTION
84 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
85 if (rectCount > 0) {
86 SkTDArray<SkRect> rects;
87 SkTDArray<SkPath::Direction> directions;
88 rects.setCount(rectCount);
89 directions.setCount(rectCount);
90 path.rectContours(rects.begin(), directions.begin());
91 for (int contour = 0; contour < rectCount; ++contour) {
92 const SkRect& rect = rects[contour];
93 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
94 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
95 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
96 }
97 return;
98 }
99#endif
100 iter.setPath(path, true);
101 showPathContour(iter);
102}
103
104void showPathData(const SkPath& path) {
105 SkPath::Iter iter(path, true);
106 uint8_t verb;
107 SkPoint pts[4];
108 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
109 switch (verb) {
110 case SkPath::kMove_Verb:
111 continue;
112 case SkPath::kLine_Verb:
caryclark@google.com66089e42013-04-10 15:55:37 +0000113 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
114 pts[1].fX, pts[1].fY);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000115 break;
116 case SkPath::kQuad_Verb:
117 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000118 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 +0000119 break;
120 case SkPath::kCubic_Verb:
121 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 +0000122 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
123 pts[3].fX, pts[3].fY);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000124 break;
125 case SkPath::kClose_Verb:
126 break;
127 default:
128 SkDEBUGFAIL("bad verb");
129 return;
130 }
131 }
132}
133
134void showOp(const SkPathOp op) {
135 switch (op) {
136 case kDifference_PathOp:
137 SkDebugf("op difference\n");
138 break;
139 case kIntersect_PathOp:
140 SkDebugf("op intersect\n");
141 break;
142 case kUnion_PathOp:
143 SkDebugf("op union\n");
144 break;
145 case kXOR_PathOp:
146 SkDebugf("op xor\n");
147 break;
148 default:
149 SkASSERT(0);
150 }
151}
152
153static void showPath(const SkPath& path, const char* str, const SkMatrix& scale) {
154 SkPath scaled;
155 SkMatrix inverse;
156 bool success = scale.invert(&inverse);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000157 if (!success) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000158 SkASSERT(0);
159 }
160 path.transform(inverse, &scaled);
161 showPath(scaled, str);
162}
163
caryclark@google.com03610322013-04-18 15:58:21 +0000164#if DEBUG_SHOW_TEST_NAME
165static char hexorator(int x) {
166 if (x < 10) {
167 return x + '0';
168 }
169 x -= 10;
170 SkASSERT(x < 26);
171 return x + 'A';
172}
173#endif
174
175void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) {
176#if DEBUG_SHOW_TEST_NAME
177 state->fSerialNo[0] = hexorator(state->fA);
178 state->fSerialNo[1] = hexorator(state->fB);
179 state->fSerialNo[2] = hexorator(state->fC);
180 state->fSerialNo[3] = hexorator(state->fD);
181 state->fSerialNo[4] = hexorator(a);
182 state->fSerialNo[5] = hexorator(b);
183 state->fSerialNo[6] = hexorator(c);
184 state->fSerialNo[7] = hexorator(d);
185 state->fSerialNo[8] = '\0';
186 SkDebugf("%s\n", state->fSerialNo);
187 if (strcmp(state->fSerialNo, state->fKey) == 0) {
188 SkDebugf("%s\n", state->fPathStr);
189 }
190#endif
191}
192
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000193const int bitWidth = 64;
194const int bitHeight = 64;
195
196static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
197 SkRect larger = one.getBounds();
198 larger.join(two.getBounds());
199 SkScalar largerWidth = larger.width();
200 if (largerWidth < 4) {
201 largerWidth = 4;
202 }
203 SkScalar largerHeight = larger.height();
204 if (largerHeight < 4) {
205 largerHeight = 4;
206 }
207 SkScalar hScale = (bitWidth - 2) / largerWidth;
208 SkScalar vScale = (bitHeight - 2) / largerHeight;
209 scale.reset();
210 scale.preScale(hScale, vScale);
211}
212
213static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
214 int& error2x2) {
215 if (bits.width() == 0) {
216 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
217 bits.allocPixels();
218 }
219 SkCanvas canvas(bits);
220 canvas.drawColor(SK_ColorWHITE);
221 SkPaint paint;
222 canvas.save();
223 const SkRect& bounds1 = scaledOne.getBounds();
224 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
225 canvas.drawPath(scaledOne, paint);
226 canvas.restore();
227 canvas.save();
228 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
229 canvas.drawPath(scaledTwo, paint);
230 canvas.restore();
231 int errors2 = 0;
232 int errors = 0;
233 for (int y = 0; y < bitHeight - 1; ++y) {
234 uint32_t* addr1 = bits.getAddr32(0, y);
235 uint32_t* addr2 = bits.getAddr32(0, y + 1);
236 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
237 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
238 for (int x = 0; x < bitWidth - 1; ++x) {
239 // count 2x2 blocks
240 bool err = addr1[x] != addr3[x];
241 if (err) {
242 errors2 += addr1[x + 1] != addr3[x + 1]
243 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
244 errors++;
245 }
246 }
247 }
248 if (errors2 >= 6 || errors > 160) {
249 SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
250 }
251 error2x2 = errors2;
252 return errors;
253}
254
255static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
256 SkPath& scaledTwo, int& error2x2) {
257 SkMatrix scale;
258 scaleMatrix(one, two, scale);
259 one.transform(scale, &scaledOne);
260 two.transform(scale, &scaledTwo);
261 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
262}
263
264bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
265 if (!drawPaths) {
266 return true;
267 }
268 const SkRect& bounds1 = one.getBounds();
269 const SkRect& bounds2 = two.getBounds();
270 SkRect larger = bounds1;
271 larger.join(bounds2);
272 SkBitmap bits;
273 char out[256];
274 int bitWidth = SkScalarCeil(larger.width()) + 2;
275 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
276 return false;
277 }
278 int bitHeight = SkScalarCeil(larger.height()) + 2;
279 if (bitHeight >= (int) sizeof(out)) {
280 return false;
281 }
282 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
283 bits.allocPixels();
284 SkCanvas canvas(bits);
285 canvas.drawColor(SK_ColorWHITE);
286 SkPaint paint;
287 canvas.save();
288 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
289 canvas.drawPath(one, paint);
290 canvas.restore();
291 canvas.save();
292 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
293 canvas.drawPath(two, paint);
294 canvas.restore();
295 for (int y = 0; y < bitHeight; ++y) {
296 uint32_t* addr1 = bits.getAddr32(0, y);
297 int x;
298 char* outPtr = out;
299 for (x = 0; x < bitWidth; ++x) {
300 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
301 }
302 *outPtr++ = '|';
303 for (x = bitWidth; x < bitWidth * 2; ++x) {
304 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
305 }
306 *outPtr++ = '\0';
307 SkDebugf("%s\n", out);
308 }
309 return true;
310}
311
312static void showSimplifiedPath(const SkPath& one, const SkPath& two,
313 const SkPath& scaledOne, const SkPath& scaledTwo) {
314 showPath(one, "original:");
315 showPath(two, "simplified:");
316 drawAsciiPaths(scaledOne, scaledTwo, true);
317}
318
319static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& two,
320 SkBitmap& bitmap) {
321 int errors2x2;
322 SkPath scaledOne, scaledTwo;
323 int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
324 if (errors2x2 == 0) {
325 return 0;
326 }
327 const int MAX_ERRORS = 9;
328 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
329 showSimplifiedPath(one, two, scaledOne, scaledTwo);
330 }
331 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
332 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
333 showSimplifiedPath(one, two, scaledOne, scaledTwo);
334 REPORTER_ASSERT(reporter, 0);
335 }
336 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
337}
338
339static void showPathOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
340 const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp,
341 const SkMatrix& scale) {
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000342 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000343 showPath(a, "minuend:");
344 SkDebugf("op: %s\n", opStrs[shapeOp]);
345 showPath(b, "subtrahend:");
346 // the region often isn't very helpful since it approximates curves with a lot of line-tos
347 if (0) showPath(scaledOne, "region:", scale);
348 showPath(two, "op result:");
349 drawAsciiPaths(scaledOne, scaledTwo, true);
350}
351
352static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& scaledOne,
353 const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000354 const SkPath& a, const SkPath& b, const SkPathOp shapeOp,
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000355 const SkMatrix& scale) {
356 int errors2x2;
357 int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
358 if (errors2x2 == 0) {
359 return 0;
360 }
361 const int MAX_ERRORS = 8;
362 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
363 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
364 }
365 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
366 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
367 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
368 REPORTER_ASSERT(reporter, 0);
369 }
370 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
371}
372
caryclark@google.com66089e42013-04-10 15:55:37 +0000373static int testNumber;
374static const char* testName;
375
376static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
377 outFile.writeText(testName);
378 outFile.writeDecAsText(testNumber);
379 if (nameSuffix) {
380 outFile.writeText(nameSuffix);
381 }
382}
383
384static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
385 const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
386 outFile.writeText("<div id=\"");
387 writeTestName(nameSuffix, outFile);
388 outFile.writeText("\">\n");
389 if (pathPrefix) {
390 outFile.writeText(pathPrefix);
391 }
392 outFile.writeText(pathStr);
393 outFile.writeText("</div>\n\n");
394
395 outFile.writeText(marker);
396 outFile.writeText(" ");
397 writeTestName(nameSuffix, outFile);
398 outFile.writeText(",\n\n\n");
399
400 outFile.writeText("static void ");
401 writeTestName(nameSuffix, outFile);
402 outFile.writeText("() {\n SkPath path");
403 if (twoPaths) {
404 outFile.writeText(", pathB");
405 }
406 outFile.writeText(";\n");
407 if (pathPrefix) {
408 outFile.writeText(pathPrefix);
409 }
410 outFile.writeText(pathStr);
411 outFile.writeText(" ");
412 outFile.writeText(testFunction);
413 outFile.writeText("\n}\n\n");
414 outFile.writeText("static void (*firstTest)() = ");
415 writeTestName(nameSuffix, outFile);
416 outFile.writeText(";\n\n");
417
418 outFile.writeText("static struct {\n");
419 outFile.writeText(" void (*fun)();\n");
420 outFile.writeText(" const char* str;\n");
421 outFile.writeText("} tests[] = {\n");
422 outFile.writeText(" TEST(");
423 writeTestName(nameSuffix, outFile);
424 outFile.writeText("),\n");
425 outFile.flush();
426}
427
428bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
429 const char* pathStr) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000430 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
431 path.setFillType(fillType);
432 if (gShowPath) {
433 showPath(path);
434 }
435 Simplify(path, &out);
436 if (!gComparePaths) {
437 return true;
438 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000439 int result = comparePaths(state.fReporter, path, out, *state.fBitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000440 if (result && gPathStrAssert) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000441 char temp[8192];
442 sk_bzero(temp, sizeof(temp));
443 SkMemoryWStream stream(temp, sizeof(temp));
444 const char* pathPrefix = NULL;
445 const char* nameSuffix = NULL;
446 if (fillType == SkPath::kEvenOdd_FillType) {
447 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
448 nameSuffix = "x";
449 }
450 const char testFunction[] = "testSimplifyx(path);";
caryclark@google.com66089e42013-04-10 15:55:37 +0000451 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000452 SkDebugf(temp);
caryclark@google.com66089e42013-04-10 15:55:37 +0000453 REPORTER_ASSERT(state.fReporter, 0);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000454 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000455 state.fReporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000456 return result == 0;
457}
458
459bool testSimplify(skiatest::Reporter* reporter, const SkPath& path) {
caryclark@google.com03610322013-04-18 15:58:21 +0000460#if FORCE_RELEASE == 0
461 showPathData(path);
462#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000463 SkPath out;
464 Simplify(path, &out);
465 SkBitmap bitmap;
466 int result = comparePaths(reporter, path, out, bitmap);
467 if (result && gPathStrAssert) {
468 REPORTER_ASSERT(reporter, 0);
469 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000470 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000471 return result == 0;
472}
473
474bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
475 const SkPathOp shapeOp) {
476#if FORCE_RELEASE == 0
477 showPathData(a);
478 showOp(shapeOp);
479 showPathData(b);
480#endif
481 SkPath out;
482 Op(a, b, shapeOp, &out);
483 SkPath pathOut, scaledPathOut;
484 SkRegion rgnA, rgnB, openClip, rgnOut;
485 openClip.setRect(-16000, -16000, 16000, 16000);
486 rgnA.setPath(a, openClip);
487 rgnB.setPath(b, openClip);
488 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
489 rgnOut.getBoundaryPath(&pathOut);
490
491 SkMatrix scale;
492 scaleMatrix(a, b, scale);
493 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
494 SkPath scaledA, scaledB;
495 scaledA.addPath(a, scale);
496 scaledA.setFillType(a.getFillType());
497 scaledB.addPath(b, scale);
498 scaledB.setFillType(b.getFillType());
499 scaledRgnA.setPath(scaledA, openClip);
500 scaledRgnB.setPath(scaledB, openClip);
501 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
502 scaledRgnOut.getBoundaryPath(&scaledPathOut);
503 SkBitmap bitmap;
504 SkPath scaledOut;
505 scaledOut.addPath(out, scale);
506 scaledOut.setFillType(out.getFillType());
507 int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b,
508 shapeOp, scale);
509 if (result && gPathStrAssert) {
510 REPORTER_ASSERT(reporter, 0);
511 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000512 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000513 return result == 0;
514}
515
516const int maxThreadsAllocated = 64;
517static int maxThreads = 1;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000518
caryclark@google.com16cfe402013-04-18 18:47:37 +0000519int initializeTests(skiatest::Reporter* reporter, const char* test) {
caryclark@google.com66089e42013-04-10 15:55:37 +0000520#ifdef SK_DEBUG
521 gDebugMaxWindSum = 4;
522 gDebugMaxWindValue = 4;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000523#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000524 testName = test;
caryclark@google.com66089e42013-04-10 15:55:37 +0000525 size_t testNameSize = strlen(test);
caryclark@google.com16cfe402013-04-18 18:47:37 +0000526 if (reporter->allowThreaded()) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000527 int threads = -1;
528#ifdef SK_BUILD_FOR_MAC
529 size_t size = sizeof(threads);
530 sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
531#endif
532 if (threads > 0) {
533 maxThreads = threads;
534 } else {
caryclark@google.com66089e42013-04-10 15:55:37 +0000535 maxThreads = 16;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000536 }
537 }
538 SkFILEStream inFile("../../experimental/Intersection/op.htm");
539 if (inFile.isValid()) {
540 SkTDArray<char> inData;
541 inData.setCount(inFile.getLength());
542 size_t inLen = inData.count();
543 inFile.read(inData.begin(), inLen);
544 inFile.setPath(NULL);
545 char* insert = strstr(inData.begin(), marker);
546 if (insert) {
547 insert += sizeof(marker) - 1;
548 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
549 testNumber = atoi(numLoc) + 1;
550 }
551 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000552 return maxThreads;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000553}
554
caryclark@google.com66089e42013-04-10 15:55:37 +0000555void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
556 const char testFunction[] = "testSimplify(path);";
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000557 const char* pathPrefix = NULL;
558 const char* nameSuffix = NULL;
559 if (pathFillType == SkPath::kEvenOdd_FillType) {
560 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
561 nameSuffix = "x";
562 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000563 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
564 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000565}
566
caryclark@google.com66089e42013-04-10 15:55:37 +0000567void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
568 const char testFunction[] = "testOp(path);";
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000569 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000570 const char* nameSuffix = opSuffixes[op];
caryclark@google.com66089e42013-04-10 15:55:37 +0000571 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
572 outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000573}
574
575void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
576 void (*firstTest)(skiatest::Reporter* ),
577 void (*stopTest)(skiatest::Reporter* ), bool reverse) {
578 size_t index;
579 if (firstTest) {
580 index = count - 1;
581 while (index > 0 && tests[index].fun != firstTest) {
582 --index;
583 }
584#if FORCE_RELEASE == 0
585 SkDebugf("<div id=\"%s\">\n", tests[index].str);
586 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
587#endif
588 (*tests[index].fun)(reporter);
589 }
590 index = reverse ? count - 1 : 0;
591 size_t last = reverse ? 0 : count - 1;
592 do {
593 if (tests[index].fun != firstTest) {
594 #if FORCE_RELEASE == 0
595 SkDebugf("<div id=\"%s\">\n", tests[index].str);
596 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
597 #endif
598 (*tests[index].fun)(reporter);
599 }
600 if (tests[index].fun == stopTest) {
601 SkDebugf("lastTest\n");
602 }
603 if (index == last) {
604 break;
605 }
606 index += reverse ? -1 : 1;
607 } while (true);
608}