blob: e23ee0e9b18fab3238f7c140dd4396da4a28f869 [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) {
908 SkPath p;
909 p.moveTo(0, 0);
910 p.quadTo(100, 100, 200, 200);
911 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
912 REPORTER_ASSERT(reporter, !p.isEmpty());
913 p.cubicTo(100, 100, 200, 200, 300, 300);
914 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
915 REPORTER_ASSERT(reporter, !p.isEmpty());
916 p.reset();
917 p.moveTo(0, 0);
918 p.cubicTo(100, 100, 200, 200, 300, 300);
919 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
920 REPORTER_ASSERT(reporter, !p.isEmpty());
921}
922
923static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000924 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000925 SkPoint pts[4];
926
927 // Test an iterator with no path
928 SkPath::Iter noPathIter;
929 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000930
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000931 // Test that setting an empty path works
932 noPathIter.setPath(p, false);
933 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000934
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000935 // Test that close path makes no difference for an empty path
936 noPathIter.setPath(p, true);
937 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000938
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000939 // Test an iterator with an initial empty path
940 SkPath::Iter iter(p, false);
941 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
942
943 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +0000944 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000945 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
946
schenney@chromium.org7e963602012-06-13 17:05:43 +0000947
948 struct iterTestData {
949 const char* testPath;
950 const bool forceClose;
951 const bool consumeDegenerates;
952 const size_t* numResultPtsPerVerb;
953 const SkPoint* resultPts;
954 const SkPath::Verb* resultVerbs;
955 const size_t numResultVerbs;
956 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000957
schenney@chromium.org7e963602012-06-13 17:05:43 +0000958 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
959 static const SkPath::Verb resultVerbs2[] = {
960 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
961 };
962 static const SkPath::Verb resultVerbs3[] = {
963 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
964 };
965 static const SkPath::Verb resultVerbs4[] = {
966 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
967 };
968 static const SkPath::Verb resultVerbs5[] = {
969 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
970 };
971 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +0000972 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
973 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
974 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
975 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000976 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000977 static const SkPoint resultPts2[] = {
978 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
979 };
980 static const SkPoint resultPts3[] = {
981 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
982 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
983 };
984 static const SkPoint resultPts4[] = {
985 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
986 };
987 static const SkPoint resultPts5[] = {
988 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
989 };
990 static const struct iterTestData gIterTests[] = {
991 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000992 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
993 { "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 +0000994 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
995 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
996 { "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) },
997 { "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 +0000998 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
999 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1000 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1001 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1002 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1003 { "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 +00001004 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001005
schenney@chromium.org7e963602012-06-13 17:05:43 +00001006 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1007 p.reset();
1008 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1009 REPORTER_ASSERT(reporter, valid);
1010 iter.setPath(p, gIterTests[i].forceClose);
1011 int j = 0, l = 0;
1012 do {
1013 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1014 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1015 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1016 }
1017 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1018 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1019 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001020
1021 // The GM degeneratesegments.cpp test is more extensive
1022}
1023
1024static void test_raw_iter(skiatest::Reporter* reporter) {
1025 SkPath p;
1026 SkPoint pts[4];
1027
1028 // Test an iterator with no path
1029 SkPath::RawIter noPathIter;
1030 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1031 // Test that setting an empty path works
1032 noPathIter.setPath(p);
1033 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1034
1035 // Test an iterator with an initial empty path
1036 SkPath::RawIter iter(p);
1037 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1038
1039 // Test that a move-only path returns the move.
1040 p.moveTo(SK_Scalar1, 0);
1041 iter.setPath(p);
1042 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1043 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1044 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1045 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1046
1047 // No matter how many moves we add, we should get them all back
1048 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1049 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
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::kMove_Verb);
1055 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1056 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1057 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1058 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1059 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1060 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1061
1062 // Initial close is never ever stored
1063 p.reset();
1064 p.close();
1065 iter.setPath(p);
1066 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1067
1068 // Move/close sequences
1069 p.reset();
1070 p.close(); // Not stored, no purpose
1071 p.moveTo(SK_Scalar1, 0);
1072 p.close();
1073 p.close(); // Not stored, no purpose
1074 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1075 p.close();
1076 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1077 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1078 p.close();
1079 iter.setPath(p);
1080 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1081 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1082 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1083 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1084 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1085 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1086 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1087 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1088 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1089 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1090 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1091 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1092 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1093 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1094 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1095 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1096 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1097 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1098 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1099 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1100 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1101 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1102
1103 // Generate random paths and verify
1104 SkPoint randomPts[25];
1105 for (int i = 0; i < 5; ++i) {
1106 for (int j = 0; j < 5; ++j) {
1107 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1108 }
1109 }
1110
1111 // Max of 10 segments, max 3 points per segment
1112 SkRandom rand(9876543);
1113 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001114 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001115 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001116
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001117 for (int i = 0; i < 500; ++i) {
1118 p.reset();
1119 bool lastWasClose = true;
1120 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001121 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001122 int numPoints = 0;
1123 int numVerbs = (rand.nextU() >> 16) % 10;
1124 int numIterVerbs = 0;
1125 for (int j = 0; j < numVerbs; ++j) {
1126 do {
1127 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1128 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001129 switch (nextVerb) {
1130 case SkPath::kMove_Verb:
1131 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1132 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001133 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001134 numPoints += 1;
1135 lastWasClose = false;
1136 haveMoveTo = true;
1137 break;
1138 case SkPath::kLine_Verb:
1139 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001140 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001141 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1142 haveMoveTo = true;
1143 }
1144 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1145 p.lineTo(expectedPts[numPoints]);
1146 numPoints += 1;
1147 lastWasClose = false;
1148 break;
1149 case SkPath::kQuad_Verb:
1150 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001151 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001152 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1153 haveMoveTo = true;
1154 }
1155 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1156 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1157 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1158 numPoints += 2;
1159 lastWasClose = false;
1160 break;
1161 case SkPath::kCubic_Verb:
1162 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001163 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001164 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1165 haveMoveTo = true;
1166 }
1167 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1168 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1169 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1170 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1171 expectedPts[numPoints + 2]);
1172 numPoints += 3;
1173 lastWasClose = false;
1174 break;
1175 case SkPath::kClose_Verb:
1176 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001177 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001178 lastWasClose = true;
1179 break;
1180 default:;
1181 }
1182 expectedVerbs[numIterVerbs++] = nextVerb;
1183 }
1184
1185 iter.setPath(p);
1186 numVerbs = numIterVerbs;
1187 numIterVerbs = 0;
1188 int numIterPts = 0;
1189 SkPoint lastMoveTo;
1190 SkPoint lastPt;
1191 lastMoveTo.set(0, 0);
1192 lastPt.set(0, 0);
1193 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1194 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1195 numIterVerbs++;
1196 switch (nextVerb) {
1197 case SkPath::kMove_Verb:
1198 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1199 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1200 lastPt = lastMoveTo = pts[0];
1201 numIterPts += 1;
1202 break;
1203 case SkPath::kLine_Verb:
1204 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1205 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1206 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1207 lastPt = pts[1];
1208 numIterPts += 1;
1209 break;
1210 case SkPath::kQuad_Verb:
1211 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1212 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1213 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1214 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1215 lastPt = pts[2];
1216 numIterPts += 2;
1217 break;
1218 case SkPath::kCubic_Verb:
1219 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1220 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1221 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1222 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1223 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1224 lastPt = pts[3];
1225 numIterPts += 3;
1226 break;
1227 case SkPath::kClose_Verb:
1228 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1229 lastPt = lastMoveTo;
1230 break;
1231 default:;
1232 }
1233 }
1234 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1235 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1236 }
1237}
1238
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001239static void check_for_circle(skiatest::Reporter* reporter,
1240 const SkPath& path, bool expected) {
1241 SkRect rect;
1242 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1243 if (expected) {
1244 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1245 }
1246}
1247
1248static void test_circle_skew(skiatest::Reporter* reporter,
1249 const SkPath& path) {
1250 SkPath tmp;
1251
1252 SkMatrix m;
1253 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1254 path.transform(m, &tmp);
1255 check_for_circle(reporter, tmp, false);
1256}
1257
1258static void test_circle_translate(skiatest::Reporter* reporter,
1259 const SkPath& path) {
1260 SkPath tmp;
1261
1262 // translate at small offset
1263 SkMatrix m;
1264 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1265 path.transform(m, &tmp);
1266 check_for_circle(reporter, tmp, true);
1267
1268 tmp.reset();
1269 m.reset();
1270
1271 // translate at a relatively big offset
1272 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1273 path.transform(m, &tmp);
1274 check_for_circle(reporter, tmp, true);
1275}
1276
1277static void test_circle_rotate(skiatest::Reporter* reporter,
1278 const SkPath& path) {
1279 for (int angle = 0; angle < 360; ++angle) {
1280 SkPath tmp;
1281 SkMatrix m;
1282 m.setRotate(SkIntToScalar(angle));
1283 path.transform(m, &tmp);
1284
1285 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1286 // degrees is not an oval anymore, this can be improved. we made this
1287 // for the simplicity of our implementation.
1288 if (angle % 90 == 0) {
1289 check_for_circle(reporter, tmp, true);
1290 } else {
1291 check_for_circle(reporter, tmp, false);
1292 }
1293 }
1294}
1295
1296static void test_circle_with_direction(skiatest::Reporter* reporter,
1297 SkPath::Direction dir) {
1298 SkPath path;
1299
1300 // circle at origin
1301 path.addCircle(0, 0, SkIntToScalar(20), dir);
1302 check_for_circle(reporter, path, true);
1303 test_circle_rotate(reporter, path);
1304 test_circle_translate(reporter, path);
1305 test_circle_skew(reporter, path);
1306
1307 // circle at an offset at (10, 10)
1308 path.reset();
1309 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1310 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
1317static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1318 SkPath path;
1319 SkPath circle;
1320 SkPath rect;
1321 SkPath empty;
1322
1323 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1324 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1325 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1326
1327 SkMatrix translate;
1328 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1329
1330 // For simplicity, all the path concatenation related operations
1331 // would mark it non-circle, though in theory it's still a circle.
1332
1333 // empty + circle (translate)
1334 path = empty;
1335 path.addPath(circle, translate);
1336 check_for_circle(reporter, path, false);
1337
1338 // circle + empty (translate)
1339 path = circle;
1340 path.addPath(empty, translate);
1341 check_for_circle(reporter, path, false);
1342
1343 // test reverseAddPath
1344 path = circle;
1345 path.reverseAddPath(rect);
1346 check_for_circle(reporter, path, false);
1347}
1348
1349static void test_circle(skiatest::Reporter* reporter) {
1350 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1351 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1352
1353 // multiple addCircle()
1354 SkPath path;
1355 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1356 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1357 check_for_circle(reporter, path, false);
1358
1359 // some extra lineTo() would make isOval() fail
1360 path.reset();
1361 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1362 path.lineTo(0, 0);
1363 check_for_circle(reporter, path, false);
1364
1365 // not back to the original point
1366 path.reset();
1367 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1368 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1369 check_for_circle(reporter, path, false);
1370
1371 test_circle_with_add_paths(reporter);
1372}
1373
1374static void test_oval(skiatest::Reporter* reporter) {
1375 SkRect rect;
1376 SkMatrix m;
1377 SkPath path;
1378
1379 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1380 path.addOval(rect);
1381
1382 REPORTER_ASSERT(reporter, path.isOval(NULL));
1383
1384 m.setRotate(SkIntToScalar(90));
1385 SkPath tmp;
1386 path.transform(m, &tmp);
1387 // an oval rotated 90 degrees is still an oval.
1388 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1389
1390 m.reset();
1391 m.setRotate(SkIntToScalar(30));
1392 tmp.reset();
1393 path.transform(m, &tmp);
1394 // an oval rotated 30 degrees is not an oval anymore.
1395 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1396
1397 // since empty path being transformed.
1398 path.reset();
1399 tmp.reset();
1400 m.reset();
1401 path.transform(m, &tmp);
1402 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1403
1404 // empty path is not an oval
1405 tmp.reset();
1406 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1407
1408 // only has moveTo()s
1409 tmp.reset();
1410 tmp.moveTo(0, 0);
1411 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1412 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1413
1414 // mimic WebKit's calling convention,
1415 // call moveTo() first and then call addOval()
1416 path.reset();
1417 path.moveTo(0, 0);
1418 path.addOval(rect);
1419 REPORTER_ASSERT(reporter, path.isOval(NULL));
1420
1421 // copy path
1422 path.reset();
1423 tmp.reset();
1424 tmp.addOval(rect);
1425 path = tmp;
1426 REPORTER_ASSERT(reporter, path.isOval(NULL));
1427}
1428
caryclark@google.com42639cd2012-06-06 12:03:39 +00001429static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001430 {
1431 SkSize size;
1432 size.fWidth = 3.4f;
1433 size.width();
1434 size = SkSize::Make(3,4);
1435 SkISize isize = SkISize::Make(3,4);
1436 }
1437
1438 SkTSize<SkScalar>::Make(3,4);
1439
reed@android.com3abec1d2009-03-02 05:36:20 +00001440 SkPath p, p2;
1441 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001442
reed@android.com3abec1d2009-03-02 05:36:20 +00001443 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001444 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001445 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001446 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001447 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001448 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1449 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1450 REPORTER_ASSERT(reporter, p == p2);
1451 REPORTER_ASSERT(reporter, !(p != p2));
1452
reed@android.comd252db02009-04-01 18:31:44 +00001453 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001454
reed@android.com3abec1d2009-03-02 05:36:20 +00001455 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001456
reed@android.com6b82d1a2009-06-03 02:35:01 +00001457 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1458 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001459 // we have quads or cubics
1460 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001461 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001462
reed@android.com6b82d1a2009-06-03 02:35:01 +00001463 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001464 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001465 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001466
reed@android.com6b82d1a2009-06-03 02:35:01 +00001467 p.addOval(bounds);
1468 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001469 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001470
reed@android.com6b82d1a2009-06-03 02:35:01 +00001471 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001472 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001473 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001474 // we have only lines
1475 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001476 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001477
1478 REPORTER_ASSERT(reporter, p != p2);
1479 REPORTER_ASSERT(reporter, !(p == p2));
1480
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001481 // do getPoints and getVerbs return the right result
1482 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1483 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001484 SkPoint pts[4];
1485 int count = p.getPoints(pts, 4);
1486 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001487 uint8_t verbs[6];
1488 verbs[5] = 0xff;
1489 p.getVerbs(verbs, 5);
1490 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1491 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1492 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1493 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1494 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1495 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001496 bounds2.set(pts, 4);
1497 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001498
reed@android.com3abec1d2009-03-02 05:36:20 +00001499 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1500 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001501 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001502
reed@android.com3abec1d2009-03-02 05:36:20 +00001503 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001504 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001505 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1506 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001507
reed@android.com3abec1d2009-03-02 05:36:20 +00001508 // now force p to not be a rect
1509 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1510 p.addRect(bounds);
1511 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001512
reed@google.com7e6c4d12012-05-10 14:05:43 +00001513 test_isLine(reporter);
1514 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001515 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001516 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001517 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001518 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001519 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001520 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001521 test_flattening(reporter);
1522 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001523 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001524 test_iter(reporter);
1525 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001526 test_circle(reporter);
1527 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001528 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001529 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00001530 test_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001531}
1532
1533#include "TestClassDef.h"
1534DEFINE_TESTCLASS("Path", PathTestClass, TestPath)