blob: 0053640e15b1301d011f8ad4acb71e035e68f97a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com3abec1d2009-03-02 05:36:20 +00008#include "Test.h"
reed@google.com55b5f4b2011-09-07 12:23:41 +00009#include "SkPaint.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000010#include "SkPath.h"
reed@google.com04863fa2011-05-15 04:08:24 +000011#include "SkParse.h"
reed@google.com3e71a882012-01-10 18:44:37 +000012#include "SkParsePath.h"
reed@google.com8b06f1a2012-05-29 12:03:46 +000013#include "SkPathEffect.h"
schenney@chromium.org6630d8d2012-01-04 21:05:51 +000014#include "SkRandom.h"
reed@google.com53effc52011-09-21 19:05:12 +000015#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000016#include "SkSize.h"
reed@google.com53effc52011-09-21 19:05:12 +000017#include "SkWriter32.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000018
tomhudson@google.comed02c4d2012-08-10 14:10:45 +000019// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
20//
21static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
22 SkPath path;
23 path.quadTo(157, 366, 286, 208);
24 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
25
26 SkMatrix matrix;
27 matrix.setScale(1000*1000, 1000*1000);
28
29 // Be sure that path::transform correctly updates isFinite and the bounds
30 // if the transformation overflows. The previous bug was that isFinite was
31 // set to true in this case, but the bounds were not set to empty (which
32 // they should be).
33 while (path.isFinite()) {
34 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
35 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
36 path.transform(matrix);
37 }
38 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
39
40 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
41 path.transform(matrix);
42 // we need to still be non-finite
43 REPORTER_ASSERT(reporter, !path.isFinite());
44 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
45}
46
reed@google.com0bb18bb2012-07-26 15:20:36 +000047static void test_rect_isfinite(skiatest::Reporter* reporter) {
48 const SkScalar inf = SK_ScalarInfinity;
49 const SkScalar nan = SK_ScalarNaN;
50
51 SkRect r;
52 r.setEmpty();
53 REPORTER_ASSERT(reporter, r.isFinite());
54 r.set(0, 0, inf, -inf);
55 REPORTER_ASSERT(reporter, !r.isFinite());
56 r.set(0, 0, nan, 0);
57 REPORTER_ASSERT(reporter, !r.isFinite());
58
59 SkPoint pts[] = {
60 { 0, 0 },
61 { SK_Scalar1, 0 },
62 { 0, SK_Scalar1 },
63 };
64
65 bool isFine = r.setBoundsCheck(pts, 3);
66 REPORTER_ASSERT(reporter, isFine);
67 REPORTER_ASSERT(reporter, !r.isEmpty());
68
69 pts[1].set(inf, 0);
70 isFine = r.setBoundsCheck(pts, 3);
71 REPORTER_ASSERT(reporter, !isFine);
72 REPORTER_ASSERT(reporter, r.isEmpty());
73
74 pts[1].set(nan, 0);
75 isFine = r.setBoundsCheck(pts, 3);
76 REPORTER_ASSERT(reporter, !isFine);
77 REPORTER_ASSERT(reporter, r.isEmpty());
78}
79
80static void test_path_isfinite(skiatest::Reporter* reporter) {
81 const SkScalar inf = SK_ScalarInfinity;
82 const SkScalar nan = SK_ScalarNaN;
83
84 SkPath path;
85 REPORTER_ASSERT(reporter, path.isFinite());
86
87 path.reset();
88 REPORTER_ASSERT(reporter, path.isFinite());
89
90 path.reset();
91 path.moveTo(SK_Scalar1, 0);
92 REPORTER_ASSERT(reporter, path.isFinite());
93
94 path.reset();
95 path.moveTo(inf, -inf);
96 REPORTER_ASSERT(reporter, !path.isFinite());
97
98 path.reset();
99 path.moveTo(nan, 0);
100 REPORTER_ASSERT(reporter, !path.isFinite());
101}
102
103static void test_isfinite(skiatest::Reporter* reporter) {
104 test_rect_isfinite(reporter);
105 test_path_isfinite(reporter);
106}
107
reed@google.com744faba2012-05-29 19:54:52 +0000108// assert that we always
109// start with a moveTo
110// only have 1 moveTo
111// only have Lines after that
112// end with a single close
113// only have (at most) 1 close
114//
115static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
116 const SkPoint srcPts[], int count, bool expectClose) {
117 SkPath::RawIter iter(path);
118 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000119
120 bool firstTime = true;
121 bool foundClose = false;
122 for (;;) {
123 switch (iter.next(pts)) {
124 case SkPath::kMove_Verb:
125 REPORTER_ASSERT(reporter, firstTime);
126 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
127 srcPts++;
128 firstTime = false;
129 break;
130 case SkPath::kLine_Verb:
131 REPORTER_ASSERT(reporter, !firstTime);
132 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
133 srcPts++;
134 break;
135 case SkPath::kQuad_Verb:
136 REPORTER_ASSERT(reporter, !"unexpected quad verb");
137 break;
138 case SkPath::kCubic_Verb:
139 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
140 break;
141 case SkPath::kClose_Verb:
142 REPORTER_ASSERT(reporter, !firstTime);
143 REPORTER_ASSERT(reporter, !foundClose);
144 REPORTER_ASSERT(reporter, expectClose);
145 foundClose = true;
146 break;
147 case SkPath::kDone_Verb:
148 goto DONE;
149 }
150 }
151DONE:
152 REPORTER_ASSERT(reporter, foundClose == expectClose);
153}
154
155static void test_addPoly(skiatest::Reporter* reporter) {
156 SkPoint pts[32];
157 SkRandom rand;
158
159 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
160 pts[i].fX = rand.nextSScalar1();
161 pts[i].fY = rand.nextSScalar1();
162 }
163
164 for (int doClose = 0; doClose <= 1; ++doClose) {
165 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
166 SkPath path;
167 path.addPoly(pts, count, SkToBool(doClose));
168 test_poly(reporter, path, pts, count, SkToBool(doClose));
169 }
170 }
171}
172
reed@google.com8b06f1a2012-05-29 12:03:46 +0000173static void test_strokerec(skiatest::Reporter* reporter) {
174 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
175 REPORTER_ASSERT(reporter, rec.isFillStyle());
176
177 rec.setHairlineStyle();
178 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
179
180 rec.setStrokeStyle(SK_Scalar1, false);
181 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
182
183 rec.setStrokeStyle(SK_Scalar1, true);
184 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
185
186 rec.setStrokeStyle(0, false);
187 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
188
189 rec.setStrokeStyle(0, true);
190 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
191}
192
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000193/**
194 * cheapIsDirection can take a shortcut when a path is marked convex.
195 * This function ensures that we always test cheapIsDirection when the path
196 * is flagged with unknown convexity status.
197 */
198static void check_direction(SkPath* path,
199 SkPath::Direction expectedDir,
200 skiatest::Reporter* reporter) {
201 if (SkPath::kConvex_Convexity == path->getConvexity()) {
202 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
203 path->setConvexity(SkPath::kUnknown_Convexity);
204 }
205 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
206}
207
reed@google.com3e71a882012-01-10 18:44:37 +0000208static void test_direction(skiatest::Reporter* reporter) {
209 size_t i;
210 SkPath path;
211 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
212 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
213 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
214
215 static const char* gDegen[] = {
216 "M 10 10",
217 "M 10 10 M 20 20",
218 "M 10 10 L 20 20",
219 "M 10 10 L 10 10 L 10 10",
220 "M 10 10 Q 10 10 10 10",
221 "M 10 10 C 10 10 10 10 10 10",
222 };
223 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
224 path.reset();
225 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
226 REPORTER_ASSERT(reporter, valid);
227 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
228 }
229
230 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000231 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000232 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000233 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000234 // rect with top two corners replaced by cubics with identical middle
235 // control points
236 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000237 };
238 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
239 path.reset();
240 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
241 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000242 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000243 }
244
245 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000246 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000247 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000248 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000249 // rect with top two corners replaced by cubics with identical middle
250 // control points
251 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000252 };
253 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
254 path.reset();
255 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
256 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000257 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000258 }
reed@google.comac8543f2012-01-30 20:51:25 +0000259
260 // Test two donuts, each wound a different direction. Only the outer contour
261 // determines the cheap direction
262 path.reset();
263 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
264 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000265 check_direction(&path, SkPath::kCW_Direction, reporter);
266
reed@google.comac8543f2012-01-30 20:51:25 +0000267 path.reset();
268 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
269 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000270 check_direction(&path, SkPath::kCCW_Direction, reporter);
271
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000272#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000273 // triangle with one point really far from the origin.
274 path.reset();
275 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000276 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
277 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
278 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
279 check_direction(&path, SkPath::kCCW_Direction, reporter);
280#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000281}
282
reed@google.comffdb0182011-11-14 19:29:14 +0000283static void add_rect(SkPath* path, const SkRect& r) {
284 path->moveTo(r.fLeft, r.fTop);
285 path->lineTo(r.fRight, r.fTop);
286 path->lineTo(r.fRight, r.fBottom);
287 path->lineTo(r.fLeft, r.fBottom);
288 path->close();
289}
290
291static void test_bounds(skiatest::Reporter* reporter) {
292 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000293 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
294 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
295 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
296 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000297 };
298
299 SkPath path0, path1;
300 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
301 path0.addRect(rects[i]);
302 add_rect(&path1, rects[i]);
303 }
304
305 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
306}
307
reed@google.com55b5f4b2011-09-07 12:23:41 +0000308static void stroke_cubic(const SkPoint pts[4]) {
309 SkPath path;
310 path.moveTo(pts[0]);
311 path.cubicTo(pts[1], pts[2], pts[3]);
312
313 SkPaint paint;
314 paint.setStyle(SkPaint::kStroke_Style);
315 paint.setStrokeWidth(SK_Scalar1 * 2);
316
317 SkPath fill;
318 paint.getFillPath(path, &fill);
319}
320
321// just ensure this can run w/o any SkASSERTS firing in the debug build
322// we used to assert due to differences in how we determine a degenerate vector
323// but that was fixed with the introduction of SkPoint::CanNormalize
324static void stroke_tiny_cubic() {
325 SkPoint p0[] = {
326 { 372.0f, 92.0f },
327 { 372.0f, 92.0f },
328 { 372.0f, 92.0f },
329 { 372.0f, 92.0f },
330 };
331
332 stroke_cubic(p0);
333
334 SkPoint p1[] = {
335 { 372.0f, 92.0f },
336 { 372.0007f, 92.000755f },
337 { 371.99927f, 92.003922f },
338 { 371.99826f, 92.003899f },
339 };
340
341 stroke_cubic(p1);
342}
343
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000344static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
345 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000346 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000347 SkPoint mv;
348 SkPoint pts[4];
349 SkPath::Verb v;
350 int nMT = 0;
351 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000352 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000353 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
354 switch (v) {
355 case SkPath::kMove_Verb:
356 mv = pts[0];
357 ++nMT;
358 break;
359 case SkPath::kClose_Verb:
360 REPORTER_ASSERT(reporter, mv == pts[0]);
361 ++nCL;
362 break;
363 default:
364 break;
365 }
366 }
367 // if we force a close on the interator we should have a close
368 // for every moveTo
369 REPORTER_ASSERT(reporter, !i || nMT == nCL);
370 }
371}
372
373static void test_close(skiatest::Reporter* reporter) {
374 SkPath closePt;
375 closePt.moveTo(0, 0);
376 closePt.close();
377 check_close(reporter, closePt);
378
379 SkPath openPt;
380 openPt.moveTo(0, 0);
381 check_close(reporter, openPt);
382
383 SkPath empty;
384 check_close(reporter, empty);
385 empty.close();
386 check_close(reporter, empty);
387
388 SkPath rect;
389 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
390 check_close(reporter, rect);
391 rect.close();
392 check_close(reporter, rect);
393
394 SkPath quad;
395 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
396 check_close(reporter, quad);
397 quad.close();
398 check_close(reporter, quad);
399
400 SkPath cubic;
401 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
402 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
403 check_close(reporter, cubic);
404 cubic.close();
405 check_close(reporter, cubic);
406
407 SkPath line;
408 line.moveTo(SK_Scalar1, SK_Scalar1);
409 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
410 check_close(reporter, line);
411 line.close();
412 check_close(reporter, line);
413
414 SkPath rect2;
415 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
416 rect2.close();
417 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
418 check_close(reporter, rect2);
419 rect2.close();
420 check_close(reporter, rect2);
421
422 SkPath oval3;
423 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
424 oval3.close();
425 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
426 check_close(reporter, oval3);
427 oval3.close();
428 check_close(reporter, oval3);
429
430 SkPath moves;
431 moves.moveTo(SK_Scalar1, SK_Scalar1);
432 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
433 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
434 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
435 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000436
437 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000438}
439
reed@google.com7c424812011-05-15 04:38:34 +0000440static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
441 SkPath::Convexity expected) {
442 SkPath::Convexity c = SkPath::ComputeConvexity(path);
443 REPORTER_ASSERT(reporter, c == expected);
444}
445
446static void test_convexity2(skiatest::Reporter* reporter) {
447 SkPath pt;
448 pt.moveTo(0, 0);
449 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000450 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000451
452 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000453 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
454 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000455 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000456 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000457
458 SkPath triLeft;
459 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000460 triLeft.lineTo(SK_Scalar1, 0);
461 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000462 triLeft.close();
463 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
464
465 SkPath triRight;
466 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000467 triRight.lineTo(-SK_Scalar1, 0);
468 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000469 triRight.close();
470 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
471
472 SkPath square;
473 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000474 square.lineTo(SK_Scalar1, 0);
475 square.lineTo(SK_Scalar1, SK_Scalar1);
476 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000477 square.close();
478 check_convexity(reporter, square, SkPath::kConvex_Convexity);
479
480 SkPath redundantSquare;
481 redundantSquare.moveTo(0, 0);
482 redundantSquare.lineTo(0, 0);
483 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000484 redundantSquare.lineTo(SK_Scalar1, 0);
485 redundantSquare.lineTo(SK_Scalar1, 0);
486 redundantSquare.lineTo(SK_Scalar1, 0);
487 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
488 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
489 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
490 redundantSquare.lineTo(0, SK_Scalar1);
491 redundantSquare.lineTo(0, SK_Scalar1);
492 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000493 redundantSquare.close();
494 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
495
496 SkPath bowTie;
497 bowTie.moveTo(0, 0);
498 bowTie.lineTo(0, 0);
499 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000500 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
501 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
502 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
503 bowTie.lineTo(SK_Scalar1, 0);
504 bowTie.lineTo(SK_Scalar1, 0);
505 bowTie.lineTo(SK_Scalar1, 0);
506 bowTie.lineTo(0, SK_Scalar1);
507 bowTie.lineTo(0, SK_Scalar1);
508 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000509 bowTie.close();
510 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
511
512 SkPath spiral;
513 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000514 spiral.lineTo(100*SK_Scalar1, 0);
515 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
516 spiral.lineTo(0, 100*SK_Scalar1);
517 spiral.lineTo(0, 50*SK_Scalar1);
518 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
519 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000520 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000521 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000522
523 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000524 dent.moveTo(0, 0);
525 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
526 dent.lineTo(0, 100*SK_Scalar1);
527 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
528 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000529 dent.close();
530 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
531}
532
reed@android.com6b82d1a2009-06-03 02:35:01 +0000533static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
534 const SkRect& bounds) {
535 REPORTER_ASSERT(reporter, p.isConvex());
536 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000537
reed@android.com6b82d1a2009-06-03 02:35:01 +0000538 SkPath p2(p);
539 REPORTER_ASSERT(reporter, p2.isConvex());
540 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
541
542 SkPath other;
543 other.swap(p2);
544 REPORTER_ASSERT(reporter, other.isConvex());
545 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
546}
547
reed@google.com04863fa2011-05-15 04:08:24 +0000548static void setFromString(SkPath* path, const char str[]) {
549 bool first = true;
550 while (str) {
551 SkScalar x, y;
552 str = SkParse::FindScalar(str, &x);
553 if (NULL == str) {
554 break;
555 }
556 str = SkParse::FindScalar(str, &y);
557 SkASSERT(str);
558 if (first) {
559 path->moveTo(x, y);
560 first = false;
561 } else {
562 path->lineTo(x, y);
563 }
564 }
565}
566
567static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000568 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
569 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
570
571 SkPath path;
572
reed@google.comb54455e2011-05-16 14:16:04 +0000573 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000574 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000575 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000576 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000577 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
578 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000579 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000580 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000581 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000582 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000583 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000584 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000585 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000586
587 static const struct {
588 const char* fPathStr;
589 SkPath::Convexity fExpectedConvexity;
590 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000591 { "", SkPath::kConvex_Convexity },
592 { "0 0", SkPath::kConvex_Convexity },
593 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000594 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000595 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
596 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
597 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
598 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
599 };
600
601 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
602 SkPath path;
603 setFromString(&path, gRec[i].fPathStr);
604 SkPath::Convexity c = SkPath::ComputeConvexity(path);
605 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
606 }
607}
608
reed@google.com7e6c4d12012-05-10 14:05:43 +0000609static void test_isLine(skiatest::Reporter* reporter) {
610 SkPath path;
611 SkPoint pts[2];
612 const SkScalar value = SkIntToScalar(5);
613
614 REPORTER_ASSERT(reporter, !path.isLine(NULL));
615
616 // set some non-zero values
617 pts[0].set(value, value);
618 pts[1].set(value, value);
619 REPORTER_ASSERT(reporter, !path.isLine(pts));
620 // check that pts was untouched
621 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
622 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
623
624 const SkScalar moveX = SkIntToScalar(1);
625 const SkScalar moveY = SkIntToScalar(2);
626 SkASSERT(value != moveX && value != moveY);
627
628 path.moveTo(moveX, moveY);
629 REPORTER_ASSERT(reporter, !path.isLine(NULL));
630 REPORTER_ASSERT(reporter, !path.isLine(pts));
631 // check that pts was untouched
632 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
633 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
634
635 const SkScalar lineX = SkIntToScalar(2);
636 const SkScalar lineY = SkIntToScalar(2);
637 SkASSERT(value != lineX && value != lineY);
638
639 path.lineTo(lineX, lineY);
640 REPORTER_ASSERT(reporter, path.isLine(NULL));
641
642 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
643 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
644 REPORTER_ASSERT(reporter, path.isLine(pts));
645 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
646 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
647
648 path.lineTo(0, 0); // too many points/verbs
649 REPORTER_ASSERT(reporter, !path.isLine(NULL));
650 REPORTER_ASSERT(reporter, !path.isLine(pts));
651 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
652 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
653}
654
caryclark@google.comf1316942011-07-26 19:54:45 +0000655// Simple isRect test is inline TestPath, below.
656// test_isRect provides more extensive testing.
657static void test_isRect(skiatest::Reporter* reporter) {
658 // passing tests (all moveTo / lineTo...
659 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
660 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
661 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
662 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
663 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
664 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
665 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
666 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
667 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
668 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
669 {1, 0}, {.5f, 0}};
670 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
671 {0, 1}, {0, .5f}};
672 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
673 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
674 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
675
676 // failing tests
677 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
678 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
679 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
680 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
681 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
682 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
683 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
684 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
685
686 // failing, no close
687 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
688 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
689
690 size_t testLen[] = {
691 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
692 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
693 sizeof(rd), sizeof(re),
694 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
695 sizeof(f7), sizeof(f8),
696 sizeof(c1), sizeof(c2)
697 };
698 SkPoint* tests[] = {
699 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
700 f1, f2, f3, f4, f5, f6, f7, f8,
701 c1, c2
702 };
703 SkPoint* lastPass = re;
704 SkPoint* lastClose = f8;
705 bool fail = false;
706 bool close = true;
707 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
708 size_t index;
709 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
710 SkPath path;
711 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
712 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
713 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
714 }
715 if (close) {
716 path.close();
717 }
718 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
719 if (tests[testIndex] == lastPass) {
720 fail = true;
721 }
722 if (tests[testIndex] == lastClose) {
723 close = false;
724 }
725 }
726
727 // fail, close then line
728 SkPath path1;
729 path1.moveTo(r1[0].fX, r1[0].fY);
730 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
731 path1.lineTo(r1[index].fX, r1[index].fY);
732 }
733 path1.close();
734 path1.lineTo(1, 0);
735 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
736
737 // fail, move in the middle
738 path1.reset();
739 path1.moveTo(r1[0].fX, r1[0].fY);
740 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
741 if (index == 2) {
742 path1.moveTo(1, .5f);
743 }
744 path1.lineTo(r1[index].fX, r1[index].fY);
745 }
746 path1.close();
747 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
748
749 // fail, move on the edge
750 path1.reset();
751 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
752 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
753 path1.lineTo(r1[index].fX, r1[index].fY);
754 }
755 path1.close();
756 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
757
758 // fail, quad
759 path1.reset();
760 path1.moveTo(r1[0].fX, r1[0].fY);
761 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
762 if (index == 2) {
763 path1.quadTo(1, .5f, 1, .5f);
764 }
765 path1.lineTo(r1[index].fX, r1[index].fY);
766 }
767 path1.close();
768 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
769
770 // fail, cubic
771 path1.reset();
772 path1.moveTo(r1[0].fX, r1[0].fY);
773 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
774 if (index == 2) {
775 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
776 }
777 path1.lineTo(r1[index].fX, r1[index].fY);
778 }
779 path1.close();
780 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
781}
782
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000783static void write_and_read_back(skiatest::Reporter* reporter,
784 const SkPath& p) {
785 SkWriter32 writer(100);
786 writer.writePath(p);
787 size_t size = writer.size();
788 SkAutoMalloc storage(size);
789 writer.flatten(storage.get());
790 SkReader32 reader(storage.get(), size);
791
792 SkPath readBack;
793 REPORTER_ASSERT(reporter, readBack != p);
794 reader.readPath(&readBack);
795 REPORTER_ASSERT(reporter, readBack == p);
796
797 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
798 p.getConvexityOrUnknown());
799
800 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
801
802 const SkRect& origBounds = p.getBounds();
803 const SkRect& readBackBounds = readBack.getBounds();
804
805 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
806}
807
reed@google.com53effc52011-09-21 19:05:12 +0000808static void test_flattening(skiatest::Reporter* reporter) {
809 SkPath p;
810
811 static const SkPoint pts[] = {
812 { 0, 0 },
813 { SkIntToScalar(10), SkIntToScalar(10) },
814 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
815 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
816 };
817 p.moveTo(pts[0]);
818 p.lineTo(pts[1]);
819 p.quadTo(pts[2], pts[3]);
820 p.cubicTo(pts[4], pts[5], pts[6]);
821
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000822 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000823
824 // create a buffer that should be much larger than the path so we don't
825 // kill our stack if writer goes too far.
826 char buffer[1024];
827 uint32_t size1 = p.writeToMemory(NULL);
828 uint32_t size2 = p.writeToMemory(buffer);
829 REPORTER_ASSERT(reporter, size1 == size2);
830
831 SkPath p2;
832 uint32_t size3 = p2.readFromMemory(buffer);
833 REPORTER_ASSERT(reporter, size1 == size3);
834 REPORTER_ASSERT(reporter, p == p2);
835
836 char buffer2[1024];
837 size3 = p2.writeToMemory(buffer2);
838 REPORTER_ASSERT(reporter, size1 == size3);
839 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000840
841 // test persistence of the oval flag & convexity
842 {
843 SkPath oval;
844 SkRect rect = SkRect::MakeWH(10, 10);
845 oval.addOval(rect);
846
847 write_and_read_back(reporter, oval);
848 }
reed@google.com53effc52011-09-21 19:05:12 +0000849}
850
851static void test_transform(skiatest::Reporter* reporter) {
852 SkPath p, p1;
853
854 static const SkPoint pts[] = {
855 { 0, 0 },
856 { SkIntToScalar(10), SkIntToScalar(10) },
857 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
858 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
859 };
860 p.moveTo(pts[0]);
861 p.lineTo(pts[1]);
862 p.quadTo(pts[2], pts[3]);
863 p.cubicTo(pts[4], pts[5], pts[6]);
864
865 SkMatrix matrix;
866 matrix.reset();
867 p.transform(matrix, &p1);
868 REPORTER_ASSERT(reporter, p == p1);
869
870 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
871 p.transform(matrix, &p1);
872 SkPoint pts1[7];
873 int count = p1.getPoints(pts1, 7);
874 REPORTER_ASSERT(reporter, 7 == count);
875 for (int i = 0; i < count; ++i) {
876 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
877 REPORTER_ASSERT(reporter, newPt == pts1[i]);
878 }
879}
880
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000881static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000882 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000883 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000884
schenney@chromium.org7e963602012-06-13 17:05:43 +0000885 struct zeroPathTestData {
886 const char* testPath;
887 const size_t numResultPts;
888 const SkRect resultBound;
889 const SkPath::Verb* resultVerbs;
890 const size_t numResultVerbs;
891 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000892
schenney@chromium.org7e963602012-06-13 17:05:43 +0000893 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
894 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
895 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
896 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
897 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
898 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
899 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
900 static const SkPath::Verb resultVerbs8[] = {
901 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
902 };
903 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
904 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
905 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
906 static const SkPath::Verb resultVerbs12[] = {
907 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
908 };
909 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
910 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
911 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
912 static const SkPath::Verb resultVerbs16[] = {
913 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
914 };
915 static const struct zeroPathTestData gZeroLengthTests[] = {
916 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000917 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
schenney@chromium.org7e963602012-06-13 17:05:43 +0000918 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000919 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
920 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
921 { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
922 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
923 { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
924 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
925 { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
926 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
927 { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
928 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
929 { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
schenney@chromium.org7e963602012-06-13 17:05:43 +0000930 SK_ARRAY_COUNT(resultVerbs14)
931 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000932 { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
933 { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
schenney@chromium.org7e963602012-06-13 17:05:43 +0000934 SK_ARRAY_COUNT(resultVerbs16)
935 }
936 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000937
schenney@chromium.org7e963602012-06-13 17:05:43 +0000938 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
939 p.reset();
940 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
941 REPORTER_ASSERT(reporter, valid);
942 REPORTER_ASSERT(reporter, !p.isEmpty());
943 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
944 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
945 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
946 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
947 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
948 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000949 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000950}
951
952struct SegmentInfo {
953 SkPath fPath;
954 int fPointCount;
955};
956
reed@google.com10296cc2011-09-21 12:29:05 +0000957#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
958
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000959static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +0000960 SkPath p, p2;
961
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000962 p.moveTo(0, 0);
963 p.quadTo(100, 100, 200, 200);
964 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
965 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000966 p2 = p;
967 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000968 p.cubicTo(100, 100, 200, 200, 300, 300);
969 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
970 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000971 p2 = p;
972 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
973
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000974 p.reset();
975 p.moveTo(0, 0);
976 p.cubicTo(100, 100, 200, 200, 300, 300);
977 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +0000978 p2 = p;
979 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
980
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000981 REPORTER_ASSERT(reporter, !p.isEmpty());
982}
983
984static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000985 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000986 SkPoint pts[4];
987
988 // Test an iterator with no path
989 SkPath::Iter noPathIter;
990 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000991
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000992 // Test that setting an empty path works
993 noPathIter.setPath(p, false);
994 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000995
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000996 // Test that close path makes no difference for an empty path
997 noPathIter.setPath(p, true);
998 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000999
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001000 // Test an iterator with an initial empty path
1001 SkPath::Iter iter(p, false);
1002 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1003
1004 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001005 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001006 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1007
schenney@chromium.org7e963602012-06-13 17:05:43 +00001008
1009 struct iterTestData {
1010 const char* testPath;
1011 const bool forceClose;
1012 const bool consumeDegenerates;
1013 const size_t* numResultPtsPerVerb;
1014 const SkPoint* resultPts;
1015 const SkPath::Verb* resultVerbs;
1016 const size_t numResultVerbs;
1017 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001018
schenney@chromium.org7e963602012-06-13 17:05:43 +00001019 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1020 static const SkPath::Verb resultVerbs2[] = {
1021 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1022 };
1023 static const SkPath::Verb resultVerbs3[] = {
1024 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1025 };
1026 static const SkPath::Verb resultVerbs4[] = {
1027 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1028 };
1029 static const SkPath::Verb resultVerbs5[] = {
1030 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1031 };
1032 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001033 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1034 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1035 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1036 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001037 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001038 static const SkPoint resultPts2[] = {
1039 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1040 };
1041 static const SkPoint resultPts3[] = {
1042 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1043 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1044 };
1045 static const SkPoint resultPts4[] = {
1046 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1047 };
1048 static const SkPoint resultPts5[] = {
1049 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1050 };
1051 static const struct iterTestData gIterTests[] = {
1052 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001053 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1054 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.org7e963602012-06-13 17:05:43 +00001055 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1056 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1057 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1058 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001059 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1060 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1061 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1062 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1063 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1064 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
schenney@chromium.org7e963602012-06-13 17:05:43 +00001065 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001066
schenney@chromium.org7e963602012-06-13 17:05:43 +00001067 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1068 p.reset();
1069 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1070 REPORTER_ASSERT(reporter, valid);
1071 iter.setPath(p, gIterTests[i].forceClose);
1072 int j = 0, l = 0;
1073 do {
1074 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1075 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1076 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1077 }
1078 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1079 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1080 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001081
1082 // The GM degeneratesegments.cpp test is more extensive
1083}
1084
1085static void test_raw_iter(skiatest::Reporter* reporter) {
1086 SkPath p;
1087 SkPoint pts[4];
1088
1089 // Test an iterator with no path
1090 SkPath::RawIter noPathIter;
1091 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1092 // Test that setting an empty path works
1093 noPathIter.setPath(p);
1094 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1095
1096 // Test an iterator with an initial empty path
1097 SkPath::RawIter iter(p);
1098 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1099
1100 // Test that a move-only path returns the move.
1101 p.moveTo(SK_Scalar1, 0);
1102 iter.setPath(p);
1103 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1104 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1105 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1106 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1107
1108 // No matter how many moves we add, we should get them all back
1109 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1110 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1111 iter.setPath(p);
1112 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1113 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1114 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1115 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1116 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1117 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1118 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1119 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1120 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1121 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1122
1123 // Initial close is never ever stored
1124 p.reset();
1125 p.close();
1126 iter.setPath(p);
1127 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1128
1129 // Move/close sequences
1130 p.reset();
1131 p.close(); // Not stored, no purpose
1132 p.moveTo(SK_Scalar1, 0);
1133 p.close();
1134 p.close(); // Not stored, no purpose
1135 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1136 p.close();
1137 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1138 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1139 p.close();
1140 iter.setPath(p);
1141 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1142 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1143 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1144 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1145 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1146 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1147 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1148 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1149 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1150 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1151 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1152 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1153 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1154 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1155 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1156 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1157 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1158 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1159 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1160 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1161 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1162 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1163
1164 // Generate random paths and verify
1165 SkPoint randomPts[25];
1166 for (int i = 0; i < 5; ++i) {
1167 for (int j = 0; j < 5; ++j) {
1168 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1169 }
1170 }
1171
1172 // Max of 10 segments, max 3 points per segment
1173 SkRandom rand(9876543);
1174 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001175 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001176 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001177
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001178 for (int i = 0; i < 500; ++i) {
1179 p.reset();
1180 bool lastWasClose = true;
1181 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001182 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001183 int numPoints = 0;
1184 int numVerbs = (rand.nextU() >> 16) % 10;
1185 int numIterVerbs = 0;
1186 for (int j = 0; j < numVerbs; ++j) {
1187 do {
1188 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1189 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001190 switch (nextVerb) {
1191 case SkPath::kMove_Verb:
1192 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1193 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001194 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001195 numPoints += 1;
1196 lastWasClose = false;
1197 haveMoveTo = true;
1198 break;
1199 case SkPath::kLine_Verb:
1200 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001201 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001202 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1203 haveMoveTo = true;
1204 }
1205 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1206 p.lineTo(expectedPts[numPoints]);
1207 numPoints += 1;
1208 lastWasClose = false;
1209 break;
1210 case SkPath::kQuad_Verb:
1211 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001212 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001213 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1214 haveMoveTo = true;
1215 }
1216 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1217 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1218 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1219 numPoints += 2;
1220 lastWasClose = false;
1221 break;
1222 case SkPath::kCubic_Verb:
1223 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001224 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001225 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1226 haveMoveTo = true;
1227 }
1228 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1229 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1230 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1231 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1232 expectedPts[numPoints + 2]);
1233 numPoints += 3;
1234 lastWasClose = false;
1235 break;
1236 case SkPath::kClose_Verb:
1237 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001238 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001239 lastWasClose = true;
1240 break;
1241 default:;
1242 }
1243 expectedVerbs[numIterVerbs++] = nextVerb;
1244 }
1245
1246 iter.setPath(p);
1247 numVerbs = numIterVerbs;
1248 numIterVerbs = 0;
1249 int numIterPts = 0;
1250 SkPoint lastMoveTo;
1251 SkPoint lastPt;
1252 lastMoveTo.set(0, 0);
1253 lastPt.set(0, 0);
1254 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1255 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1256 numIterVerbs++;
1257 switch (nextVerb) {
1258 case SkPath::kMove_Verb:
1259 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1260 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1261 lastPt = lastMoveTo = pts[0];
1262 numIterPts += 1;
1263 break;
1264 case SkPath::kLine_Verb:
1265 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1266 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1267 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1268 lastPt = pts[1];
1269 numIterPts += 1;
1270 break;
1271 case SkPath::kQuad_Verb:
1272 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1273 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1274 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1275 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1276 lastPt = pts[2];
1277 numIterPts += 2;
1278 break;
1279 case SkPath::kCubic_Verb:
1280 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1281 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1282 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1283 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1284 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1285 lastPt = pts[3];
1286 numIterPts += 3;
1287 break;
1288 case SkPath::kClose_Verb:
1289 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1290 lastPt = lastMoveTo;
1291 break;
1292 default:;
1293 }
1294 }
1295 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1296 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1297 }
1298}
1299
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001300static void check_for_circle(skiatest::Reporter* reporter,
1301 const SkPath& path, bool expected) {
1302 SkRect rect;
1303 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1304 if (expected) {
1305 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1306 }
1307}
1308
1309static void test_circle_skew(skiatest::Reporter* reporter,
1310 const SkPath& path) {
1311 SkPath tmp;
1312
1313 SkMatrix m;
1314 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1315 path.transform(m, &tmp);
1316 check_for_circle(reporter, tmp, false);
1317}
1318
1319static void test_circle_translate(skiatest::Reporter* reporter,
1320 const SkPath& path) {
1321 SkPath tmp;
1322
1323 // translate at small offset
1324 SkMatrix m;
1325 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1326 path.transform(m, &tmp);
1327 check_for_circle(reporter, tmp, true);
1328
1329 tmp.reset();
1330 m.reset();
1331
1332 // translate at a relatively big offset
1333 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1334 path.transform(m, &tmp);
1335 check_for_circle(reporter, tmp, true);
1336}
1337
1338static void test_circle_rotate(skiatest::Reporter* reporter,
1339 const SkPath& path) {
1340 for (int angle = 0; angle < 360; ++angle) {
1341 SkPath tmp;
1342 SkMatrix m;
1343 m.setRotate(SkIntToScalar(angle));
1344 path.transform(m, &tmp);
1345
1346 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1347 // degrees is not an oval anymore, this can be improved. we made this
1348 // for the simplicity of our implementation.
1349 if (angle % 90 == 0) {
1350 check_for_circle(reporter, tmp, true);
1351 } else {
1352 check_for_circle(reporter, tmp, false);
1353 }
1354 }
1355}
1356
1357static void test_circle_with_direction(skiatest::Reporter* reporter,
1358 SkPath::Direction dir) {
1359 SkPath path;
1360
1361 // circle at origin
1362 path.addCircle(0, 0, SkIntToScalar(20), dir);
1363 check_for_circle(reporter, path, true);
1364 test_circle_rotate(reporter, path);
1365 test_circle_translate(reporter, path);
1366 test_circle_skew(reporter, path);
1367
1368 // circle at an offset at (10, 10)
1369 path.reset();
1370 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1371 SkIntToScalar(20), dir);
1372 check_for_circle(reporter, path, true);
1373 test_circle_rotate(reporter, path);
1374 test_circle_translate(reporter, path);
1375 test_circle_skew(reporter, path);
1376}
1377
1378static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1379 SkPath path;
1380 SkPath circle;
1381 SkPath rect;
1382 SkPath empty;
1383
1384 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1385 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1386 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1387
1388 SkMatrix translate;
1389 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1390
1391 // For simplicity, all the path concatenation related operations
1392 // would mark it non-circle, though in theory it's still a circle.
1393
1394 // empty + circle (translate)
1395 path = empty;
1396 path.addPath(circle, translate);
1397 check_for_circle(reporter, path, false);
1398
1399 // circle + empty (translate)
1400 path = circle;
1401 path.addPath(empty, translate);
1402 check_for_circle(reporter, path, false);
1403
1404 // test reverseAddPath
1405 path = circle;
1406 path.reverseAddPath(rect);
1407 check_for_circle(reporter, path, false);
1408}
1409
1410static void test_circle(skiatest::Reporter* reporter) {
1411 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1412 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1413
1414 // multiple addCircle()
1415 SkPath path;
1416 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1417 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1418 check_for_circle(reporter, path, false);
1419
1420 // some extra lineTo() would make isOval() fail
1421 path.reset();
1422 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1423 path.lineTo(0, 0);
1424 check_for_circle(reporter, path, false);
1425
1426 // not back to the original point
1427 path.reset();
1428 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1429 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1430 check_for_circle(reporter, path, false);
1431
1432 test_circle_with_add_paths(reporter);
1433}
1434
1435static void test_oval(skiatest::Reporter* reporter) {
1436 SkRect rect;
1437 SkMatrix m;
1438 SkPath path;
1439
1440 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1441 path.addOval(rect);
1442
1443 REPORTER_ASSERT(reporter, path.isOval(NULL));
1444
1445 m.setRotate(SkIntToScalar(90));
1446 SkPath tmp;
1447 path.transform(m, &tmp);
1448 // an oval rotated 90 degrees is still an oval.
1449 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1450
1451 m.reset();
1452 m.setRotate(SkIntToScalar(30));
1453 tmp.reset();
1454 path.transform(m, &tmp);
1455 // an oval rotated 30 degrees is not an oval anymore.
1456 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1457
1458 // since empty path being transformed.
1459 path.reset();
1460 tmp.reset();
1461 m.reset();
1462 path.transform(m, &tmp);
1463 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1464
1465 // empty path is not an oval
1466 tmp.reset();
1467 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1468
1469 // only has moveTo()s
1470 tmp.reset();
1471 tmp.moveTo(0, 0);
1472 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1473 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1474
1475 // mimic WebKit's calling convention,
1476 // call moveTo() first and then call addOval()
1477 path.reset();
1478 path.moveTo(0, 0);
1479 path.addOval(rect);
1480 REPORTER_ASSERT(reporter, path.isOval(NULL));
1481
1482 // copy path
1483 path.reset();
1484 tmp.reset();
1485 tmp.addOval(rect);
1486 path = tmp;
1487 REPORTER_ASSERT(reporter, path.isOval(NULL));
1488}
1489
caryclark@google.com42639cd2012-06-06 12:03:39 +00001490static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001491 {
1492 SkSize size;
1493 size.fWidth = 3.4f;
1494 size.width();
1495 size = SkSize::Make(3,4);
1496 SkISize isize = SkISize::Make(3,4);
1497 }
1498
1499 SkTSize<SkScalar>::Make(3,4);
1500
reed@android.com3abec1d2009-03-02 05:36:20 +00001501 SkPath p, p2;
1502 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001503
reed@android.com3abec1d2009-03-02 05:36:20 +00001504 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001505 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001506 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001507 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001508 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001509 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1510 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1511 REPORTER_ASSERT(reporter, p == p2);
1512 REPORTER_ASSERT(reporter, !(p != p2));
1513
reed@android.comd252db02009-04-01 18:31:44 +00001514 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001515
reed@android.com3abec1d2009-03-02 05:36:20 +00001516 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001517
reed@android.com6b82d1a2009-06-03 02:35:01 +00001518 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1519 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001520 // we have quads or cubics
1521 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001522 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001523
reed@android.com6b82d1a2009-06-03 02:35:01 +00001524 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001525 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001526 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001527
reed@android.com6b82d1a2009-06-03 02:35:01 +00001528 p.addOval(bounds);
1529 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001530 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001531
reed@android.com6b82d1a2009-06-03 02:35:01 +00001532 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001533 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001534 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001535 // we have only lines
1536 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001537 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001538
1539 REPORTER_ASSERT(reporter, p != p2);
1540 REPORTER_ASSERT(reporter, !(p == p2));
1541
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001542 // do getPoints and getVerbs return the right result
1543 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1544 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001545 SkPoint pts[4];
1546 int count = p.getPoints(pts, 4);
1547 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001548 uint8_t verbs[6];
1549 verbs[5] = 0xff;
1550 p.getVerbs(verbs, 5);
1551 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1552 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1553 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1554 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1555 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1556 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001557 bounds2.set(pts, 4);
1558 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001559
reed@android.com3abec1d2009-03-02 05:36:20 +00001560 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1561 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001562 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001563
reed@android.com3abec1d2009-03-02 05:36:20 +00001564 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001565 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001566 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1567 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001568
reed@android.com3abec1d2009-03-02 05:36:20 +00001569 // now force p to not be a rect
1570 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1571 p.addRect(bounds);
1572 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001573
reed@google.com7e6c4d12012-05-10 14:05:43 +00001574 test_isLine(reporter);
1575 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001576 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001577 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001578 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001579 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001580 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001581 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001582 test_flattening(reporter);
1583 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001584 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001585 test_iter(reporter);
1586 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001587 test_circle(reporter);
1588 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001589 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001590 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00001591 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00001592 test_isfinite_after_transform(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001593}
1594
1595#include "TestClassDef.h"
1596DEFINE_TESTCLASS("Path", PathTestClass, TestPath)