blob: 08954327e96cfd99debdcace5700fe8f4d035abb [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"
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000012#include "SkForceLinking.h"
caryclark@google.com818b0cc2013-04-08 11:50:46 +000013#include "SkMatrix.h"
skia.committer@gmail.come02c5da2014-04-26 03:04:35 +000014#include "SkPaint.h"
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +000015#include "SkRTConf.h"
caryclark@google.com818b0cc2013-04-08 11:50:46 +000016#include "SkStream.h"
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000017#include "SkThread.h"
caryclark@google.coma5e55922013-05-07 18:51:31 +000018#include "SkThreadPool.h"
caryclark@google.com818b0cc2013-04-08 11:50:46 +000019
20#ifdef SK_BUILD_FOR_MAC
21#include <sys/sysctl.h>
22#endif
23
caryclark@google.com7eaa53d2013-10-02 14:49:34 +000024__SK_FORCE_IMAGE_DECODER_LINKING;
25
caryclark@google.com818b0cc2013-04-08 11:50:46 +000026static const char marker[] =
27 "</div>\n"
28 "\n"
29 "<script type=\"text/javascript\">\n"
30 "\n"
31 "var testDivs = [\n";
32
33static const char* opStrs[] = {
34 "kDifference_PathOp",
35 "kIntersect_PathOp",
36 "kUnion_PathOp",
37 "kXor_PathOp",
caryclark@google.com6dc7df62013-04-25 11:51:54 +000038 "kReverseDifference_PathOp",
caryclark@google.com818b0cc2013-04-08 11:50:46 +000039};
40
41static const char* opSuffixes[] = {
42 "d",
43 "i",
44 "u",
caryclark@google.com66089e42013-04-10 15:55:37 +000045 "o",
caryclark@google.com818b0cc2013-04-08 11:50:46 +000046};
47
48static bool gShowPath = false;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000049static bool gComparePathsAssert = true;
50static bool gPathStrAssert = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +000051
caryclark@google.com07e97fc2013-07-08 17:17:02 +000052static const char* gFillTypeStr[] = {
53 "kWinding_FillType",
54 "kEvenOdd_FillType",
55 "kInverseWinding_FillType",
56 "kInverseEvenOdd_FillType"
57};
58
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000059static void output_scalar(SkScalar num) {
60 if (num == (int) num) {
61 SkDebugf("%d", (int) num);
62 } else {
63 SkString str;
64 str.printf("%1.9g", num);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000065 int width = (int) str.size();
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000066 const char* cStr = str.c_str();
67 while (cStr[width - 1] == '0') {
68 --width;
69 }
70 str.resize(width);
71 SkDebugf("%sf", str.c_str());
72 }
73}
74
75static void output_points(const SkPoint* pts, int count) {
76 for (int index = 0; index < count; ++index) {
77 output_scalar(pts[index].fX);
78 SkDebugf(", ");
79 output_scalar(pts[index].fY);
80 if (index + 1 < count) {
81 SkDebugf(", ");
82 }
83 }
84 SkDebugf(");\n");
85}
86
caryclark@google.com07e97fc2013-07-08 17:17:02 +000087static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +000088 uint8_t verb;
89 SkPoint pts[4];
90 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
91 switch (verb) {
92 case SkPath::kMove_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000093 SkDebugf(" %s.moveTo(", pathName);
94 output_points(&pts[0], 1);
caryclark@google.com818b0cc2013-04-08 11:50:46 +000095 continue;
96 case SkPath::kLine_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000097 SkDebugf(" %s.lineTo(", pathName);
98 output_points(&pts[1], 1);
caryclark@google.com818b0cc2013-04-08 11:50:46 +000099 break;
100 case SkPath::kQuad_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000101 SkDebugf(" %s.quadTo(", pathName);
102 output_points(&pts[1], 2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000103 break;
104 case SkPath::kCubic_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000105 SkDebugf(" %s.cubicTo(", pathName);
106 output_points(&pts[1], 3);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000107 break;
108 case SkPath::kClose_Verb:
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000109 SkDebugf(" %s.close();\n", pathName);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000110 break;
111 default:
112 SkDEBUGFAIL("bad verb");
113 return;
114 }
115 }
116}
117
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000118static void showPath(const SkPath& path, const char* pathName, bool includeDeclaration) {
119 SkPath::RawIter iter(path);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000120#define SUPPORT_RECT_CONTOUR_DETECTION 0
121#if SUPPORT_RECT_CONTOUR_DETECTION
122 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
123 if (rectCount > 0) {
124 SkTDArray<SkRect> rects;
125 SkTDArray<SkPath::Direction> directions;
126 rects.setCount(rectCount);
127 directions.setCount(rectCount);
128 path.rectContours(rects.begin(), directions.begin());
129 for (int contour = 0; contour < rectCount; ++contour) {
130 const SkRect& rect = rects[contour];
131 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
132 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
133 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
134 }
135 return;
136 }
137#endif
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000138 SkPath::FillType fillType = path.getFillType();
139 SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000140 if (includeDeclaration) {
141 SkDebugf(" SkPath %s;\n", pathName);
142 }
143 SkDebugf(" %s.setFillType(SkPath::%s);\n", pathName, gFillTypeStr[fillType]);
144 iter.setPath(path);
145 showPathContours(iter, pathName);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000146}
147
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000148#if DEBUG_SHOW_TEST_NAME
149static void showPathData(const SkPath& path) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000150 SkPath::RawIter iter(path);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000151 uint8_t verb;
152 SkPoint pts[4];
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000153 SkPoint firstPt = {0, 0}, lastPt = {0, 0};
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000154 bool firstPtSet = false;
155 bool lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000156 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
157 switch (verb) {
158 case SkPath::kMove_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000159 firstPt = pts[0];
160 firstPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000161 continue;
162 case SkPath::kLine_Verb:
caryclark@google.com66089e42013-04-10 15:55:37 +0000163 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
164 pts[1].fX, pts[1].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000165 lastPt = pts[1];
166 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000167 break;
168 case SkPath::kQuad_Verb:
169 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000170 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 +0000171 lastPt = pts[2];
172 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000173 break;
174 case SkPath::kCubic_Verb:
175 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 +0000176 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
177 pts[3].fX, pts[3].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000178 lastPt = pts[3];
179 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000180 break;
181 case SkPath::kClose_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000182 if (firstPtSet && lastPtSet && firstPt != lastPt) {
183 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
184 firstPt.fX, firstPt.fY);
185 }
186 firstPtSet = lastPtSet = false;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000187 break;
188 default:
189 SkDEBUGFAIL("bad verb");
190 return;
191 }
192 }
193}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000194#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000195
196void showOp(const SkPathOp op) {
197 switch (op) {
198 case kDifference_PathOp:
199 SkDebugf("op difference\n");
200 break;
201 case kIntersect_PathOp:
202 SkDebugf("op intersect\n");
203 break;
204 case kUnion_PathOp:
205 SkDebugf("op union\n");
206 break;
207 case kXOR_PathOp:
208 SkDebugf("op xor\n");
209 break;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000210 case kReverseDifference_PathOp:
211 SkDebugf("op reverse difference\n");
212 break;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000213 default:
214 SkASSERT(0);
215 }
216}
217
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000218#if DEBUG_SHOW_TEST_NAME
219
220void ShowFunctionHeader(const char* functionName) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000221 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000222 if (strcmp("skphealth_com76", functionName) == 0) {
223 SkDebugf("found it\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000224 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000225}
226
227static const char* gOpStrs[] = {
228 "kDifference_PathOp",
229 "kIntersect_PathOp",
230 "kUnion_PathOp",
231 "kXor_PathOp",
232 "kReverseDifference_PathOp",
233};
234
235void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000236 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000237 SkDebugf("}\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000238}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000239#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000240
caryclark@google.com03610322013-04-18 15:58:21 +0000241#if DEBUG_SHOW_TEST_NAME
242static char hexorator(int x) {
243 if (x < 10) {
244 return x + '0';
245 }
246 x -= 10;
247 SkASSERT(x < 26);
248 return x + 'A';
249}
250#endif
251
252void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) {
253#if DEBUG_SHOW_TEST_NAME
254 state->fSerialNo[0] = hexorator(state->fA);
255 state->fSerialNo[1] = hexorator(state->fB);
256 state->fSerialNo[2] = hexorator(state->fC);
257 state->fSerialNo[3] = hexorator(state->fD);
258 state->fSerialNo[4] = hexorator(a);
259 state->fSerialNo[5] = hexorator(b);
260 state->fSerialNo[6] = hexorator(c);
261 state->fSerialNo[7] = hexorator(d);
262 state->fSerialNo[8] = '\0';
263 SkDebugf("%s\n", state->fSerialNo);
264 if (strcmp(state->fSerialNo, state->fKey) == 0) {
265 SkDebugf("%s\n", state->fPathStr);
266 }
267#endif
268}
269
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000270const int bitWidth = 64;
271const int bitHeight = 64;
272
273static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
274 SkRect larger = one.getBounds();
275 larger.join(two.getBounds());
276 SkScalar largerWidth = larger.width();
277 if (largerWidth < 4) {
278 largerWidth = 4;
279 }
280 SkScalar largerHeight = larger.height();
281 if (largerHeight < 4) {
282 largerHeight = 4;
283 }
284 SkScalar hScale = (bitWidth - 2) / largerWidth;
285 SkScalar vScale = (bitHeight - 2) / largerHeight;
286 scale.reset();
287 scale.preScale(hScale, vScale);
288}
289
290static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
291 int& error2x2) {
292 if (bits.width() == 0) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000293 bits.allocN32Pixels(bitWidth * 2, bitHeight);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000294 }
295 SkCanvas canvas(bits);
296 canvas.drawColor(SK_ColorWHITE);
297 SkPaint paint;
298 canvas.save();
299 const SkRect& bounds1 = scaledOne.getBounds();
300 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
301 canvas.drawPath(scaledOne, paint);
302 canvas.restore();
303 canvas.save();
304 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
305 canvas.drawPath(scaledTwo, paint);
306 canvas.restore();
307 int errors2 = 0;
308 int errors = 0;
309 for (int y = 0; y < bitHeight - 1; ++y) {
310 uint32_t* addr1 = bits.getAddr32(0, y);
311 uint32_t* addr2 = bits.getAddr32(0, y + 1);
312 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
313 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
314 for (int x = 0; x < bitWidth - 1; ++x) {
315 // count 2x2 blocks
316 bool err = addr1[x] != addr3[x];
317 if (err) {
318 errors2 += addr1[x + 1] != addr3[x + 1]
319 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
320 errors++;
321 }
322 }
323 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000324 error2x2 = errors2;
325 return errors;
326}
327
328static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
329 SkPath& scaledTwo, int& error2x2) {
330 SkMatrix scale;
331 scaleMatrix(one, two, scale);
332 one.transform(scale, &scaledOne);
333 two.transform(scale, &scaledTwo);
334 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
335}
336
337bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
338 if (!drawPaths) {
339 return true;
340 }
341 const SkRect& bounds1 = one.getBounds();
342 const SkRect& bounds2 = two.getBounds();
343 SkRect larger = bounds1;
344 larger.join(bounds2);
345 SkBitmap bits;
346 char out[256];
reed@google.come1ca7052013-12-17 19:22:07 +0000347 int bitWidth = SkScalarCeilToInt(larger.width()) + 2;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000348 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
349 return false;
350 }
reed@google.come1ca7052013-12-17 19:22:07 +0000351 int bitHeight = SkScalarCeilToInt(larger.height()) + 2;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000352 if (bitHeight >= (int) sizeof(out)) {
353 return false;
354 }
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000355 bits.allocN32Pixels(bitWidth * 2, bitHeight);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000356 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
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000384static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
385 const SkPath& two, SkBitmap& bitmap) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000386 int errors2x2;
387 SkPath scaledOne, scaledTwo;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000388 (void) pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000389 if (errors2x2 == 0) {
390 return 0;
391 }
392 const int MAX_ERRORS = 9;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000393 REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000394 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
395}
396
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000397const int gTestFirst = 4;
398static int gTestNo = gTestFirst;
399static SkTDArray<SkPathOp> gTestOp;
400
401static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
402 const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
403 const SkPathOp shapeOp, const SkMatrix& scale) {
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000404 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000405 SkString defaultTestName;
406 if (!testName) {
407 defaultTestName.printf("xOp%d%s", gTestNo, opSuffixes[shapeOp]);
408 testName = defaultTestName.c_str();
409 }
410 SkDebugf("static void %s(skiatest::Reporter* reporter, const char* filename) {\n", testName);
411 *gTestOp.append() = shapeOp;
412 ++gTestNo;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000413 SkDebugf(" SkPath path, pathB;\n");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000414 showPath(a, "path", false);
415 showPath(b, "pathB", false);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000416 SkDebugf(" testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000417 SkDebugf("}\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000418 drawAsciiPaths(scaledOne, scaledTwo, false);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000419}
420
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000421void ShowTestArray() {
422 for (int x = gTestFirst; x < gTestNo; ++x) {
423 SkDebugf(" TEST(xOp%d%s),\n", x, opSuffixes[gTestOp[x - gTestFirst]]);
424 }
425}
426
427static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one,
428 const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
429 const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000430 int errors2x2;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000431 (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000432 if (errors2x2 == 0) {
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000433 if (gShowPath) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000434 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000435 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000436 return 0;
437 }
438 const int MAX_ERRORS = 8;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000439 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000440 SK_DECLARE_STATIC_MUTEX(compareDebugOut3);
441 SkAutoMutexAcquire autoM(compareDebugOut3);
442 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000443 REPORTER_ASSERT(reporter, 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000444 } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
445 SK_DECLARE_STATIC_MUTEX(compareDebugOut4);
446 SkAutoMutexAcquire autoM(compareDebugOut4);
447 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000448 }
449 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
450}
451
commit-bot@chromium.org409774e2013-10-02 16:15:44 +0000452// Default values for when reporter->verbose() is false.
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000453static int testNumber = 55;
commit-bot@chromium.org409774e2013-10-02 16:15:44 +0000454static const char* testName = "pathOpTest";
caryclark@google.com66089e42013-04-10 15:55:37 +0000455
456static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
457 outFile.writeText(testName);
458 outFile.writeDecAsText(testNumber);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000459 ++testNumber;
caryclark@google.com66089e42013-04-10 15:55:37 +0000460 if (nameSuffix) {
461 outFile.writeText(nameSuffix);
462 }
463}
464
465static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
466 const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000467#if 0
caryclark@google.com66089e42013-04-10 15:55:37 +0000468 outFile.writeText("<div id=\"");
469 writeTestName(nameSuffix, outFile);
470 outFile.writeText("\">\n");
471 if (pathPrefix) {
472 outFile.writeText(pathPrefix);
473 }
474 outFile.writeText(pathStr);
475 outFile.writeText("</div>\n\n");
476
477 outFile.writeText(marker);
478 outFile.writeText(" ");
479 writeTestName(nameSuffix, outFile);
480 outFile.writeText(",\n\n\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000481#endif
caryclark@google.com66089e42013-04-10 15:55:37 +0000482 outFile.writeText("static void ");
483 writeTestName(nameSuffix, outFile);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000484 outFile.writeText("(skiatest::Reporter* reporter) {\n SkPath path");
caryclark@google.com66089e42013-04-10 15:55:37 +0000485 if (twoPaths) {
486 outFile.writeText(", pathB");
487 }
488 outFile.writeText(";\n");
489 if (pathPrefix) {
490 outFile.writeText(pathPrefix);
491 }
492 outFile.writeText(pathStr);
493 outFile.writeText(" ");
494 outFile.writeText(testFunction);
495 outFile.writeText("\n}\n\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000496#if 0
caryclark@google.com66089e42013-04-10 15:55:37 +0000497 outFile.writeText("static void (*firstTest)() = ");
498 writeTestName(nameSuffix, outFile);
499 outFile.writeText(";\n\n");
500
501 outFile.writeText("static struct {\n");
502 outFile.writeText(" void (*fun)();\n");
503 outFile.writeText(" const char* str;\n");
504 outFile.writeText("} tests[] = {\n");
505 outFile.writeText(" TEST(");
506 writeTestName(nameSuffix, outFile);
507 outFile.writeText("),\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000508#endif
caryclark@google.com66089e42013-04-10 15:55:37 +0000509 outFile.flush();
510}
511
512bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
513 const char* pathStr) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000514 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
515 path.setFillType(fillType);
516 if (gShowPath) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000517 showPath(path, "path", false);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000518 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000519 if (!Simplify(path, &out)) {
520 SkDebugf("%s did not expect failure\n", __FUNCTION__);
521 REPORTER_ASSERT(state.fReporter, 0);
522 return false;
523 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000524 if (!state.fReporter->verbose()) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000525 return true;
526 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000527 int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000528 if (result && gPathStrAssert) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000529 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
530 SkAutoMutexAcquire autoM(simplifyDebugOut);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000531 char temp[8192];
532 sk_bzero(temp, sizeof(temp));
533 SkMemoryWStream stream(temp, sizeof(temp));
534 const char* pathPrefix = NULL;
535 const char* nameSuffix = NULL;
536 if (fillType == SkPath::kEvenOdd_FillType) {
537 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
538 nameSuffix = "x";
539 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000540 const char testFunction[] = "testSimplify(reporter, path);";
caryclark@google.com66089e42013-04-10 15:55:37 +0000541 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000542 SkDebugf(temp);
caryclark@google.com66089e42013-04-10 15:55:37 +0000543 REPORTER_ASSERT(state.fReporter, 0);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000544 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000545 state.fReporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000546 return result == 0;
547}
548
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000549bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000550#if DEBUG_SHOW_TEST_NAME
caryclark@google.com03610322013-04-18 15:58:21 +0000551 showPathData(path);
552#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000553 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000554 if (!Simplify(path, &out)) {
555 SkDebugf("%s did not expect failure\n", __FUNCTION__);
556 REPORTER_ASSERT(reporter, 0);
557 return false;
558 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000559 SkBitmap bitmap;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000560 int result = comparePaths(reporter, filename, path, out, bitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000561 if (result && gPathStrAssert) {
562 REPORTER_ASSERT(reporter, 0);
563 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000564 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000565 return result == 0;
566}
567
caryclark@google.coma5e55922013-05-07 18:51:31 +0000568#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000569
570SK_DECLARE_STATIC_MUTEX(gTestMutex);
571
caryclark@google.com570863f2013-09-16 15:55:01 +0000572void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
573 const char* testName) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000574 SkAutoMutexAcquire ac(gTestMutex);
caryclark@google.com570863f2013-09-16 15:55:01 +0000575 ShowFunctionHeader(testName);
576 showPath(a, "path", true);
577 showPath(b, "pathB", true);
578 ShowOp(shapeOp, "path", "pathB");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000579}
580#endif
581
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000582#if DEBUG_SHOW_TEST_NAME
583static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
584 SkDebugf("\n");
585 showPathData(a);
586 showOp(shapeOp);
587 showPathData(b);
588}
589#endif
590
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000591static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
592 const SkPathOp shapeOp, const char* testName, bool threaded) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000593#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000594 showName(a, b, shapeOp);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000595#endif
596 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000597 if (!Op(a, b, shapeOp, &out) ) {
598 SkDebugf("%s did not expect failure\n", __FUNCTION__);
599 REPORTER_ASSERT(reporter, 0);
600 return false;
601 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000602 if (threaded && !reporter->verbose()) {
603 return true;
604 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000605 SkPath pathOut, scaledPathOut;
606 SkRegion rgnA, rgnB, openClip, rgnOut;
607 openClip.setRect(-16000, -16000, 16000, 16000);
608 rgnA.setPath(a, openClip);
609 rgnB.setPath(b, openClip);
610 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
611 rgnOut.getBoundaryPath(&pathOut);
612
613 SkMatrix scale;
614 scaleMatrix(a, b, scale);
615 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
616 SkPath scaledA, scaledB;
617 scaledA.addPath(a, scale);
618 scaledA.setFillType(a.getFillType());
619 scaledB.addPath(b, scale);
620 scaledB.setFillType(b.getFillType());
621 scaledRgnA.setPath(scaledA, openClip);
622 scaledRgnB.setPath(scaledB, openClip);
623 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
624 scaledRgnOut.getBoundaryPath(&scaledPathOut);
625 SkBitmap bitmap;
626 SkPath scaledOut;
627 scaledOut.addPath(out, scale);
628 scaledOut.setFillType(out.getFillType());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000629 int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
630 a, b, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000631 if (result && gPathStrAssert) {
632 REPORTER_ASSERT(reporter, 0);
633 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000634 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000635 return result == 0;
636}
637
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000638bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
639 const SkPathOp shapeOp, const char* testName) {
640 return innerPathOp(reporter, a, b, shapeOp, testName, false);
641}
642
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000643bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
644 const SkPathOp shapeOp, const char* testName) {
645#if DEBUG_SHOW_TEST_NAME
646 showName(a, b, shapeOp);
647#endif
648 SkPath out;
649 if (Op(a, b, shapeOp, &out) ) {
650 SkDebugf("%s test is expected to fail\n", __FUNCTION__);
651 REPORTER_ASSERT(reporter, 0);
652 return false;
653 }
654 return true;
655}
656
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000657bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
658 const SkPathOp shapeOp, const char* testName) {
659 return innerPathOp(reporter, a, b, shapeOp, testName, true);
660}
661
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000662SK_DECLARE_STATIC_MUTEX(gMutex);
663
caryclark@google.com16cfe402013-04-18 18:47:37 +0000664int initializeTests(skiatest::Reporter* reporter, const char* test) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000665#if 0 // doesn't work yet
666 SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
667 SK_CONF_SET("images.png.suppressDecoderWarnings", true);
668#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000669 if (reporter->verbose()) {
670 SkAutoMutexAcquire lock(gMutex);
671 testName = test;
672 size_t testNameSize = strlen(test);
673 SkFILEStream inFile("../../experimental/Intersection/op.htm");
674 if (inFile.isValid()) {
675 SkTDArray<char> inData;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000676 inData.setCount((int) inFile.getLength());
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000677 size_t inLen = inData.count();
678 inFile.read(inData.begin(), inLen);
679 inFile.setPath(NULL);
680 char* insert = strstr(inData.begin(), marker);
681 if (insert) {
682 insert += sizeof(marker) - 1;
683 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
684 testNumber = atoi(numLoc) + 1;
685 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000686 }
687 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000688 return reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000689}
690
caryclark@google.com66089e42013-04-10 15:55:37 +0000691void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
692 const char testFunction[] = "testSimplify(path);";
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000693 const char* pathPrefix = NULL;
694 const char* nameSuffix = NULL;
695 if (pathFillType == SkPath::kEvenOdd_FillType) {
696 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
697 nameSuffix = "x";
698 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000699 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
700 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000701}
702
caryclark@google.com66089e42013-04-10 15:55:37 +0000703void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
704 const char testFunction[] = "testOp(path);";
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000705 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000706 const char* nameSuffix = opSuffixes[op];
caryclark@google.com66089e42013-04-10 15:55:37 +0000707 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
708 outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000709}
710
711void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000712 void (*firstTest)(skiatest::Reporter* , const char* filename),
713 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000714 size_t index;
715 if (firstTest) {
716 index = count - 1;
717 while (index > 0 && tests[index].fun != firstTest) {
718 --index;
719 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000720#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000721 SkDebugf("<div id=\"%s\">\n", tests[index].str);
722 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000723#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000724 (*tests[index].fun)(reporter, tests[index].str);
725 if (tests[index].fun == stopTest) {
726 return;
727 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000728 }
729 index = reverse ? count - 1 : 0;
730 size_t last = reverse ? 0 : count - 1;
731 do {
732 if (tests[index].fun != firstTest) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000733 #if DEBUG_SHOW_TEST_NAME
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000734 SkDebugf("<div id=\"%s\">\n", tests[index].str);
735 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
736 #endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000737 (*tests[index].fun)(reporter, tests[index].str);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000738 }
739 if (tests[index].fun == stopTest) {
740 SkDebugf("lastTest\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000741 break;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000742 }
743 if (index == last) {
744 break;
745 }
746 index += reverse ? -1 : 1;
747 } while (true);
748}