blob: 79b79b35e9a2dadc9a84350f02c50ce9ff89818d [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
reed@google.com0bb18bb2012-07-26 15:20:36 +000019static void test_rect_isfinite(skiatest::Reporter* reporter) {
20 const SkScalar inf = SK_ScalarInfinity;
21 const SkScalar nan = SK_ScalarNaN;
22
23 SkRect r;
24 r.setEmpty();
25 REPORTER_ASSERT(reporter, r.isFinite());
26 r.set(0, 0, inf, -inf);
27 REPORTER_ASSERT(reporter, !r.isFinite());
28 r.set(0, 0, nan, 0);
29 REPORTER_ASSERT(reporter, !r.isFinite());
30
31 SkPoint pts[] = {
32 { 0, 0 },
33 { SK_Scalar1, 0 },
34 { 0, SK_Scalar1 },
35 };
36
37 bool isFine = r.setBoundsCheck(pts, 3);
38 REPORTER_ASSERT(reporter, isFine);
39 REPORTER_ASSERT(reporter, !r.isEmpty());
40
41 pts[1].set(inf, 0);
42 isFine = r.setBoundsCheck(pts, 3);
43 REPORTER_ASSERT(reporter, !isFine);
44 REPORTER_ASSERT(reporter, r.isEmpty());
45
46 pts[1].set(nan, 0);
47 isFine = r.setBoundsCheck(pts, 3);
48 REPORTER_ASSERT(reporter, !isFine);
49 REPORTER_ASSERT(reporter, r.isEmpty());
50}
51
52static void test_path_isfinite(skiatest::Reporter* reporter) {
53 const SkScalar inf = SK_ScalarInfinity;
54 const SkScalar nan = SK_ScalarNaN;
55
56 SkPath path;
57 REPORTER_ASSERT(reporter, path.isFinite());
58
59 path.reset();
60 REPORTER_ASSERT(reporter, path.isFinite());
61
62 path.reset();
63 path.moveTo(SK_Scalar1, 0);
64 REPORTER_ASSERT(reporter, path.isFinite());
65
66 path.reset();
67 path.moveTo(inf, -inf);
68 REPORTER_ASSERT(reporter, !path.isFinite());
69
70 path.reset();
71 path.moveTo(nan, 0);
72 REPORTER_ASSERT(reporter, !path.isFinite());
73}
74
75static void test_isfinite(skiatest::Reporter* reporter) {
76 test_rect_isfinite(reporter);
77 test_path_isfinite(reporter);
78}
79
reed@google.com744faba2012-05-29 19:54:52 +000080// assert that we always
81// start with a moveTo
82// only have 1 moveTo
83// only have Lines after that
84// end with a single close
85// only have (at most) 1 close
86//
87static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
88 const SkPoint srcPts[], int count, bool expectClose) {
89 SkPath::RawIter iter(path);
90 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +000091
92 bool firstTime = true;
93 bool foundClose = false;
94 for (;;) {
95 switch (iter.next(pts)) {
96 case SkPath::kMove_Verb:
97 REPORTER_ASSERT(reporter, firstTime);
98 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
99 srcPts++;
100 firstTime = false;
101 break;
102 case SkPath::kLine_Verb:
103 REPORTER_ASSERT(reporter, !firstTime);
104 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
105 srcPts++;
106 break;
107 case SkPath::kQuad_Verb:
108 REPORTER_ASSERT(reporter, !"unexpected quad verb");
109 break;
110 case SkPath::kCubic_Verb:
111 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
112 break;
113 case SkPath::kClose_Verb:
114 REPORTER_ASSERT(reporter, !firstTime);
115 REPORTER_ASSERT(reporter, !foundClose);
116 REPORTER_ASSERT(reporter, expectClose);
117 foundClose = true;
118 break;
119 case SkPath::kDone_Verb:
120 goto DONE;
121 }
122 }
123DONE:
124 REPORTER_ASSERT(reporter, foundClose == expectClose);
125}
126
127static void test_addPoly(skiatest::Reporter* reporter) {
128 SkPoint pts[32];
129 SkRandom rand;
130
131 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
132 pts[i].fX = rand.nextSScalar1();
133 pts[i].fY = rand.nextSScalar1();
134 }
135
136 for (int doClose = 0; doClose <= 1; ++doClose) {
137 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
138 SkPath path;
139 path.addPoly(pts, count, SkToBool(doClose));
140 test_poly(reporter, path, pts, count, SkToBool(doClose));
141 }
142 }
143}
144
reed@google.com8b06f1a2012-05-29 12:03:46 +0000145static void test_strokerec(skiatest::Reporter* reporter) {
146 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
147 REPORTER_ASSERT(reporter, rec.isFillStyle());
148
149 rec.setHairlineStyle();
150 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
151
152 rec.setStrokeStyle(SK_Scalar1, false);
153 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
154
155 rec.setStrokeStyle(SK_Scalar1, true);
156 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
157
158 rec.setStrokeStyle(0, false);
159 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
160
161 rec.setStrokeStyle(0, true);
162 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
163}
164
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000165/**
166 * cheapIsDirection can take a shortcut when a path is marked convex.
167 * This function ensures that we always test cheapIsDirection when the path
168 * is flagged with unknown convexity status.
169 */
170static void check_direction(SkPath* path,
171 SkPath::Direction expectedDir,
172 skiatest::Reporter* reporter) {
173 if (SkPath::kConvex_Convexity == path->getConvexity()) {
174 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
175 path->setConvexity(SkPath::kUnknown_Convexity);
176 }
177 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
178}
179
reed@google.com3e71a882012-01-10 18:44:37 +0000180static void test_direction(skiatest::Reporter* reporter) {
181 size_t i;
182 SkPath path;
183 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
184 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
185 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
186
187 static const char* gDegen[] = {
188 "M 10 10",
189 "M 10 10 M 20 20",
190 "M 10 10 L 20 20",
191 "M 10 10 L 10 10 L 10 10",
192 "M 10 10 Q 10 10 10 10",
193 "M 10 10 C 10 10 10 10 10 10",
194 };
195 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
196 path.reset();
197 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
198 REPORTER_ASSERT(reporter, valid);
199 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
200 }
201
202 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000203 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000204 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000205 "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 +0000206 // rect with top two corners replaced by cubics with identical middle
207 // control points
208 "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 +0000209 };
210 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
211 path.reset();
212 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
213 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000214 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000215 }
216
217 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000218 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000219 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000220 "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 +0000221 // rect with top two corners replaced by cubics with identical middle
222 // control points
223 "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 +0000224 };
225 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
226 path.reset();
227 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
228 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000229 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000230 }
reed@google.comac8543f2012-01-30 20:51:25 +0000231
232 // Test two donuts, each wound a different direction. Only the outer contour
233 // determines the cheap direction
234 path.reset();
235 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
236 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000237 check_direction(&path, SkPath::kCW_Direction, reporter);
238
reed@google.comac8543f2012-01-30 20:51:25 +0000239 path.reset();
240 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
241 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000242 check_direction(&path, SkPath::kCCW_Direction, reporter);
243
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000244#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000245 // triangle with one point really far from the origin.
246 path.reset();
247 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000248 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
249 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
250 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
251 check_direction(&path, SkPath::kCCW_Direction, reporter);
252#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000253}
254
reed@google.comffdb0182011-11-14 19:29:14 +0000255static void add_rect(SkPath* path, const SkRect& r) {
256 path->moveTo(r.fLeft, r.fTop);
257 path->lineTo(r.fRight, r.fTop);
258 path->lineTo(r.fRight, r.fBottom);
259 path->lineTo(r.fLeft, r.fBottom);
260 path->close();
261}
262
263static void test_bounds(skiatest::Reporter* reporter) {
264 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000265 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
266 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
267 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
268 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000269 };
270
271 SkPath path0, path1;
272 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
273 path0.addRect(rects[i]);
274 add_rect(&path1, rects[i]);
275 }
276
277 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
278}
279
reed@google.com55b5f4b2011-09-07 12:23:41 +0000280static void stroke_cubic(const SkPoint pts[4]) {
281 SkPath path;
282 path.moveTo(pts[0]);
283 path.cubicTo(pts[1], pts[2], pts[3]);
284
285 SkPaint paint;
286 paint.setStyle(SkPaint::kStroke_Style);
287 paint.setStrokeWidth(SK_Scalar1 * 2);
288
289 SkPath fill;
290 paint.getFillPath(path, &fill);
291}
292
293// just ensure this can run w/o any SkASSERTS firing in the debug build
294// we used to assert due to differences in how we determine a degenerate vector
295// but that was fixed with the introduction of SkPoint::CanNormalize
296static void stroke_tiny_cubic() {
297 SkPoint p0[] = {
298 { 372.0f, 92.0f },
299 { 372.0f, 92.0f },
300 { 372.0f, 92.0f },
301 { 372.0f, 92.0f },
302 };
303
304 stroke_cubic(p0);
305
306 SkPoint p1[] = {
307 { 372.0f, 92.0f },
308 { 372.0007f, 92.000755f },
309 { 371.99927f, 92.003922f },
310 { 371.99826f, 92.003899f },
311 };
312
313 stroke_cubic(p1);
314}
315
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000316static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
317 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000318 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000319 SkPoint mv;
320 SkPoint pts[4];
321 SkPath::Verb v;
322 int nMT = 0;
323 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000324 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000325 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
326 switch (v) {
327 case SkPath::kMove_Verb:
328 mv = pts[0];
329 ++nMT;
330 break;
331 case SkPath::kClose_Verb:
332 REPORTER_ASSERT(reporter, mv == pts[0]);
333 ++nCL;
334 break;
335 default:
336 break;
337 }
338 }
339 // if we force a close on the interator we should have a close
340 // for every moveTo
341 REPORTER_ASSERT(reporter, !i || nMT == nCL);
342 }
343}
344
345static void test_close(skiatest::Reporter* reporter) {
346 SkPath closePt;
347 closePt.moveTo(0, 0);
348 closePt.close();
349 check_close(reporter, closePt);
350
351 SkPath openPt;
352 openPt.moveTo(0, 0);
353 check_close(reporter, openPt);
354
355 SkPath empty;
356 check_close(reporter, empty);
357 empty.close();
358 check_close(reporter, empty);
359
360 SkPath rect;
361 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
362 check_close(reporter, rect);
363 rect.close();
364 check_close(reporter, rect);
365
366 SkPath quad;
367 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
368 check_close(reporter, quad);
369 quad.close();
370 check_close(reporter, quad);
371
372 SkPath cubic;
373 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
374 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
375 check_close(reporter, cubic);
376 cubic.close();
377 check_close(reporter, cubic);
378
379 SkPath line;
380 line.moveTo(SK_Scalar1, SK_Scalar1);
381 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
382 check_close(reporter, line);
383 line.close();
384 check_close(reporter, line);
385
386 SkPath rect2;
387 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
388 rect2.close();
389 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
390 check_close(reporter, rect2);
391 rect2.close();
392 check_close(reporter, rect2);
393
394 SkPath oval3;
395 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
396 oval3.close();
397 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
398 check_close(reporter, oval3);
399 oval3.close();
400 check_close(reporter, oval3);
401
402 SkPath moves;
403 moves.moveTo(SK_Scalar1, SK_Scalar1);
404 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
405 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
406 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
407 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000408
409 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000410}
411
reed@google.com7c424812011-05-15 04:38:34 +0000412static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
413 SkPath::Convexity expected) {
414 SkPath::Convexity c = SkPath::ComputeConvexity(path);
415 REPORTER_ASSERT(reporter, c == expected);
416}
417
418static void test_convexity2(skiatest::Reporter* reporter) {
419 SkPath pt;
420 pt.moveTo(0, 0);
421 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000422 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000423
424 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000425 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
426 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000427 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000428 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000429
430 SkPath triLeft;
431 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000432 triLeft.lineTo(SK_Scalar1, 0);
433 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000434 triLeft.close();
435 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
436
437 SkPath triRight;
438 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000439 triRight.lineTo(-SK_Scalar1, 0);
440 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000441 triRight.close();
442 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
443
444 SkPath square;
445 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000446 square.lineTo(SK_Scalar1, 0);
447 square.lineTo(SK_Scalar1, SK_Scalar1);
448 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000449 square.close();
450 check_convexity(reporter, square, SkPath::kConvex_Convexity);
451
452 SkPath redundantSquare;
453 redundantSquare.moveTo(0, 0);
454 redundantSquare.lineTo(0, 0);
455 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000456 redundantSquare.lineTo(SK_Scalar1, 0);
457 redundantSquare.lineTo(SK_Scalar1, 0);
458 redundantSquare.lineTo(SK_Scalar1, 0);
459 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
460 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
461 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
462 redundantSquare.lineTo(0, SK_Scalar1);
463 redundantSquare.lineTo(0, SK_Scalar1);
464 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000465 redundantSquare.close();
466 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
467
468 SkPath bowTie;
469 bowTie.moveTo(0, 0);
470 bowTie.lineTo(0, 0);
471 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000472 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
473 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
474 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
475 bowTie.lineTo(SK_Scalar1, 0);
476 bowTie.lineTo(SK_Scalar1, 0);
477 bowTie.lineTo(SK_Scalar1, 0);
478 bowTie.lineTo(0, SK_Scalar1);
479 bowTie.lineTo(0, SK_Scalar1);
480 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000481 bowTie.close();
482 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
483
484 SkPath spiral;
485 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000486 spiral.lineTo(100*SK_Scalar1, 0);
487 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
488 spiral.lineTo(0, 100*SK_Scalar1);
489 spiral.lineTo(0, 50*SK_Scalar1);
490 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
491 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000492 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000493 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000494
495 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000496 dent.moveTo(0, 0);
497 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
498 dent.lineTo(0, 100*SK_Scalar1);
499 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
500 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000501 dent.close();
502 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
503}
504
reed@android.com6b82d1a2009-06-03 02:35:01 +0000505static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
506 const SkRect& bounds) {
507 REPORTER_ASSERT(reporter, p.isConvex());
508 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000509
reed@android.com6b82d1a2009-06-03 02:35:01 +0000510 SkPath p2(p);
511 REPORTER_ASSERT(reporter, p2.isConvex());
512 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
513
514 SkPath other;
515 other.swap(p2);
516 REPORTER_ASSERT(reporter, other.isConvex());
517 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
518}
519
reed@google.com04863fa2011-05-15 04:08:24 +0000520static void setFromString(SkPath* path, const char str[]) {
521 bool first = true;
522 while (str) {
523 SkScalar x, y;
524 str = SkParse::FindScalar(str, &x);
525 if (NULL == str) {
526 break;
527 }
528 str = SkParse::FindScalar(str, &y);
529 SkASSERT(str);
530 if (first) {
531 path->moveTo(x, y);
532 first = false;
533 } else {
534 path->lineTo(x, y);
535 }
536 }
537}
538
539static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000540 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
541 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
542
543 SkPath path;
544
reed@google.comb54455e2011-05-16 14:16:04 +0000545 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000546 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000547 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000548 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000549 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
550 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000551 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000552 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000553 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000554 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000555 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000556 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000557 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000558
559 static const struct {
560 const char* fPathStr;
561 SkPath::Convexity fExpectedConvexity;
562 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000563 { "", SkPath::kConvex_Convexity },
564 { "0 0", SkPath::kConvex_Convexity },
565 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000566 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000567 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
568 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
569 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
570 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
571 };
572
573 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
574 SkPath path;
575 setFromString(&path, gRec[i].fPathStr);
576 SkPath::Convexity c = SkPath::ComputeConvexity(path);
577 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
578 }
579}
580
reed@google.com7e6c4d12012-05-10 14:05:43 +0000581static void test_isLine(skiatest::Reporter* reporter) {
582 SkPath path;
583 SkPoint pts[2];
584 const SkScalar value = SkIntToScalar(5);
585
586 REPORTER_ASSERT(reporter, !path.isLine(NULL));
587
588 // set some non-zero values
589 pts[0].set(value, value);
590 pts[1].set(value, value);
591 REPORTER_ASSERT(reporter, !path.isLine(pts));
592 // check that pts was untouched
593 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
594 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
595
596 const SkScalar moveX = SkIntToScalar(1);
597 const SkScalar moveY = SkIntToScalar(2);
598 SkASSERT(value != moveX && value != moveY);
599
600 path.moveTo(moveX, moveY);
601 REPORTER_ASSERT(reporter, !path.isLine(NULL));
602 REPORTER_ASSERT(reporter, !path.isLine(pts));
603 // check that pts was untouched
604 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
605 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
606
607 const SkScalar lineX = SkIntToScalar(2);
608 const SkScalar lineY = SkIntToScalar(2);
609 SkASSERT(value != lineX && value != lineY);
610
611 path.lineTo(lineX, lineY);
612 REPORTER_ASSERT(reporter, path.isLine(NULL));
613
614 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
615 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
616 REPORTER_ASSERT(reporter, path.isLine(pts));
617 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
618 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
619
620 path.lineTo(0, 0); // too many points/verbs
621 REPORTER_ASSERT(reporter, !path.isLine(NULL));
622 REPORTER_ASSERT(reporter, !path.isLine(pts));
623 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
624 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
625}
626
caryclark@google.comf1316942011-07-26 19:54:45 +0000627// Simple isRect test is inline TestPath, below.
628// test_isRect provides more extensive testing.
629static void test_isRect(skiatest::Reporter* reporter) {
630 // passing tests (all moveTo / lineTo...
631 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
632 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
633 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
634 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
635 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
636 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
637 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
638 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
639 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
640 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
641 {1, 0}, {.5f, 0}};
642 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
643 {0, 1}, {0, .5f}};
644 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
645 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
646 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
647
648 // failing tests
649 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
650 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
651 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
652 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
653 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
654 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
655 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
656 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
657
658 // failing, no close
659 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
660 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
661
662 size_t testLen[] = {
663 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
664 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
665 sizeof(rd), sizeof(re),
666 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
667 sizeof(f7), sizeof(f8),
668 sizeof(c1), sizeof(c2)
669 };
670 SkPoint* tests[] = {
671 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
672 f1, f2, f3, f4, f5, f6, f7, f8,
673 c1, c2
674 };
675 SkPoint* lastPass = re;
676 SkPoint* lastClose = f8;
677 bool fail = false;
678 bool close = true;
679 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
680 size_t index;
681 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
682 SkPath path;
683 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
684 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
685 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
686 }
687 if (close) {
688 path.close();
689 }
690 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
691 if (tests[testIndex] == lastPass) {
692 fail = true;
693 }
694 if (tests[testIndex] == lastClose) {
695 close = false;
696 }
697 }
698
699 // fail, close then line
700 SkPath path1;
701 path1.moveTo(r1[0].fX, r1[0].fY);
702 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
703 path1.lineTo(r1[index].fX, r1[index].fY);
704 }
705 path1.close();
706 path1.lineTo(1, 0);
707 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
708
709 // fail, move in the middle
710 path1.reset();
711 path1.moveTo(r1[0].fX, r1[0].fY);
712 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
713 if (index == 2) {
714 path1.moveTo(1, .5f);
715 }
716 path1.lineTo(r1[index].fX, r1[index].fY);
717 }
718 path1.close();
719 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
720
721 // fail, move on the edge
722 path1.reset();
723 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
724 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
725 path1.lineTo(r1[index].fX, r1[index].fY);
726 }
727 path1.close();
728 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
729
730 // fail, quad
731 path1.reset();
732 path1.moveTo(r1[0].fX, r1[0].fY);
733 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
734 if (index == 2) {
735 path1.quadTo(1, .5f, 1, .5f);
736 }
737 path1.lineTo(r1[index].fX, r1[index].fY);
738 }
739 path1.close();
740 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
741
742 // fail, cubic
743 path1.reset();
744 path1.moveTo(r1[0].fX, r1[0].fY);
745 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
746 if (index == 2) {
747 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
748 }
749 path1.lineTo(r1[index].fX, r1[index].fY);
750 }
751 path1.close();
752 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
753}
754
reed@google.com53effc52011-09-21 19:05:12 +0000755static void test_flattening(skiatest::Reporter* reporter) {
756 SkPath p;
757
758 static const SkPoint pts[] = {
759 { 0, 0 },
760 { SkIntToScalar(10), SkIntToScalar(10) },
761 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
762 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
763 };
764 p.moveTo(pts[0]);
765 p.lineTo(pts[1]);
766 p.quadTo(pts[2], pts[3]);
767 p.cubicTo(pts[4], pts[5], pts[6]);
768
769 SkWriter32 writer(100);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000770 writer.writePath(p);
reed@google.com53effc52011-09-21 19:05:12 +0000771 size_t size = writer.size();
772 SkAutoMalloc storage(size);
773 writer.flatten(storage.get());
774 SkReader32 reader(storage.get(), size);
775
776 SkPath p1;
777 REPORTER_ASSERT(reporter, p1 != p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000778 reader.readPath(&p1);
reed@google.com53effc52011-09-21 19:05:12 +0000779 REPORTER_ASSERT(reporter, p1 == p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000780
781 // create a buffer that should be much larger than the path so we don't
782 // kill our stack if writer goes too far.
783 char buffer[1024];
784 uint32_t size1 = p.writeToMemory(NULL);
785 uint32_t size2 = p.writeToMemory(buffer);
786 REPORTER_ASSERT(reporter, size1 == size2);
787
788 SkPath p2;
789 uint32_t size3 = p2.readFromMemory(buffer);
790 REPORTER_ASSERT(reporter, size1 == size3);
791 REPORTER_ASSERT(reporter, p == p2);
792
793 char buffer2[1024];
794 size3 = p2.writeToMemory(buffer2);
795 REPORTER_ASSERT(reporter, size1 == size3);
796 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
reed@google.com53effc52011-09-21 19:05:12 +0000797}
798
799static void test_transform(skiatest::Reporter* reporter) {
800 SkPath p, p1;
801
802 static const SkPoint pts[] = {
803 { 0, 0 },
804 { SkIntToScalar(10), SkIntToScalar(10) },
805 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
806 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
807 };
808 p.moveTo(pts[0]);
809 p.lineTo(pts[1]);
810 p.quadTo(pts[2], pts[3]);
811 p.cubicTo(pts[4], pts[5], pts[6]);
812
813 SkMatrix matrix;
814 matrix.reset();
815 p.transform(matrix, &p1);
816 REPORTER_ASSERT(reporter, p == p1);
817
818 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
819 p.transform(matrix, &p1);
820 SkPoint pts1[7];
821 int count = p1.getPoints(pts1, 7);
822 REPORTER_ASSERT(reporter, 7 == count);
823 for (int i = 0; i < count; ++i) {
824 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
825 REPORTER_ASSERT(reporter, newPt == pts1[i]);
826 }
827}
828
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000829static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000830 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000831 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000832
schenney@chromium.org7e963602012-06-13 17:05:43 +0000833 struct zeroPathTestData {
834 const char* testPath;
835 const size_t numResultPts;
836 const SkRect resultBound;
837 const SkPath::Verb* resultVerbs;
838 const size_t numResultVerbs;
839 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000840
schenney@chromium.org7e963602012-06-13 17:05:43 +0000841 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
842 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
843 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
844 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
845 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
846 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
847 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
848 static const SkPath::Verb resultVerbs8[] = {
849 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
850 };
851 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
852 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
853 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
854 static const SkPath::Verb resultVerbs12[] = {
855 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
856 };
857 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
858 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
859 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
860 static const SkPath::Verb resultVerbs16[] = {
861 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
862 };
863 static const struct zeroPathTestData gZeroLengthTests[] = {
864 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000865 { "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 +0000866 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000867 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
868 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
869 { "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) },
870 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
871 { "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) },
872 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
873 { "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) },
874 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
875 { "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) },
876 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
877 { "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 +0000878 SK_ARRAY_COUNT(resultVerbs14)
879 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000880 { "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) },
881 { "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 +0000882 SK_ARRAY_COUNT(resultVerbs16)
883 }
884 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000885
schenney@chromium.org7e963602012-06-13 17:05:43 +0000886 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
887 p.reset();
888 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
889 REPORTER_ASSERT(reporter, valid);
890 REPORTER_ASSERT(reporter, !p.isEmpty());
891 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
892 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
893 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
894 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
895 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
896 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000897 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000898}
899
900struct SegmentInfo {
901 SkPath fPath;
902 int fPointCount;
903};
904
reed@google.com10296cc2011-09-21 12:29:05 +0000905#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
906
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000907static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +0000908 SkPath p, p2;
909
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000910 p.moveTo(0, 0);
911 p.quadTo(100, 100, 200, 200);
912 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
913 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000914 p2 = p;
915 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000916 p.cubicTo(100, 100, 200, 200, 300, 300);
917 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
918 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000919 p2 = p;
920 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
921
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000922 p.reset();
923 p.moveTo(0, 0);
924 p.cubicTo(100, 100, 200, 200, 300, 300);
925 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +0000926 p2 = p;
927 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
928
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000929 REPORTER_ASSERT(reporter, !p.isEmpty());
930}
931
932static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000933 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000934 SkPoint pts[4];
935
936 // Test an iterator with no path
937 SkPath::Iter noPathIter;
938 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000939
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000940 // Test that setting an empty path works
941 noPathIter.setPath(p, false);
942 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000943
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000944 // Test that close path makes no difference for an empty path
945 noPathIter.setPath(p, true);
946 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000947
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000948 // Test an iterator with an initial empty path
949 SkPath::Iter iter(p, false);
950 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
951
952 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +0000953 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000954 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
955
schenney@chromium.org7e963602012-06-13 17:05:43 +0000956
957 struct iterTestData {
958 const char* testPath;
959 const bool forceClose;
960 const bool consumeDegenerates;
961 const size_t* numResultPtsPerVerb;
962 const SkPoint* resultPts;
963 const SkPath::Verb* resultVerbs;
964 const size_t numResultVerbs;
965 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000966
schenney@chromium.org7e963602012-06-13 17:05:43 +0000967 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
968 static const SkPath::Verb resultVerbs2[] = {
969 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
970 };
971 static const SkPath::Verb resultVerbs3[] = {
972 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
973 };
974 static const SkPath::Verb resultVerbs4[] = {
975 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
976 };
977 static const SkPath::Verb resultVerbs5[] = {
978 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
979 };
980 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +0000981 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
982 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
983 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
984 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000985 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000986 static const SkPoint resultPts2[] = {
987 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
988 };
989 static const SkPoint resultPts3[] = {
990 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
991 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
992 };
993 static const SkPoint resultPts4[] = {
994 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
995 };
996 static const SkPoint resultPts5[] = {
997 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
998 };
999 static const struct iterTestData gIterTests[] = {
1000 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001001 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1002 { "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 +00001003 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1004 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1005 { "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) },
1006 { "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 +00001007 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1008 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1009 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1010 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1011 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1012 { "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 +00001013 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001014
schenney@chromium.org7e963602012-06-13 17:05:43 +00001015 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1016 p.reset();
1017 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1018 REPORTER_ASSERT(reporter, valid);
1019 iter.setPath(p, gIterTests[i].forceClose);
1020 int j = 0, l = 0;
1021 do {
1022 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1023 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1024 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1025 }
1026 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1027 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1028 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001029
1030 // The GM degeneratesegments.cpp test is more extensive
1031}
1032
1033static void test_raw_iter(skiatest::Reporter* reporter) {
1034 SkPath p;
1035 SkPoint pts[4];
1036
1037 // Test an iterator with no path
1038 SkPath::RawIter noPathIter;
1039 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1040 // Test that setting an empty path works
1041 noPathIter.setPath(p);
1042 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1043
1044 // Test an iterator with an initial empty path
1045 SkPath::RawIter iter(p);
1046 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1047
1048 // Test that a move-only path returns the move.
1049 p.moveTo(SK_Scalar1, 0);
1050 iter.setPath(p);
1051 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1052 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1053 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1054 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1055
1056 // No matter how many moves we add, we should get them all back
1057 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1058 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1059 iter.setPath(p);
1060 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1061 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1062 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1063 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1064 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1065 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1066 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1067 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1068 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1069 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1070
1071 // Initial close is never ever stored
1072 p.reset();
1073 p.close();
1074 iter.setPath(p);
1075 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1076
1077 // Move/close sequences
1078 p.reset();
1079 p.close(); // Not stored, no purpose
1080 p.moveTo(SK_Scalar1, 0);
1081 p.close();
1082 p.close(); // Not stored, no purpose
1083 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1084 p.close();
1085 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1086 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1087 p.close();
1088 iter.setPath(p);
1089 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1090 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1091 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1092 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1093 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1094 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1095 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1096 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1097 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1098 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1099 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1100 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1101 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1102 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1103 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1104 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1105 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1106 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1107 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1108 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1109 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1110 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1111
1112 // Generate random paths and verify
1113 SkPoint randomPts[25];
1114 for (int i = 0; i < 5; ++i) {
1115 for (int j = 0; j < 5; ++j) {
1116 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1117 }
1118 }
1119
1120 // Max of 10 segments, max 3 points per segment
1121 SkRandom rand(9876543);
1122 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001123 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001124 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001125
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001126 for (int i = 0; i < 500; ++i) {
1127 p.reset();
1128 bool lastWasClose = true;
1129 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001130 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001131 int numPoints = 0;
1132 int numVerbs = (rand.nextU() >> 16) % 10;
1133 int numIterVerbs = 0;
1134 for (int j = 0; j < numVerbs; ++j) {
1135 do {
1136 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1137 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001138 switch (nextVerb) {
1139 case SkPath::kMove_Verb:
1140 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1141 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001142 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001143 numPoints += 1;
1144 lastWasClose = false;
1145 haveMoveTo = true;
1146 break;
1147 case SkPath::kLine_Verb:
1148 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001149 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001150 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1151 haveMoveTo = true;
1152 }
1153 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1154 p.lineTo(expectedPts[numPoints]);
1155 numPoints += 1;
1156 lastWasClose = false;
1157 break;
1158 case SkPath::kQuad_Verb:
1159 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001160 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001161 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1162 haveMoveTo = true;
1163 }
1164 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1165 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1166 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1167 numPoints += 2;
1168 lastWasClose = false;
1169 break;
1170 case SkPath::kCubic_Verb:
1171 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001172 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001173 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1174 haveMoveTo = true;
1175 }
1176 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1177 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1178 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1179 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1180 expectedPts[numPoints + 2]);
1181 numPoints += 3;
1182 lastWasClose = false;
1183 break;
1184 case SkPath::kClose_Verb:
1185 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001186 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001187 lastWasClose = true;
1188 break;
1189 default:;
1190 }
1191 expectedVerbs[numIterVerbs++] = nextVerb;
1192 }
1193
1194 iter.setPath(p);
1195 numVerbs = numIterVerbs;
1196 numIterVerbs = 0;
1197 int numIterPts = 0;
1198 SkPoint lastMoveTo;
1199 SkPoint lastPt;
1200 lastMoveTo.set(0, 0);
1201 lastPt.set(0, 0);
1202 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1203 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1204 numIterVerbs++;
1205 switch (nextVerb) {
1206 case SkPath::kMove_Verb:
1207 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1208 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1209 lastPt = lastMoveTo = pts[0];
1210 numIterPts += 1;
1211 break;
1212 case SkPath::kLine_Verb:
1213 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1214 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1215 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1216 lastPt = pts[1];
1217 numIterPts += 1;
1218 break;
1219 case SkPath::kQuad_Verb:
1220 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1221 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1222 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1223 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1224 lastPt = pts[2];
1225 numIterPts += 2;
1226 break;
1227 case SkPath::kCubic_Verb:
1228 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1229 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1230 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1231 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1232 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1233 lastPt = pts[3];
1234 numIterPts += 3;
1235 break;
1236 case SkPath::kClose_Verb:
1237 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1238 lastPt = lastMoveTo;
1239 break;
1240 default:;
1241 }
1242 }
1243 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1244 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1245 }
1246}
1247
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001248static void check_for_circle(skiatest::Reporter* reporter,
1249 const SkPath& path, bool expected) {
1250 SkRect rect;
1251 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1252 if (expected) {
1253 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1254 }
1255}
1256
1257static void test_circle_skew(skiatest::Reporter* reporter,
1258 const SkPath& path) {
1259 SkPath tmp;
1260
1261 SkMatrix m;
1262 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1263 path.transform(m, &tmp);
1264 check_for_circle(reporter, tmp, false);
1265}
1266
1267static void test_circle_translate(skiatest::Reporter* reporter,
1268 const SkPath& path) {
1269 SkPath tmp;
1270
1271 // translate at small offset
1272 SkMatrix m;
1273 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1274 path.transform(m, &tmp);
1275 check_for_circle(reporter, tmp, true);
1276
1277 tmp.reset();
1278 m.reset();
1279
1280 // translate at a relatively big offset
1281 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1282 path.transform(m, &tmp);
1283 check_for_circle(reporter, tmp, true);
1284}
1285
1286static void test_circle_rotate(skiatest::Reporter* reporter,
1287 const SkPath& path) {
1288 for (int angle = 0; angle < 360; ++angle) {
1289 SkPath tmp;
1290 SkMatrix m;
1291 m.setRotate(SkIntToScalar(angle));
1292 path.transform(m, &tmp);
1293
1294 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1295 // degrees is not an oval anymore, this can be improved. we made this
1296 // for the simplicity of our implementation.
1297 if (angle % 90 == 0) {
1298 check_for_circle(reporter, tmp, true);
1299 } else {
1300 check_for_circle(reporter, tmp, false);
1301 }
1302 }
1303}
1304
1305static void test_circle_with_direction(skiatest::Reporter* reporter,
1306 SkPath::Direction dir) {
1307 SkPath path;
1308
1309 // circle at origin
1310 path.addCircle(0, 0, SkIntToScalar(20), dir);
1311 check_for_circle(reporter, path, true);
1312 test_circle_rotate(reporter, path);
1313 test_circle_translate(reporter, path);
1314 test_circle_skew(reporter, path);
1315
1316 // circle at an offset at (10, 10)
1317 path.reset();
1318 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1319 SkIntToScalar(20), dir);
1320 check_for_circle(reporter, path, true);
1321 test_circle_rotate(reporter, path);
1322 test_circle_translate(reporter, path);
1323 test_circle_skew(reporter, path);
1324}
1325
1326static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1327 SkPath path;
1328 SkPath circle;
1329 SkPath rect;
1330 SkPath empty;
1331
1332 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1333 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1334 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1335
1336 SkMatrix translate;
1337 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1338
1339 // For simplicity, all the path concatenation related operations
1340 // would mark it non-circle, though in theory it's still a circle.
1341
1342 // empty + circle (translate)
1343 path = empty;
1344 path.addPath(circle, translate);
1345 check_for_circle(reporter, path, false);
1346
1347 // circle + empty (translate)
1348 path = circle;
1349 path.addPath(empty, translate);
1350 check_for_circle(reporter, path, false);
1351
1352 // test reverseAddPath
1353 path = circle;
1354 path.reverseAddPath(rect);
1355 check_for_circle(reporter, path, false);
1356}
1357
1358static void test_circle(skiatest::Reporter* reporter) {
1359 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1360 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1361
1362 // multiple addCircle()
1363 SkPath path;
1364 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1365 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1366 check_for_circle(reporter, path, false);
1367
1368 // some extra lineTo() would make isOval() fail
1369 path.reset();
1370 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1371 path.lineTo(0, 0);
1372 check_for_circle(reporter, path, false);
1373
1374 // not back to the original point
1375 path.reset();
1376 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1377 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1378 check_for_circle(reporter, path, false);
1379
1380 test_circle_with_add_paths(reporter);
1381}
1382
1383static void test_oval(skiatest::Reporter* reporter) {
1384 SkRect rect;
1385 SkMatrix m;
1386 SkPath path;
1387
1388 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1389 path.addOval(rect);
1390
1391 REPORTER_ASSERT(reporter, path.isOval(NULL));
1392
1393 m.setRotate(SkIntToScalar(90));
1394 SkPath tmp;
1395 path.transform(m, &tmp);
1396 // an oval rotated 90 degrees is still an oval.
1397 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1398
1399 m.reset();
1400 m.setRotate(SkIntToScalar(30));
1401 tmp.reset();
1402 path.transform(m, &tmp);
1403 // an oval rotated 30 degrees is not an oval anymore.
1404 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1405
1406 // since empty path being transformed.
1407 path.reset();
1408 tmp.reset();
1409 m.reset();
1410 path.transform(m, &tmp);
1411 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1412
1413 // empty path is not an oval
1414 tmp.reset();
1415 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1416
1417 // only has moveTo()s
1418 tmp.reset();
1419 tmp.moveTo(0, 0);
1420 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1421 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1422
1423 // mimic WebKit's calling convention,
1424 // call moveTo() first and then call addOval()
1425 path.reset();
1426 path.moveTo(0, 0);
1427 path.addOval(rect);
1428 REPORTER_ASSERT(reporter, path.isOval(NULL));
1429
1430 // copy path
1431 path.reset();
1432 tmp.reset();
1433 tmp.addOval(rect);
1434 path = tmp;
1435 REPORTER_ASSERT(reporter, path.isOval(NULL));
1436}
1437
caryclark@google.com42639cd2012-06-06 12:03:39 +00001438static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001439 {
1440 SkSize size;
1441 size.fWidth = 3.4f;
1442 size.width();
1443 size = SkSize::Make(3,4);
1444 SkISize isize = SkISize::Make(3,4);
1445 }
1446
1447 SkTSize<SkScalar>::Make(3,4);
1448
reed@android.com3abec1d2009-03-02 05:36:20 +00001449 SkPath p, p2;
1450 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001451
reed@android.com3abec1d2009-03-02 05:36:20 +00001452 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001453 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001454 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001455 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001456 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001457 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1458 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1459 REPORTER_ASSERT(reporter, p == p2);
1460 REPORTER_ASSERT(reporter, !(p != p2));
1461
reed@android.comd252db02009-04-01 18:31:44 +00001462 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001463
reed@android.com3abec1d2009-03-02 05:36:20 +00001464 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001465
reed@android.com6b82d1a2009-06-03 02:35:01 +00001466 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1467 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001468 // we have quads or cubics
1469 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001470 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001471
reed@android.com6b82d1a2009-06-03 02:35:01 +00001472 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001473 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001474 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001475
reed@android.com6b82d1a2009-06-03 02:35:01 +00001476 p.addOval(bounds);
1477 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001478 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001479
reed@android.com6b82d1a2009-06-03 02:35:01 +00001480 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001481 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001482 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001483 // we have only lines
1484 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001485 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001486
1487 REPORTER_ASSERT(reporter, p != p2);
1488 REPORTER_ASSERT(reporter, !(p == p2));
1489
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001490 // do getPoints and getVerbs return the right result
1491 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1492 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001493 SkPoint pts[4];
1494 int count = p.getPoints(pts, 4);
1495 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001496 uint8_t verbs[6];
1497 verbs[5] = 0xff;
1498 p.getVerbs(verbs, 5);
1499 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1500 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1501 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1502 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1503 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1504 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001505 bounds2.set(pts, 4);
1506 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001507
reed@android.com3abec1d2009-03-02 05:36:20 +00001508 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1509 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001510 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001511
reed@android.com3abec1d2009-03-02 05:36:20 +00001512 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001513 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001514 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1515 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001516
reed@android.com3abec1d2009-03-02 05:36:20 +00001517 // now force p to not be a rect
1518 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1519 p.addRect(bounds);
1520 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001521
reed@google.com7e6c4d12012-05-10 14:05:43 +00001522 test_isLine(reporter);
1523 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001524 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001525 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001526 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001527 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001528 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001529 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001530 test_flattening(reporter);
1531 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001532 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001533 test_iter(reporter);
1534 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001535 test_circle(reporter);
1536 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001537 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001538 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00001539 test_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001540}
1541
1542#include "TestClassDef.h"
1543DEFINE_TESTCLASS("Path", PathTestClass, TestPath)