blob: b85644dca5a854b55e7c55d3f7f315405fdb70df [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"
caryclark@google.coma5e55922013-05-07 18:51:31 +000015#include "SkThreadPool.h"
caryclark@google.com818b0cc2013-04-08 11:50:46 +000016
17#ifdef SK_BUILD_FOR_MAC
18#include <sys/sysctl.h>
19#endif
20
caryclark@google.com818b0cc2013-04-08 11:50:46 +000021static const char marker[] =
22 "</div>\n"
23 "\n"
24 "<script type=\"text/javascript\">\n"
25 "\n"
26 "var testDivs = [\n";
27
28static const char* opStrs[] = {
29 "kDifference_PathOp",
30 "kIntersect_PathOp",
31 "kUnion_PathOp",
32 "kXor_PathOp",
caryclark@google.com6dc7df62013-04-25 11:51:54 +000033 "kReverseDifference_PathOp",
caryclark@google.com818b0cc2013-04-08 11:50:46 +000034};
35
36static const char* opSuffixes[] = {
37 "d",
38 "i",
39 "u",
caryclark@google.com66089e42013-04-10 15:55:37 +000040 "o",
caryclark@google.com818b0cc2013-04-08 11:50:46 +000041};
42
43static bool gShowPath = false;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000044static bool gComparePathsAssert = true;
45static bool gPathStrAssert = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000046
caryclark@google.com07e97fc2013-07-08 17:17:02 +000047static const char* gFillTypeStr[] = {
48 "kWinding_FillType",
49 "kEvenOdd_FillType",
50 "kInverseWinding_FillType",
51 "kInverseEvenOdd_FillType"
52};
53
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000054static void output_scalar(SkScalar num) {
55 if (num == (int) num) {
56 SkDebugf("%d", (int) num);
57 } else {
58 SkString str;
59 str.printf("%1.9g", num);
60 int width = str.size();
61 const char* cStr = str.c_str();
62 while (cStr[width - 1] == '0') {
63 --width;
64 }
65 str.resize(width);
66 SkDebugf("%sf", str.c_str());
67 }
68}
69
70static void output_points(const SkPoint* pts, int count) {
71 for (int index = 0; index < count; ++index) {
72 output_scalar(pts[index].fX);
73 SkDebugf(", ");
74 output_scalar(pts[index].fY);
75 if (index + 1 < count) {
76 SkDebugf(", ");
77 }
78 }
79 SkDebugf(");\n");
80}
81
caryclark@google.com07e97fc2013-07-08 17:17:02 +000082static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +000083 uint8_t verb;
84 SkPoint pts[4];
85 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
86 switch (verb) {
87 case SkPath::kMove_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000088 SkDebugf(" %s.moveTo(", pathName);
89 output_points(&pts[0], 1);
caryclark@google.com818b0cc2013-04-08 11:50:46 +000090 continue;
91 case SkPath::kLine_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000092 SkDebugf(" %s.lineTo(", pathName);
93 output_points(&pts[1], 1);
caryclark@google.com818b0cc2013-04-08 11:50:46 +000094 break;
95 case SkPath::kQuad_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000096 SkDebugf(" %s.quadTo(", pathName);
97 output_points(&pts[1], 2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +000098 break;
99 case SkPath::kCubic_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000100 SkDebugf(" %s.cubicTo(", pathName);
101 output_points(&pts[1], 3);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000102 break;
103 case SkPath::kClose_Verb:
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000104 SkDebugf(" %s.close();\n", pathName);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000105 break;
106 default:
107 SkDEBUGFAIL("bad verb");
108 return;
109 }
110 }
111}
112
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000113static void showPath(const SkPath& path, const char* pathName, bool includeDeclaration) {
114 SkPath::RawIter iter(path);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000115#define SUPPORT_RECT_CONTOUR_DETECTION 0
116#if SUPPORT_RECT_CONTOUR_DETECTION
117 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
118 if (rectCount > 0) {
119 SkTDArray<SkRect> rects;
120 SkTDArray<SkPath::Direction> directions;
121 rects.setCount(rectCount);
122 directions.setCount(rectCount);
123 path.rectContours(rects.begin(), directions.begin());
124 for (int contour = 0; contour < rectCount; ++contour) {
125 const SkRect& rect = rects[contour];
126 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
127 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
128 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
129 }
130 return;
131 }
132#endif
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000133 SkPath::FillType fillType = path.getFillType();
134 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000135 if (includeDeclaration) {
136 SkDebugf(" SkPath %s;\n", pathName);
137 }
138 SkDebugf(" %s.setFillType(SkPath::%s);\n", pathName, gFillTypeStr[fillType]);
139 iter.setPath(path);
140 showPathContours(iter, pathName);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000141}
142
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000143#if DEBUG_SHOW_TEST_NAME
144static void showPathData(const SkPath& path) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000145 SkPath::RawIter iter(path);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000146 uint8_t verb;
147 SkPoint pts[4];
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000148 SkPoint firstPt, lastPt;
149 bool firstPtSet = false;
150 bool lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000151 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
152 switch (verb) {
153 case SkPath::kMove_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000154 firstPt = pts[0];
155 firstPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000156 continue;
157 case SkPath::kLine_Verb:
caryclark@google.com66089e42013-04-10 15:55:37 +0000158 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
159 pts[1].fX, pts[1].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000160 lastPt = pts[1];
161 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000162 break;
163 case SkPath::kQuad_Verb:
164 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000165 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000166 lastPt = pts[2];
167 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000168 break;
169 case SkPath::kCubic_Verb:
170 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 +0000171 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
172 pts[3].fX, pts[3].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000173 lastPt = pts[3];
174 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000175 break;
176 case SkPath::kClose_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000177 if (firstPtSet && lastPtSet && firstPt != lastPt) {
178 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
179 firstPt.fX, firstPt.fY);
180 }
181 firstPtSet = lastPtSet = false;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000182 break;
183 default:
184 SkDEBUGFAIL("bad verb");
185 return;
186 }
187 }
188}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000189#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000190
191void showOp(const SkPathOp op) {
192 switch (op) {
193 case kDifference_PathOp:
194 SkDebugf("op difference\n");
195 break;
196 case kIntersect_PathOp:
197 SkDebugf("op intersect\n");
198 break;
199 case kUnion_PathOp:
200 SkDebugf("op union\n");
201 break;
202 case kXOR_PathOp:
203 SkDebugf("op xor\n");
204 break;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000205 case kReverseDifference_PathOp:
206 SkDebugf("op reverse difference\n");
207 break;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000208 default:
209 SkASSERT(0);
210 }
211}
212
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000213#if DEBUG_SHOW_TEST_NAME
214
215void ShowFunctionHeader(const char* functionName) {
216 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter) {\n", functionName);
217 if (strcmp("skphealth_com76", functionName) == 0) {
218 SkDebugf("found it\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000219 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000220}
221
222static const char* gOpStrs[] = {
223 "kDifference_PathOp",
224 "kIntersect_PathOp",
225 "kUnion_PathOp",
226 "kXor_PathOp",
227 "kReverseDifference_PathOp",
228};
229
230void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) {
231 SkDebugf(" testPathOp(reporter, %s, %s, %s);\n", pathOne, pathTwo, gOpStrs[op]);
232 SkDebugf("}\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000233}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000234#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000235
caryclark@google.com03610322013-04-18 15:58:21 +0000236#if DEBUG_SHOW_TEST_NAME
237static char hexorator(int x) {
238 if (x < 10) {
239 return x + '0';
240 }
241 x -= 10;
242 SkASSERT(x < 26);
243 return x + 'A';
244}
245#endif
246
247void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) {
248#if DEBUG_SHOW_TEST_NAME
249 state->fSerialNo[0] = hexorator(state->fA);
250 state->fSerialNo[1] = hexorator(state->fB);
251 state->fSerialNo[2] = hexorator(state->fC);
252 state->fSerialNo[3] = hexorator(state->fD);
253 state->fSerialNo[4] = hexorator(a);
254 state->fSerialNo[5] = hexorator(b);
255 state->fSerialNo[6] = hexorator(c);
256 state->fSerialNo[7] = hexorator(d);
257 state->fSerialNo[8] = '\0';
258 SkDebugf("%s\n", state->fSerialNo);
259 if (strcmp(state->fSerialNo, state->fKey) == 0) {
260 SkDebugf("%s\n", state->fPathStr);
261 }
262#endif
263}
264
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000265const int bitWidth = 64;
266const int bitHeight = 64;
267
268static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
269 SkRect larger = one.getBounds();
270 larger.join(two.getBounds());
271 SkScalar largerWidth = larger.width();
272 if (largerWidth < 4) {
273 largerWidth = 4;
274 }
275 SkScalar largerHeight = larger.height();
276 if (largerHeight < 4) {
277 largerHeight = 4;
278 }
279 SkScalar hScale = (bitWidth - 2) / largerWidth;
280 SkScalar vScale = (bitHeight - 2) / largerHeight;
281 scale.reset();
282 scale.preScale(hScale, vScale);
283}
284
285static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
286 int& error2x2) {
287 if (bits.width() == 0) {
288 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
289 bits.allocPixels();
290 }
291 SkCanvas canvas(bits);
292 canvas.drawColor(SK_ColorWHITE);
293 SkPaint paint;
294 canvas.save();
295 const SkRect& bounds1 = scaledOne.getBounds();
296 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
297 canvas.drawPath(scaledOne, paint);
298 canvas.restore();
299 canvas.save();
300 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
301 canvas.drawPath(scaledTwo, paint);
302 canvas.restore();
303 int errors2 = 0;
304 int errors = 0;
305 for (int y = 0; y < bitHeight - 1; ++y) {
306 uint32_t* addr1 = bits.getAddr32(0, y);
307 uint32_t* addr2 = bits.getAddr32(0, y + 1);
308 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
309 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
310 for (int x = 0; x < bitWidth - 1; ++x) {
311 // count 2x2 blocks
312 bool err = addr1[x] != addr3[x];
313 if (err) {
314 errors2 += addr1[x + 1] != addr3[x + 1]
315 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
316 errors++;
317 }
318 }
319 }
320 if (errors2 >= 6 || errors > 160) {
321 SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
322 }
323 error2x2 = errors2;
324 return errors;
325}
326
327static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
328 SkPath& scaledTwo, int& error2x2) {
329 SkMatrix scale;
330 scaleMatrix(one, two, scale);
331 one.transform(scale, &scaledOne);
332 two.transform(scale, &scaledTwo);
333 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
334}
335
336bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
337 if (!drawPaths) {
338 return true;
339 }
340 const SkRect& bounds1 = one.getBounds();
341 const SkRect& bounds2 = two.getBounds();
342 SkRect larger = bounds1;
343 larger.join(bounds2);
344 SkBitmap bits;
345 char out[256];
346 int bitWidth = SkScalarCeil(larger.width()) + 2;
347 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
348 return false;
349 }
350 int bitHeight = SkScalarCeil(larger.height()) + 2;
351 if (bitHeight >= (int) sizeof(out)) {
352 return false;
353 }
354 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
355 bits.allocPixels();
356 SkCanvas canvas(bits);
357 canvas.drawColor(SK_ColorWHITE);
358 SkPaint paint;
359 canvas.save();
360 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
361 canvas.drawPath(one, paint);
362 canvas.restore();
363 canvas.save();
364 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
365 canvas.drawPath(two, paint);
366 canvas.restore();
367 for (int y = 0; y < bitHeight; ++y) {
368 uint32_t* addr1 = bits.getAddr32(0, y);
369 int x;
370 char* outPtr = out;
371 for (x = 0; x < bitWidth; ++x) {
372 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
373 }
374 *outPtr++ = '|';
375 for (x = bitWidth; x < bitWidth * 2; ++x) {
376 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
377 }
378 *outPtr++ = '\0';
379 SkDebugf("%s\n", out);
380 }
381 return true;
382}
383
384static void showSimplifiedPath(const SkPath& one, const SkPath& two,
385 const SkPath& scaledOne, const SkPath& scaledTwo) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000386 showPath(one, "path", false);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000387 drawAsciiPaths(scaledOne, scaledTwo, true);
388}
389
390static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& two,
391 SkBitmap& bitmap) {
392 int errors2x2;
393 SkPath scaledOne, scaledTwo;
394 int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
395 if (errors2x2 == 0) {
396 return 0;
397 }
398 const int MAX_ERRORS = 9;
399 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
400 showSimplifiedPath(one, two, scaledOne, scaledTwo);
401 }
402 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
403 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
404 showSimplifiedPath(one, two, scaledOne, scaledTwo);
405 REPORTER_ASSERT(reporter, 0);
406 }
407 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
408}
409
410static void showPathOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
411 const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp,
412 const SkMatrix& scale) {
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000413 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000414 SkDebugf("static void xOp#%s(skiatest::Reporter* reporter) {\n", opSuffixes[shapeOp]);
415 SkDebugf(" SkPath path, pathB;\n");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000416 showPath(a, "path", false);
417 showPath(b, "pathB", false);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000418 SkDebugf(" testPathOp(reporter, path, pathB, %s);\n", opStrs[shapeOp]);
419 SkDebugf("}\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000420 drawAsciiPaths(scaledOne, scaledTwo, true);
421}
422
423static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& scaledOne,
424 const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000425 const SkPath& a, const SkPath& b, const SkPathOp shapeOp,
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000426 const SkMatrix& scale) {
427 int errors2x2;
428 int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
429 if (errors2x2 == 0) {
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000430 if (gShowPath) {
431 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
432 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000433 return 0;
434 }
435 const int MAX_ERRORS = 8;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000436 if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000437 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
438 }
439 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
440 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
441 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
442 REPORTER_ASSERT(reporter, 0);
443 }
444 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
445}
446
caryclark@google.com66089e42013-04-10 15:55:37 +0000447static int testNumber;
448static const char* testName;
449
450static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
451 outFile.writeText(testName);
452 outFile.writeDecAsText(testNumber);
453 if (nameSuffix) {
454 outFile.writeText(nameSuffix);
455 }
456}
457
458static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
459 const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
460 outFile.writeText("<div id=\"");
461 writeTestName(nameSuffix, outFile);
462 outFile.writeText("\">\n");
463 if (pathPrefix) {
464 outFile.writeText(pathPrefix);
465 }
466 outFile.writeText(pathStr);
467 outFile.writeText("</div>\n\n");
468
469 outFile.writeText(marker);
470 outFile.writeText(" ");
471 writeTestName(nameSuffix, outFile);
472 outFile.writeText(",\n\n\n");
473
474 outFile.writeText("static void ");
475 writeTestName(nameSuffix, outFile);
476 outFile.writeText("() {\n SkPath path");
477 if (twoPaths) {
478 outFile.writeText(", pathB");
479 }
480 outFile.writeText(";\n");
481 if (pathPrefix) {
482 outFile.writeText(pathPrefix);
483 }
484 outFile.writeText(pathStr);
485 outFile.writeText(" ");
486 outFile.writeText(testFunction);
487 outFile.writeText("\n}\n\n");
488 outFile.writeText("static void (*firstTest)() = ");
489 writeTestName(nameSuffix, outFile);
490 outFile.writeText(";\n\n");
491
492 outFile.writeText("static struct {\n");
493 outFile.writeText(" void (*fun)();\n");
494 outFile.writeText(" const char* str;\n");
495 outFile.writeText("} tests[] = {\n");
496 outFile.writeText(" TEST(");
497 writeTestName(nameSuffix, outFile);
498 outFile.writeText("),\n");
499 outFile.flush();
500}
501
502bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
503 const char* pathStr) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000504 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
505 path.setFillType(fillType);
506 if (gShowPath) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000507 showPath(path, "path", false);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000508 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000509 if (!Simplify(path, &out)) {
510 SkDebugf("%s did not expect failure\n", __FUNCTION__);
511 REPORTER_ASSERT(state.fReporter, 0);
512 return false;
513 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000514 if (!state.fReporter->verbose()) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000515 return true;
516 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000517 int result = comparePaths(state.fReporter, path, out, *state.fBitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000518 if (result && gPathStrAssert) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000519 char temp[8192];
520 sk_bzero(temp, sizeof(temp));
521 SkMemoryWStream stream(temp, sizeof(temp));
522 const char* pathPrefix = NULL;
523 const char* nameSuffix = NULL;
524 if (fillType == SkPath::kEvenOdd_FillType) {
525 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
526 nameSuffix = "x";
527 }
528 const char testFunction[] = "testSimplifyx(path);";
caryclark@google.com66089e42013-04-10 15:55:37 +0000529 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000530 SkDebugf(temp);
caryclark@google.com66089e42013-04-10 15:55:37 +0000531 REPORTER_ASSERT(state.fReporter, 0);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000532 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000533 state.fReporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000534 return result == 0;
535}
536
537bool testSimplify(skiatest::Reporter* reporter, const SkPath& path) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000538#if DEBUG_SHOW_TEST_NAME
caryclark@google.com03610322013-04-18 15:58:21 +0000539 showPathData(path);
540#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000541 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000542 if (!Simplify(path, &out)) {
543 SkDebugf("%s did not expect failure\n", __FUNCTION__);
544 REPORTER_ASSERT(reporter, 0);
545 return false;
546 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000547 SkBitmap bitmap;
548 int result = comparePaths(reporter, path, out, bitmap);
549 if (result && gPathStrAssert) {
550 REPORTER_ASSERT(reporter, 0);
551 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000552 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000553 return result == 0;
554}
555
caryclark@google.coma5e55922013-05-07 18:51:31 +0000556#if DEBUG_SHOW_TEST_NAME
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000557void DebugShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp, const char* testName) {
558 ShowFunctionHeader(testName);
559 showPath(a, "path", true);
560 showPath(b, "pathB", true);
561 ShowOp(shapeOp, "path", "pathB");
562}
563#endif
564
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000565static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
566 const SkPathOp shapeOp, const char* testName, bool threaded) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000567#if DEBUG_SHOW_TEST_NAME
568 if (testName == NULL) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000569 SkDebugf("\n");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000570 showPathData(a);
571 showOp(shapeOp);
572 showPathData(b);
573 } else {
574 DebugShowPath(a, b, shapeOp, testName);
575 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000576#endif
577 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000578 if (!Op(a, b, shapeOp, &out) ) {
579 SkDebugf("%s did not expect failure\n", __FUNCTION__);
580 REPORTER_ASSERT(reporter, 0);
581 return false;
582 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000583 if (threaded && !reporter->verbose()) {
584 return true;
585 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000586 SkPath pathOut, scaledPathOut;
587 SkRegion rgnA, rgnB, openClip, rgnOut;
588 openClip.setRect(-16000, -16000, 16000, 16000);
589 rgnA.setPath(a, openClip);
590 rgnB.setPath(b, openClip);
591 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
592 rgnOut.getBoundaryPath(&pathOut);
593
594 SkMatrix scale;
595 scaleMatrix(a, b, scale);
596 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
597 SkPath scaledA, scaledB;
598 scaledA.addPath(a, scale);
599 scaledA.setFillType(a.getFillType());
600 scaledB.addPath(b, scale);
601 scaledB.setFillType(b.getFillType());
602 scaledRgnA.setPath(scaledA, openClip);
603 scaledRgnB.setPath(scaledB, openClip);
604 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
605 scaledRgnOut.getBoundaryPath(&scaledPathOut);
606 SkBitmap bitmap;
607 SkPath scaledOut;
608 scaledOut.addPath(out, scale);
609 scaledOut.setFillType(out.getFillType());
610 int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b,
611 shapeOp, scale);
612 if (result && gPathStrAssert) {
613 REPORTER_ASSERT(reporter, 0);
614 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000615 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000616 return result == 0;
617}
618
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000619bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
620 const SkPathOp shapeOp, const char* testName) {
621 return innerPathOp(reporter, a, b, shapeOp, testName, false);
622}
623
624bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
625 const SkPathOp shapeOp, const char* testName) {
626 return innerPathOp(reporter, a, b, shapeOp, testName, true);
627}
628
caryclark@google.com16cfe402013-04-18 18:47:37 +0000629int initializeTests(skiatest::Reporter* reporter, const char* test) {
caryclark@google.com66089e42013-04-10 15:55:37 +0000630#ifdef SK_DEBUG
631 gDebugMaxWindSum = 4;
632 gDebugMaxWindValue = 4;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000633#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000634 testName = test;
caryclark@google.com66089e42013-04-10 15:55:37 +0000635 size_t testNameSize = strlen(test);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000636 SkFILEStream inFile("../../experimental/Intersection/op.htm");
637 if (inFile.isValid()) {
638 SkTDArray<char> inData;
639 inData.setCount(inFile.getLength());
640 size_t inLen = inData.count();
641 inFile.read(inData.begin(), inLen);
642 inFile.setPath(NULL);
643 char* insert = strstr(inData.begin(), marker);
644 if (insert) {
645 insert += sizeof(marker) - 1;
646 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
647 testNumber = atoi(numLoc) + 1;
648 }
649 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000650 return reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000651}
652
caryclark@google.com66089e42013-04-10 15:55:37 +0000653void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
654 const char testFunction[] = "testSimplify(path);";
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000655 const char* pathPrefix = NULL;
656 const char* nameSuffix = NULL;
657 if (pathFillType == SkPath::kEvenOdd_FillType) {
658 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
659 nameSuffix = "x";
660 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000661 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
662 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000663}
664
caryclark@google.com66089e42013-04-10 15:55:37 +0000665void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
666 const char testFunction[] = "testOp(path);";
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000667 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000668 const char* nameSuffix = opSuffixes[op];
caryclark@google.com66089e42013-04-10 15:55:37 +0000669 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
670 outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000671}
672
673void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
674 void (*firstTest)(skiatest::Reporter* ),
675 void (*stopTest)(skiatest::Reporter* ), bool reverse) {
676 size_t index;
677 if (firstTest) {
678 index = count - 1;
679 while (index > 0 && tests[index].fun != firstTest) {
680 --index;
681 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000682#if DEBUG_SHOW_TEST_NAME
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000683 SkDebugf("<div id=\"%s\">\n", tests[index].str);
684 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
685#endif
686 (*tests[index].fun)(reporter);
687 }
688 index = reverse ? count - 1 : 0;
689 size_t last = reverse ? 0 : count - 1;
690 do {
691 if (tests[index].fun != firstTest) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000692 #if DEBUG_SHOW_TEST_NAME
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000693 SkDebugf("<div id=\"%s\">\n", tests[index].str);
694 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
695 #endif
696 (*tests[index].fun)(reporter);
697 }
698 if (tests[index].fun == stopTest) {
699 SkDebugf("lastTest\n");
700 }
701 if (index == last) {
702 break;
703 }
704 index += reverse ? -1 : 1;
705 } while (true);
706}