blob: f721aa98202a1add306eba358dd1c7db3d2eb007 [file] [log] [blame]
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001#include "EdgeWalker_Test.h"
2#include "Intersection_Tests.h"
caryclark@google.com198e0542012-03-30 18:47:02 +00003#include "SkBitmap.h"
4
5static SkBitmap bitmap;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00006
caryclark@google.com2e7f4c82012-03-20 21:11:59 +00007static void testSimplifyCoincidentInner() {
8 SkPath path, out;
9 path.setFillType(SkPath::kWinding_FillType);
10 path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction);
11 path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
12 path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
caryclark@google.com198e0542012-03-30 18:47:02 +000013 testSimplify(path, true, out, bitmap);
caryclark@google.com2e7f4c82012-03-20 21:11:59 +000014}
15
caryclark@google.comcd4421d2012-03-01 19:16:31 +000016static void testSimplifyCoincidentVertical() {
17 SkPath path, out;
18 path.setFillType(SkPath::kWinding_FillType);
19 path.addRect(10, 10, 30, 30);
20 path.addRect(10, 30, 30, 40);
21 simplify(path, true, out);
22 SkRect rect;
23 if (!out.isRect(&rect)) {
24 SkDebugf("%s expected rect\n", __FUNCTION__);
25 }
26 if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
27 SkDebugf("%s expected union\n", __FUNCTION__);
28 }
29}
30
31static void testSimplifyCoincidentHorizontal() {
32 SkPath path, out;
33 path.setFillType(SkPath::kWinding_FillType);
34 path.addRect(10, 10, 30, 30);
35 path.addRect(30, 10, 40, 30);
36 simplify(path, true, out);
37 SkRect rect;
38 if (!out.isRect(&rect)) {
39 SkDebugf("%s expected rect\n", __FUNCTION__);
40 }
41 if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
42 SkDebugf("%s expected union\n", __FUNCTION__);
43 }
44}
45
46static void testSimplifyMulti() {
47 SkPath path, out;
48 path.setFillType(SkPath::kWinding_FillType);
49 path.addRect(10, 10, 30, 30);
50 path.addRect(20, 20, 40, 40);
51 simplify(path, true, out);
52 SkPath expected;
53 expected.setFillType(SkPath::kEvenOdd_FillType);
54 expected.moveTo(10,10); // two cutout corners
55 expected.lineTo(10,30);
56 expected.lineTo(20,30);
57 expected.lineTo(20,40);
58 expected.lineTo(40,40);
59 expected.lineTo(40,20);
60 expected.lineTo(30,20);
61 expected.lineTo(30,10);
62 expected.lineTo(10,10);
63 expected.close();
64 if (out != expected) {
65 SkDebugf("%s expected equal\n", __FUNCTION__);
66 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000067
caryclark@google.comcd4421d2012-03-01 19:16:31 +000068 path = out;
69 path.addRect(30, 10, 40, 20);
70 path.addRect(10, 30, 20, 40);
71 simplify(path, true, out);
72 SkRect rect;
73 if (!out.isRect(&rect)) {
74 SkDebugf("%s expected rect\n", __FUNCTION__);
75 }
76 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
77 SkDebugf("%s expected union\n", __FUNCTION__);
78 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000079
caryclark@google.comcd4421d2012-03-01 19:16:31 +000080 path = out;
81 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
82 simplify(path, true, out);
83 if (!out.isEmpty()) {
84 SkDebugf("%s expected empty\n", __FUNCTION__);
85 }
86}
87
88static void testSimplifyAddL() {
89 SkPath path, out;
90 path.moveTo(10,10); // 'L' shape
91 path.lineTo(10,40);
92 path.lineTo(40,40);
93 path.lineTo(40,20);
94 path.lineTo(30,20);
95 path.lineTo(30,10);
96 path.lineTo(10,10);
97 path.close();
98 path.addRect(30, 10, 40, 20); // missing notch of 'L'
99 simplify(path, true, out);
100 SkRect rect;
101 if (!out.isRect(&rect)) {
102 SkDebugf("%s expected rect\n", __FUNCTION__);
103 }
104 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
105 SkDebugf("%s expected union\n", __FUNCTION__);
106 }
107}
108
109static void testSimplifyCoincidentCCW() {
110 SkPath path, out;
111 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
112 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
113 simplify(path, true, out);
114 SkRect rect;
115 if (!out.isRect(&rect)) {
116 SkDebugf("%s expected rect\n", __FUNCTION__);
117 }
118 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
119 SkDebugf("%s expected union\n", __FUNCTION__);
120 }
121}
122
123static void testSimplifyCoincidentCW() {
124 SkPath path, out;
125 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
126 path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
127 simplify(path, true, out);
128 if (!out.isEmpty()) {
129 SkDebugf("%s expected empty\n", __FUNCTION__);
130 }
131}
132
133static void testSimplifyCorner() {
134 SkPath path, out;
135 path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
136 path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
137 simplify(path, true, out);
138 SkTDArray<SkRect> boundsArray;
139 contourBounds(out, boundsArray);
140 if (boundsArray.count() != 2) {
141 SkDebugf("%s expected 2 contours\n", __FUNCTION__);
142 return;
143 }
144 SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
145 SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
146 if (boundsArray[0] != one && boundsArray[0] != two
147 || boundsArray[1] != one && boundsArray[1] != two) {
148 SkDebugf("%s expected match\n", __FUNCTION__);
149 }
150}
151
152static void testSimplifyDiagonal() {
153 SkRect rect2 = SkRect::MakeXYWH(10, 10, 10, 10);
154 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
155 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
156 for (int x = 0; x <= 20; x += 20) {
157 for (int y = 0; y <= 20; y += 20) {
158 SkPath path, out;
159 SkRect rect1 = SkRect::MakeXYWH(x, y, 10, 10);
160 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
161 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
162 simplify(path, true, out);
163 SkPath::Iter iter(out, false);
164 SkPoint pts[4], lastLine[2];
165 SkPath::Verb verb;
166 SkRect bounds[2];
167 bounds[0].setEmpty();
168 bounds[1].setEmpty();
169 SkRect* boundsPtr = bounds;
170 int count = 0, segments = 0;
171 bool lastLineSet = false;
172 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
173 switch (verb) {
174 case SkPath::kMove_Verb:
175 if (!boundsPtr->isEmpty()) {
176 SkASSERT(boundsPtr == bounds);
177 ++boundsPtr;
178 }
179 boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
180 count = 0;
181 lastLineSet = false;
182 break;
183 case SkPath::kLine_Verb:
184 if (lastLineSet) {
185 SkASSERT((lastLine[1].fX - lastLine[0].fX) *
186 (pts[1].fY - lastLine[0].fY) !=
187 (lastLine[1].fY - lastLine[0].fY) *
188 (pts[1].fX - lastLine[0].fX));
189 }
190 lastLineSet = true;
191 lastLine[0] = pts[0];
192 lastLine[1] = pts[1];
193 count = 1;
194 ++segments;
195 break;
196 case SkPath::kClose_Verb:
197 count = 0;
198 break;
199 default:
200 SkDEBUGFAIL("bad verb");
201 return;
202 }
203 for (int i = 1; i <= count; ++i) {
204 boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
205 }
206 }
207 if (boundsPtr != bounds) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000208 SkASSERT((bounds[0] == rect1 || bounds[1] == rect1)
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000209 && (bounds[0] == rect2 || bounds[1] == rect2));
210 } else {
211 SkASSERT(segments == 8);
212 }
213 }
214 }
215 }
216 }
217}
218
219static void assertOneContour(const SkPath& out, bool edge, bool extend) {
220 SkPath::Iter iter(out, false);
221 SkPoint pts[4];
222 SkPath::Verb verb;
223 SkRect bounds;
224 bounds.setEmpty();
225 int count = 0;
226 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
227 switch (verb) {
228 case SkPath::kMove_Verb:
229 SkASSERT(count == 0);
230 break;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000231 case SkPath::kLine_Verb:
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000232 SkASSERT(pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY);
233 ++count;
234 break;
235 case SkPath::kClose_Verb:
236 break;
237 default:
238 SkDEBUGFAIL("bad verb");
239 return;
240 }
241 }
242 SkASSERT(count == (extend ? 4 : edge ? 6 : 8));
243}
244
245static void testSimplifyCoincident() {
246 // outside to inside, outside to right, outside to outside
247 // left to inside, left to right, left to outside
248 // inside to right, inside to outside
249 // repeat above for left, right, bottom
250 SkScalar start[] = { 0, 10, 20 };
251 size_t startCount = sizeof(start) / sizeof(start[0]);
252 SkScalar stop[] = { 30, 40, 50 };
253 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
254 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
255 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
256 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
257 for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
258 for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
259 bool extend = start[startIndex] == rect2.fLeft && stop[stopIndex] == rect2.fRight;
260 bool edge = start[startIndex] == rect2.fLeft || stop[stopIndex] == rect2.fRight;
261 SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 10);
262 SkPath path, out;
263 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
264 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
265 simplify(path, true, out);
266 assertOneContour(out, edge, extend);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000267
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000268 path.reset();
269 rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 50);
270 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
271 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
272 simplify(path, true, out);
273 assertOneContour(out, edge, extend);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000274
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000275 path.reset();
276 rect1 = SkRect::MakeLTRB(0, start[startIndex], 10, stop[stopIndex]);
277 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
278 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
279 simplify(path, true, out);
280 assertOneContour(out, edge, extend);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000281
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000282 path.reset();
283 rect1 = SkRect::MakeLTRB(40, start[startIndex], 50, stop[stopIndex]);
284 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
285 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
286 simplify(path, true, out);
287 assertOneContour(out, edge, extend);
288 }
289 }
290 }
291 }
292}
293
294static void testSimplifyOverlap() {
295 SkScalar start[] = { 0, 10, 20 };
296 size_t startCount = sizeof(start) / sizeof(start[0]);
297 SkScalar stop[] = { 30, 40, 50 };
298 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
299 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
300 for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
301 for (size_t lefty = 0; lefty < startCount; ++lefty) {
302 for (size_t righty = 0; righty < stopCount; ++righty) {
303 for (size_t toppy = 0; toppy < startCount; ++toppy) {
304 for (size_t botty = 0; botty < stopCount; ++botty) {
305 SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
306 stop[righty], stop[botty]);
307 SkPath path, out;
308 path.addRect(rect1, static_cast<SkPath::Direction>(dir));
309 path.addRect(rect2, static_cast<SkPath::Direction>(dir));
caryclark@google.com198e0542012-03-30 18:47:02 +0000310 testSimplify(path, true, out, bitmap);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000311 }
312 }
313 }
314 }
315 }
316}
317
318static void testSimplifyOverlapTiny() {
319 SkScalar start[] = { 0, 1, 2 };
320 size_t startCount = sizeof(start) / sizeof(start[0]);
321 SkScalar stop[] = { 3, 4, 5 };
322 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
323 SkRect rect2 = SkRect::MakeXYWH(1, 1, 3, 3);
324 for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
325 for (size_t lefty = 0; lefty < startCount; ++lefty) {
326 for (size_t righty = 0; righty < stopCount; ++righty) {
327 for (size_t toppy = 0; toppy < startCount; ++toppy) {
328 for (size_t botty = 0; botty < stopCount; ++botty) {
329 SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
330 stop[righty], stop[botty]);
331 SkPath path, out;
332 path.addRect(rect1, static_cast<SkPath::Direction>(dir));
333 path.addRect(rect2, static_cast<SkPath::Direction>(dir));
334 simplify(path, true, out);
335 comparePathsTiny(path, out);
336 }
337 }
338 }
339 }
340 }
341}
342
343static void testSimplifyDegenerate() {
344 SkScalar start[] = { 0, 10, 20 };
345 size_t startCount = sizeof(start) / sizeof(start[0]);
346 SkScalar stop[] = { 30, 40, 50 };
347 size_t stopCount = sizeof(stop) / sizeof(stop[0]);
348 SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
349 for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
350 for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
351 for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
352 for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
353 SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 0);
354 SkPath path, out;
355 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
356 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
357 simplify(path, true, out);
358 SkRect rect;
359 if (!out.isRect(&rect)) {
360 SkDebugf("%s 1 expected rect\n", __FUNCTION__);
361 }
362 if (rect != rect2) {
363 SkDebugf("%s 1 expected union\n", __FUNCTION__);
364 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000365
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000366 path.reset();
367 rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 40);
368 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
369 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
370 simplify(path, true, out);
371 if (!out.isRect(&rect)) {
372 SkDebugf("%s 2 expected rect\n", __FUNCTION__);
373 }
374 if (rect != rect2) {
375 SkDebugf("%s 2 expected union\n", __FUNCTION__);
376 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000377
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000378 path.reset();
379 rect1 = SkRect::MakeLTRB(0, start[startIndex], 0, stop[stopIndex]);
380 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
381 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
382 simplify(path, true, out);
383 if (!out.isRect(&rect)) {
384 SkDebugf("%s 3 expected rect\n", __FUNCTION__);
385 }
386 if (rect != rect2) {
387 SkDebugf("%s 3 expected union\n", __FUNCTION__);
388 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000389
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000390 path.reset();
391 rect1 = SkRect::MakeLTRB(40, start[startIndex], 40, stop[stopIndex]);
392 path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
393 path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
394 simplify(path, true, out);
395 if (!out.isRect(&rect)) {
396 SkDebugf("%s 4 expected rect\n", __FUNCTION__);
397 }
398 if (rect != rect2) {
399 SkDebugf("%s 4 expected union\n", __FUNCTION__);
400 }
401 }
402 }
403 }
404 }
405}
406
407static void testSimplifyDegenerate1() {
408 SkPath path, out;
409 path.setFillType(SkPath::kWinding_FillType);
410 path.addRect( 0, 0, 0, 30);
411 path.addRect(10, 10, 40, 40);
412 simplify(path, true, out);
413 SkRect rect;
414 if (!out.isRect(&rect)) {
415 SkDebugf("%s expected rect\n", __FUNCTION__);
416 }
417 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
418 SkDebugf("%s expected union\n", __FUNCTION__);
419 }
420}
421
422static void (*simplifyTests[])() = {
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000423 testSimplifyCoincidentInner,
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000424 testSimplifyOverlapTiny,
425 testSimplifyDegenerate1,
426 testSimplifyCorner,
427 testSimplifyDegenerate,
428 testSimplifyOverlap,
429 testSimplifyDiagonal,
430 testSimplifyCoincident,
431 testSimplifyCoincidentCW,
432 testSimplifyCoincidentCCW,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000433 testSimplifyCoincidentVertical,
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000434 testSimplifyCoincidentHorizontal,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000435 testSimplifyAddL,
436 testSimplifyMulti,
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000437};
438
439static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
440
441static void (*firstTest)() = 0;
442
443void SimplifyRectangularPaths_Test() {
444 size_t index = 0;
445 if (firstTest) {
446 while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
447 ++index;
448 }
449 }
450 for ( ; index < simplifyTestsCount; ++index) {
451 if (simplifyTests[index] == testSimplifyCorner) {
452 // testSimplifyCorner fails because it expects two contours, where
453 // only one is returned. Both results are reasonable, but if two
454 // contours are desirable, or if we provide an option to choose
455 // between longer contours and more contours, turn this back on. For
456 // the moment, testSimplifyDiagonal also checks the test case, and
457 // permits either two rects or one non-crossing poly as valid
458 // unreported results.
459 continue;
460 }
461 (*simplifyTests[index])();
462 }
463}
464