blob: fe3d24d6a09b27d88f753144d6d750061c80a8f7 [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:
caryclarkdac1d172014-06-17 05:15:38 -0700159 if (firstPtSet && lastPtSet && firstPt != lastPt) {
160 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
161 firstPt.fX, firstPt.fY);
162 lastPtSet = false;
163 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000164 firstPt = pts[0];
165 firstPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000166 continue;
167 case SkPath::kLine_Verb:
caryclark@google.com66089e42013-04-10 15:55:37 +0000168 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY,
169 pts[1].fX, pts[1].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000170 lastPt = pts[1];
171 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000172 break;
173 case SkPath::kQuad_Verb:
174 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
caryclark@google.com66089e42013-04-10 15:55:37 +0000175 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 +0000176 lastPt = pts[2];
177 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000178 break;
179 case SkPath::kCubic_Verb:
180 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 +0000181 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
182 pts[3].fX, pts[3].fY);
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000183 lastPt = pts[3];
184 lastPtSet = true;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000185 break;
186 case SkPath::kClose_Verb:
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000187 if (firstPtSet && lastPtSet && firstPt != lastPt) {
188 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
189 firstPt.fX, firstPt.fY);
190 }
191 firstPtSet = lastPtSet = false;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000192 break;
193 default:
194 SkDEBUGFAIL("bad verb");
195 return;
196 }
197 }
caryclarkdac1d172014-06-17 05:15:38 -0700198 if (firstPtSet && lastPtSet && firstPt != lastPt) {
199 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", lastPt.fX, lastPt.fY,
200 firstPt.fX, firstPt.fY);
201 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000202}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000203#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000204
205void showOp(const SkPathOp op) {
206 switch (op) {
207 case kDifference_PathOp:
208 SkDebugf("op difference\n");
209 break;
210 case kIntersect_PathOp:
211 SkDebugf("op intersect\n");
212 break;
213 case kUnion_PathOp:
214 SkDebugf("op union\n");
215 break;
216 case kXOR_PathOp:
217 SkDebugf("op xor\n");
218 break;
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000219 case kReverseDifference_PathOp:
220 SkDebugf("op reverse difference\n");
221 break;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000222 default:
223 SkASSERT(0);
224 }
225}
226
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000227#if DEBUG_SHOW_TEST_NAME
228
229void ShowFunctionHeader(const char* functionName) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000230 SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000231 if (strcmp("skphealth_com76", functionName) == 0) {
232 SkDebugf("found it\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000233 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000234}
235
236static const char* gOpStrs[] = {
237 "kDifference_PathOp",
238 "kIntersect_PathOp",
239 "kUnion_PathOp",
240 "kXor_PathOp",
241 "kReverseDifference_PathOp",
242};
243
244void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000245 SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000246 SkDebugf("}\n");
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000247}
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000248#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000249
caryclark@google.com03610322013-04-18 15:58:21 +0000250#if DEBUG_SHOW_TEST_NAME
251static char hexorator(int x) {
252 if (x < 10) {
253 return x + '0';
254 }
255 x -= 10;
256 SkASSERT(x < 26);
257 return x + 'A';
258}
259#endif
260
261void ShowTestName(PathOpsThreadState* state, int a, int b, int c, int d) {
262#if DEBUG_SHOW_TEST_NAME
263 state->fSerialNo[0] = hexorator(state->fA);
264 state->fSerialNo[1] = hexorator(state->fB);
265 state->fSerialNo[2] = hexorator(state->fC);
266 state->fSerialNo[3] = hexorator(state->fD);
267 state->fSerialNo[4] = hexorator(a);
268 state->fSerialNo[5] = hexorator(b);
269 state->fSerialNo[6] = hexorator(c);
270 state->fSerialNo[7] = hexorator(d);
271 state->fSerialNo[8] = '\0';
272 SkDebugf("%s\n", state->fSerialNo);
273 if (strcmp(state->fSerialNo, state->fKey) == 0) {
274 SkDebugf("%s\n", state->fPathStr);
275 }
276#endif
277}
278
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000279const int bitWidth = 64;
280const int bitHeight = 64;
281
282static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
283 SkRect larger = one.getBounds();
284 larger.join(two.getBounds());
285 SkScalar largerWidth = larger.width();
286 if (largerWidth < 4) {
287 largerWidth = 4;
288 }
289 SkScalar largerHeight = larger.height();
290 if (largerHeight < 4) {
291 largerHeight = 4;
292 }
293 SkScalar hScale = (bitWidth - 2) / largerWidth;
294 SkScalar vScale = (bitHeight - 2) / largerHeight;
295 scale.reset();
296 scale.preScale(hScale, vScale);
297}
298
299static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
300 int& error2x2) {
301 if (bits.width() == 0) {
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000302 bits.allocN32Pixels(bitWidth * 2, bitHeight);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000303 }
304 SkCanvas canvas(bits);
305 canvas.drawColor(SK_ColorWHITE);
306 SkPaint paint;
307 canvas.save();
308 const SkRect& bounds1 = scaledOne.getBounds();
309 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
310 canvas.drawPath(scaledOne, paint);
311 canvas.restore();
312 canvas.save();
313 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
314 canvas.drawPath(scaledTwo, paint);
315 canvas.restore();
316 int errors2 = 0;
317 int errors = 0;
318 for (int y = 0; y < bitHeight - 1; ++y) {
319 uint32_t* addr1 = bits.getAddr32(0, y);
320 uint32_t* addr2 = bits.getAddr32(0, y + 1);
321 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
322 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
323 for (int x = 0; x < bitWidth - 1; ++x) {
324 // count 2x2 blocks
325 bool err = addr1[x] != addr3[x];
326 if (err) {
327 errors2 += addr1[x + 1] != addr3[x + 1]
328 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
329 errors++;
330 }
331 }
332 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000333 error2x2 = errors2;
334 return errors;
335}
336
337static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
338 SkPath& scaledTwo, int& error2x2) {
339 SkMatrix scale;
340 scaleMatrix(one, two, scale);
341 one.transform(scale, &scaledOne);
342 two.transform(scale, &scaledTwo);
343 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
344}
345
346bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
347 if (!drawPaths) {
348 return true;
349 }
350 const SkRect& bounds1 = one.getBounds();
351 const SkRect& bounds2 = two.getBounds();
352 SkRect larger = bounds1;
353 larger.join(bounds2);
354 SkBitmap bits;
355 char out[256];
reed@google.come1ca7052013-12-17 19:22:07 +0000356 int bitWidth = SkScalarCeilToInt(larger.width()) + 2;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000357 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
358 return false;
359 }
reed@google.come1ca7052013-12-17 19:22:07 +0000360 int bitHeight = SkScalarCeilToInt(larger.height()) + 2;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000361 if (bitHeight >= (int) sizeof(out)) {
362 return false;
363 }
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000364 bits.allocN32Pixels(bitWidth * 2, bitHeight);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000365 SkCanvas canvas(bits);
366 canvas.drawColor(SK_ColorWHITE);
367 SkPaint paint;
368 canvas.save();
369 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
370 canvas.drawPath(one, paint);
371 canvas.restore();
372 canvas.save();
373 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
374 canvas.drawPath(two, paint);
375 canvas.restore();
376 for (int y = 0; y < bitHeight; ++y) {
377 uint32_t* addr1 = bits.getAddr32(0, y);
378 int x;
379 char* outPtr = out;
380 for (x = 0; x < bitWidth; ++x) {
381 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
382 }
383 *outPtr++ = '|';
384 for (x = bitWidth; x < bitWidth * 2; ++x) {
385 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
386 }
387 *outPtr++ = '\0';
388 SkDebugf("%s\n", out);
389 }
390 return true;
391}
392
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000393static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
394 const SkPath& two, SkBitmap& bitmap) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000395 int errors2x2;
396 SkPath scaledOne, scaledTwo;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000397 (void) pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000398 if (errors2x2 == 0) {
399 return 0;
400 }
401 const int MAX_ERRORS = 9;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000402 REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000403 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
404}
405
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000406const int gTestFirst = 4;
407static int gTestNo = gTestFirst;
408static SkTDArray<SkPathOp> gTestOp;
409
410static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
411 const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
412 const SkPathOp shapeOp, const SkMatrix& scale) {
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000413 SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000414 SkString defaultTestName;
415 if (!testName) {
416 defaultTestName.printf("xOp%d%s", gTestNo, opSuffixes[shapeOp]);
417 testName = defaultTestName.c_str();
418 }
419 SkDebugf("static void %s(skiatest::Reporter* reporter, const char* filename) {\n", testName);
420 *gTestOp.append() = shapeOp;
421 ++gTestNo;
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000422 SkDebugf(" SkPath path, pathB;\n");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000423 showPath(a, "path", false);
424 showPath(b, "pathB", false);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000425 SkDebugf(" testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
caryclark@google.comcffbcc32013-06-04 17:59:42 +0000426 SkDebugf("}\n");
commit-bot@chromium.org91fc81c2014-04-30 13:37:48 +0000427 drawAsciiPaths(scaledOne, scaledTwo, true);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000428}
429
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000430void ShowTestArray() {
431 for (int x = gTestFirst; x < gTestNo; ++x) {
432 SkDebugf(" TEST(xOp%d%s),\n", x, opSuffixes[gTestOp[x - gTestFirst]]);
433 }
434}
435
436static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one,
437 const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
438 const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000439 int errors2x2;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000440 (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000441 if (errors2x2 == 0) {
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000442 if (gShowPath) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000443 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com6dc7df62013-04-25 11:51:54 +0000444 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000445 return 0;
446 }
447 const int MAX_ERRORS = 8;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000448 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000449 SK_DECLARE_STATIC_MUTEX(compareDebugOut3);
450 SkAutoMutexAcquire autoM(compareDebugOut3);
caryclarkdac1d172014-06-17 05:15:38 -0700451 SkDebugf("\n*** this test fails ***\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000452 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000453 REPORTER_ASSERT(reporter, 0);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000454 } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
455 SK_DECLARE_STATIC_MUTEX(compareDebugOut4);
456 SkAutoMutexAcquire autoM(compareDebugOut4);
457 showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000458 }
459 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
460}
461
commit-bot@chromium.org409774e2013-10-02 16:15:44 +0000462// Default values for when reporter->verbose() is false.
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000463static int testNumber = 55;
commit-bot@chromium.org409774e2013-10-02 16:15:44 +0000464static const char* testName = "pathOpTest";
caryclark@google.com66089e42013-04-10 15:55:37 +0000465
466static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
467 outFile.writeText(testName);
468 outFile.writeDecAsText(testNumber);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000469 ++testNumber;
caryclark@google.com66089e42013-04-10 15:55:37 +0000470 if (nameSuffix) {
471 outFile.writeText(nameSuffix);
472 }
473}
474
475static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
476 const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000477#if 0
caryclark@google.com66089e42013-04-10 15:55:37 +0000478 outFile.writeText("<div id=\"");
479 writeTestName(nameSuffix, outFile);
480 outFile.writeText("\">\n");
481 if (pathPrefix) {
482 outFile.writeText(pathPrefix);
483 }
484 outFile.writeText(pathStr);
485 outFile.writeText("</div>\n\n");
486
487 outFile.writeText(marker);
488 outFile.writeText(" ");
489 writeTestName(nameSuffix, outFile);
490 outFile.writeText(",\n\n\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000491#endif
caryclark@google.com66089e42013-04-10 15:55:37 +0000492 outFile.writeText("static void ");
493 writeTestName(nameSuffix, outFile);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000494 outFile.writeText("(skiatest::Reporter* reporter) {\n SkPath path");
caryclark@google.com66089e42013-04-10 15:55:37 +0000495 if (twoPaths) {
496 outFile.writeText(", pathB");
497 }
498 outFile.writeText(";\n");
499 if (pathPrefix) {
500 outFile.writeText(pathPrefix);
501 }
502 outFile.writeText(pathStr);
503 outFile.writeText(" ");
504 outFile.writeText(testFunction);
505 outFile.writeText("\n}\n\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000506#if 0
caryclark@google.com66089e42013-04-10 15:55:37 +0000507 outFile.writeText("static void (*firstTest)() = ");
508 writeTestName(nameSuffix, outFile);
509 outFile.writeText(";\n\n");
510
511 outFile.writeText("static struct {\n");
512 outFile.writeText(" void (*fun)();\n");
513 outFile.writeText(" const char* str;\n");
514 outFile.writeText("} tests[] = {\n");
515 outFile.writeText(" TEST(");
516 writeTestName(nameSuffix, outFile);
517 outFile.writeText("),\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000518#endif
caryclark@google.com66089e42013-04-10 15:55:37 +0000519 outFile.flush();
520}
521
522bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
523 const char* pathStr) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000524 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
525 path.setFillType(fillType);
526 if (gShowPath) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000527 showPath(path, "path", false);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000528 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000529 if (!Simplify(path, &out)) {
530 SkDebugf("%s did not expect failure\n", __FUNCTION__);
531 REPORTER_ASSERT(state.fReporter, 0);
532 return false;
533 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000534 if (!state.fReporter->verbose()) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000535 return true;
536 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000537 int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000538 if (result && gPathStrAssert) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000539 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
540 SkAutoMutexAcquire autoM(simplifyDebugOut);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000541 char temp[8192];
542 sk_bzero(temp, sizeof(temp));
543 SkMemoryWStream stream(temp, sizeof(temp));
544 const char* pathPrefix = NULL;
545 const char* nameSuffix = NULL;
546 if (fillType == SkPath::kEvenOdd_FillType) {
547 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
548 nameSuffix = "x";
549 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000550 const char testFunction[] = "testSimplify(reporter, path);";
caryclark@google.com66089e42013-04-10 15:55:37 +0000551 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000552 SkDebugf(temp);
caryclark@google.com66089e42013-04-10 15:55:37 +0000553 REPORTER_ASSERT(state.fReporter, 0);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000554 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000555 state.fReporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000556 return result == 0;
557}
558
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000559bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000560#if DEBUG_SHOW_TEST_NAME
caryclark@google.com03610322013-04-18 15:58:21 +0000561 showPathData(path);
562#endif
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000563 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000564 if (!Simplify(path, &out)) {
565 SkDebugf("%s did not expect failure\n", __FUNCTION__);
566 REPORTER_ASSERT(reporter, 0);
567 return false;
568 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000569 SkBitmap bitmap;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000570 int result = comparePaths(reporter, filename, path, out, bitmap);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000571 if (result && gPathStrAssert) {
572 REPORTER_ASSERT(reporter, 0);
573 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000574 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000575 return result == 0;
576}
577
caryclark@google.coma5e55922013-05-07 18:51:31 +0000578#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000579
580SK_DECLARE_STATIC_MUTEX(gTestMutex);
581
caryclark@google.com570863f2013-09-16 15:55:01 +0000582void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
583 const char* testName) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +0000584 SkAutoMutexAcquire ac(gTestMutex);
caryclark@google.com570863f2013-09-16 15:55:01 +0000585 ShowFunctionHeader(testName);
586 showPath(a, "path", true);
587 showPath(b, "pathB", true);
588 ShowOp(shapeOp, "path", "pathB");
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000589}
590#endif
591
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000592#if DEBUG_SHOW_TEST_NAME
593static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
594 SkDebugf("\n");
595 showPathData(a);
596 showOp(shapeOp);
597 showPathData(b);
598}
599#endif
600
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000601static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
602 const SkPathOp shapeOp, const char* testName, bool threaded) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000603#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000604 showName(a, b, shapeOp);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000605#endif
606 SkPath out;
caryclark@google.com66560ca2013-04-26 19:51:16 +0000607 if (!Op(a, b, shapeOp, &out) ) {
608 SkDebugf("%s did not expect failure\n", __FUNCTION__);
609 REPORTER_ASSERT(reporter, 0);
610 return false;
611 }
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000612 if (threaded && !reporter->verbose()) {
613 return true;
614 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000615 SkPath pathOut, scaledPathOut;
616 SkRegion rgnA, rgnB, openClip, rgnOut;
617 openClip.setRect(-16000, -16000, 16000, 16000);
618 rgnA.setPath(a, openClip);
619 rgnB.setPath(b, openClip);
620 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
621 rgnOut.getBoundaryPath(&pathOut);
622
623 SkMatrix scale;
624 scaleMatrix(a, b, scale);
625 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
626 SkPath scaledA, scaledB;
627 scaledA.addPath(a, scale);
628 scaledA.setFillType(a.getFillType());
629 scaledB.addPath(b, scale);
630 scaledB.setFillType(b.getFillType());
631 scaledRgnA.setPath(scaledA, openClip);
632 scaledRgnB.setPath(scaledB, openClip);
633 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
634 scaledRgnOut.getBoundaryPath(&scaledPathOut);
635 SkBitmap bitmap;
636 SkPath scaledOut;
637 scaledOut.addPath(out, scale);
638 scaledOut.setFillType(out.getFillType());
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000639 int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
640 a, b, shapeOp, scale);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000641 if (result && gPathStrAssert) {
642 REPORTER_ASSERT(reporter, 0);
643 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000644 reporter->bumpTestCount();
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000645 return result == 0;
646}
647
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000648bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
649 const SkPathOp shapeOp, const char* testName) {
650 return innerPathOp(reporter, a, b, shapeOp, testName, false);
651}
652
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000653bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
654 const SkPathOp shapeOp, const char* testName) {
655#if DEBUG_SHOW_TEST_NAME
656 showName(a, b, shapeOp);
657#endif
658 SkPath out;
659 if (Op(a, b, shapeOp, &out) ) {
660 SkDebugf("%s test is expected to fail\n", __FUNCTION__);
661 REPORTER_ASSERT(reporter, 0);
662 return false;
663 }
664 return true;
665}
666
caryclark@google.com8d0a5242013-07-16 16:11:16 +0000667bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
668 const SkPathOp shapeOp, const char* testName) {
669 return innerPathOp(reporter, a, b, shapeOp, testName, true);
670}
671
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000672SK_DECLARE_STATIC_MUTEX(gMutex);
673
caryclark@google.com16cfe402013-04-18 18:47:37 +0000674int initializeTests(skiatest::Reporter* reporter, const char* test) {
caryclark@google.coma2bbc6e2013-11-01 17:36:03 +0000675#if 0 // doesn't work yet
676 SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
677 SK_CONF_SET("images.png.suppressDecoderWarnings", true);
678#endif
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000679 if (reporter->verbose()) {
680 SkAutoMutexAcquire lock(gMutex);
681 testName = test;
682 size_t testNameSize = strlen(test);
683 SkFILEStream inFile("../../experimental/Intersection/op.htm");
684 if (inFile.isValid()) {
685 SkTDArray<char> inData;
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000686 inData.setCount((int) inFile.getLength());
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000687 size_t inLen = inData.count();
688 inFile.read(inData.begin(), inLen);
689 inFile.setPath(NULL);
690 char* insert = strstr(inData.begin(), marker);
691 if (insert) {
692 insert += sizeof(marker) - 1;
693 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
694 testNumber = atoi(numLoc) + 1;
695 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000696 }
697 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000698 return reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000699}
700
caryclark@google.com66089e42013-04-10 15:55:37 +0000701void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
702 const char testFunction[] = "testSimplify(path);";
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000703 const char* pathPrefix = NULL;
704 const char* nameSuffix = NULL;
705 if (pathFillType == SkPath::kEvenOdd_FillType) {
706 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
707 nameSuffix = "x";
708 }
caryclark@google.com66089e42013-04-10 15:55:37 +0000709 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
710 outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000711}
712
caryclark@google.com66089e42013-04-10 15:55:37 +0000713void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
714 const char testFunction[] = "testOp(path);";
caryclark@google.comad65a3e2013-04-15 19:13:59 +0000715 SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000716 const char* nameSuffix = opSuffixes[op];
caryclark@google.com66089e42013-04-10 15:55:37 +0000717 SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
718 outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000719}
720
721void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000722 void (*firstTest)(skiatest::Reporter* , const char* filename),
723 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000724 size_t index;
725 if (firstTest) {
726 index = count - 1;
727 while (index > 0 && tests[index].fun != firstTest) {
728 --index;
729 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000730#if DEBUG_SHOW_TEST_NAME
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000731 SkDebugf("<div id=\"%s\">\n", tests[index].str);
732 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000733#endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000734 (*tests[index].fun)(reporter, tests[index].str);
735 if (tests[index].fun == stopTest) {
736 return;
737 }
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000738 }
739 index = reverse ? count - 1 : 0;
740 size_t last = reverse ? 0 : count - 1;
741 do {
742 if (tests[index].fun != firstTest) {
caryclark@google.coma5e55922013-05-07 18:51:31 +0000743 #if DEBUG_SHOW_TEST_NAME
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000744 SkDebugf("<div id=\"%s\">\n", tests[index].str);
745 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
746 #endif
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000747 (*tests[index].fun)(reporter, tests[index].str);
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000748 }
749 if (tests[index].fun == stopTest) {
750 SkDebugf("lastTest\n");
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000751 break;
caryclark@google.com818b0cc2013-04-08 11:50:46 +0000752 }
753 if (index == last) {
754 break;
755 }
756 index += reverse ? -1 : 1;
757 } while (true);
758}