blob: 8c00efbd95f6784ce47ba5575915bd95d6a6fdf2 [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
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000755static void write_and_read_back(skiatest::Reporter* reporter,
756 const SkPath& p) {
757 SkWriter32 writer(100);
758 writer.writePath(p);
759 size_t size = writer.size();
760 SkAutoMalloc storage(size);
761 writer.flatten(storage.get());
762 SkReader32 reader(storage.get(), size);
763
764 SkPath readBack;
765 REPORTER_ASSERT(reporter, readBack != p);
766 reader.readPath(&readBack);
767 REPORTER_ASSERT(reporter, readBack == p);
768
769 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
770 p.getConvexityOrUnknown());
771
772 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
773
774 const SkRect& origBounds = p.getBounds();
775 const SkRect& readBackBounds = readBack.getBounds();
776
777 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
778}
779
reed@google.com53effc52011-09-21 19:05:12 +0000780static void test_flattening(skiatest::Reporter* reporter) {
781 SkPath p;
782
783 static const SkPoint pts[] = {
784 { 0, 0 },
785 { SkIntToScalar(10), SkIntToScalar(10) },
786 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
787 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
788 };
789 p.moveTo(pts[0]);
790 p.lineTo(pts[1]);
791 p.quadTo(pts[2], pts[3]);
792 p.cubicTo(pts[4], pts[5], pts[6]);
793
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000794 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000795
796 // create a buffer that should be much larger than the path so we don't
797 // kill our stack if writer goes too far.
798 char buffer[1024];
799 uint32_t size1 = p.writeToMemory(NULL);
800 uint32_t size2 = p.writeToMemory(buffer);
801 REPORTER_ASSERT(reporter, size1 == size2);
802
803 SkPath p2;
804 uint32_t size3 = p2.readFromMemory(buffer);
805 REPORTER_ASSERT(reporter, size1 == size3);
806 REPORTER_ASSERT(reporter, p == p2);
807
808 char buffer2[1024];
809 size3 = p2.writeToMemory(buffer2);
810 REPORTER_ASSERT(reporter, size1 == size3);
811 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000812
813 // test persistence of the oval flag & convexity
814 {
815 SkPath oval;
816 SkRect rect = SkRect::MakeWH(10, 10);
817 oval.addOval(rect);
818
819 write_and_read_back(reporter, oval);
820 }
reed@google.com53effc52011-09-21 19:05:12 +0000821}
822
823static void test_transform(skiatest::Reporter* reporter) {
824 SkPath p, p1;
825
826 static const SkPoint pts[] = {
827 { 0, 0 },
828 { SkIntToScalar(10), SkIntToScalar(10) },
829 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
830 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
831 };
832 p.moveTo(pts[0]);
833 p.lineTo(pts[1]);
834 p.quadTo(pts[2], pts[3]);
835 p.cubicTo(pts[4], pts[5], pts[6]);
836
837 SkMatrix matrix;
838 matrix.reset();
839 p.transform(matrix, &p1);
840 REPORTER_ASSERT(reporter, p == p1);
841
842 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
843 p.transform(matrix, &p1);
844 SkPoint pts1[7];
845 int count = p1.getPoints(pts1, 7);
846 REPORTER_ASSERT(reporter, 7 == count);
847 for (int i = 0; i < count; ++i) {
848 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
849 REPORTER_ASSERT(reporter, newPt == pts1[i]);
850 }
851}
852
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000853static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000854 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000855 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000856
schenney@chromium.org7e963602012-06-13 17:05:43 +0000857 struct zeroPathTestData {
858 const char* testPath;
859 const size_t numResultPts;
860 const SkRect resultBound;
861 const SkPath::Verb* resultVerbs;
862 const size_t numResultVerbs;
863 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000864
schenney@chromium.org7e963602012-06-13 17:05:43 +0000865 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
866 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
867 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
868 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
869 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
870 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
871 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
872 static const SkPath::Verb resultVerbs8[] = {
873 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
874 };
875 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
876 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
877 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
878 static const SkPath::Verb resultVerbs12[] = {
879 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
880 };
881 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
882 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
883 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
884 static const SkPath::Verb resultVerbs16[] = {
885 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
886 };
887 static const struct zeroPathTestData gZeroLengthTests[] = {
888 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000889 { "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 +0000890 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000891 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
892 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
893 { "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) },
894 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
895 { "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) },
896 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
897 { "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) },
898 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
899 { "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) },
900 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
901 { "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 +0000902 SK_ARRAY_COUNT(resultVerbs14)
903 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000904 { "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) },
905 { "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 +0000906 SK_ARRAY_COUNT(resultVerbs16)
907 }
908 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000909
schenney@chromium.org7e963602012-06-13 17:05:43 +0000910 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
911 p.reset();
912 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
913 REPORTER_ASSERT(reporter, valid);
914 REPORTER_ASSERT(reporter, !p.isEmpty());
915 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
916 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
917 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
918 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
919 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
920 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000921 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000922}
923
924struct SegmentInfo {
925 SkPath fPath;
926 int fPointCount;
927};
928
reed@google.com10296cc2011-09-21 12:29:05 +0000929#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
930
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000931static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +0000932 SkPath p, p2;
933
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000934 p.moveTo(0, 0);
935 p.quadTo(100, 100, 200, 200);
936 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
937 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000938 p2 = p;
939 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000940 p.cubicTo(100, 100, 200, 200, 300, 300);
941 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
942 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +0000943 p2 = p;
944 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
945
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000946 p.reset();
947 p.moveTo(0, 0);
948 p.cubicTo(100, 100, 200, 200, 300, 300);
949 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +0000950 p2 = p;
951 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
952
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000953 REPORTER_ASSERT(reporter, !p.isEmpty());
954}
955
956static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000957 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000958 SkPoint pts[4];
959
960 // Test an iterator with no path
961 SkPath::Iter noPathIter;
962 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000963
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000964 // Test that setting an empty path works
965 noPathIter.setPath(p, false);
966 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000967
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000968 // Test that close path makes no difference for an empty path
969 noPathIter.setPath(p, true);
970 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000971
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000972 // Test an iterator with an initial empty path
973 SkPath::Iter iter(p, false);
974 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
975
976 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +0000977 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000978 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
979
schenney@chromium.org7e963602012-06-13 17:05:43 +0000980
981 struct iterTestData {
982 const char* testPath;
983 const bool forceClose;
984 const bool consumeDegenerates;
985 const size_t* numResultPtsPerVerb;
986 const SkPoint* resultPts;
987 const SkPath::Verb* resultVerbs;
988 const size_t numResultVerbs;
989 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000990
schenney@chromium.org7e963602012-06-13 17:05:43 +0000991 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
992 static const SkPath::Verb resultVerbs2[] = {
993 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
994 };
995 static const SkPath::Verb resultVerbs3[] = {
996 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
997 };
998 static const SkPath::Verb resultVerbs4[] = {
999 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1000 };
1001 static const SkPath::Verb resultVerbs5[] = {
1002 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1003 };
1004 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001005 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1006 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1007 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1008 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001009 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001010 static const SkPoint resultPts2[] = {
1011 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1012 };
1013 static const SkPoint resultPts3[] = {
1014 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1015 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1016 };
1017 static const SkPoint resultPts4[] = {
1018 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1019 };
1020 static const SkPoint resultPts5[] = {
1021 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1022 };
1023 static const struct iterTestData gIterTests[] = {
1024 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001025 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1026 { "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 +00001027 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1028 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1029 { "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) },
1030 { "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 +00001031 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1032 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1033 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1034 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1035 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1036 { "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 +00001037 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001038
schenney@chromium.org7e963602012-06-13 17:05:43 +00001039 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1040 p.reset();
1041 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1042 REPORTER_ASSERT(reporter, valid);
1043 iter.setPath(p, gIterTests[i].forceClose);
1044 int j = 0, l = 0;
1045 do {
1046 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1047 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1048 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1049 }
1050 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1051 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1052 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001053
1054 // The GM degeneratesegments.cpp test is more extensive
1055}
1056
1057static void test_raw_iter(skiatest::Reporter* reporter) {
1058 SkPath p;
1059 SkPoint pts[4];
1060
1061 // Test an iterator with no path
1062 SkPath::RawIter noPathIter;
1063 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1064 // Test that setting an empty path works
1065 noPathIter.setPath(p);
1066 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1067
1068 // Test an iterator with an initial empty path
1069 SkPath::RawIter iter(p);
1070 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1071
1072 // Test that a move-only path returns the move.
1073 p.moveTo(SK_Scalar1, 0);
1074 iter.setPath(p);
1075 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1076 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1077 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1078 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1079
1080 // No matter how many moves we add, we should get them all back
1081 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1082 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1083 iter.setPath(p);
1084 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1085 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1086 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1087 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1088 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1089 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1090 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1091 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1092 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1093 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1094
1095 // Initial close is never ever stored
1096 p.reset();
1097 p.close();
1098 iter.setPath(p);
1099 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1100
1101 // Move/close sequences
1102 p.reset();
1103 p.close(); // Not stored, no purpose
1104 p.moveTo(SK_Scalar1, 0);
1105 p.close();
1106 p.close(); // Not stored, no purpose
1107 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1108 p.close();
1109 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1110 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1111 p.close();
1112 iter.setPath(p);
1113 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1114 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1115 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1116 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1117 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1118 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1119 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1120 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1121 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1122 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1123 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1124 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1125 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1126 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1127 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1128 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1129 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1130 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1131 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1132 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1133 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1134 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1135
1136 // Generate random paths and verify
1137 SkPoint randomPts[25];
1138 for (int i = 0; i < 5; ++i) {
1139 for (int j = 0; j < 5; ++j) {
1140 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1141 }
1142 }
1143
1144 // Max of 10 segments, max 3 points per segment
1145 SkRandom rand(9876543);
1146 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001147 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001148 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001149
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001150 for (int i = 0; i < 500; ++i) {
1151 p.reset();
1152 bool lastWasClose = true;
1153 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001154 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001155 int numPoints = 0;
1156 int numVerbs = (rand.nextU() >> 16) % 10;
1157 int numIterVerbs = 0;
1158 for (int j = 0; j < numVerbs; ++j) {
1159 do {
1160 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1161 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001162 switch (nextVerb) {
1163 case SkPath::kMove_Verb:
1164 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1165 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001166 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001167 numPoints += 1;
1168 lastWasClose = false;
1169 haveMoveTo = true;
1170 break;
1171 case SkPath::kLine_Verb:
1172 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001173 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001174 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1175 haveMoveTo = true;
1176 }
1177 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1178 p.lineTo(expectedPts[numPoints]);
1179 numPoints += 1;
1180 lastWasClose = false;
1181 break;
1182 case SkPath::kQuad_Verb:
1183 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001184 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001185 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1186 haveMoveTo = true;
1187 }
1188 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1189 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1190 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1191 numPoints += 2;
1192 lastWasClose = false;
1193 break;
1194 case SkPath::kCubic_Verb:
1195 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001196 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001197 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1198 haveMoveTo = true;
1199 }
1200 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1201 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1202 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1203 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1204 expectedPts[numPoints + 2]);
1205 numPoints += 3;
1206 lastWasClose = false;
1207 break;
1208 case SkPath::kClose_Verb:
1209 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001210 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001211 lastWasClose = true;
1212 break;
1213 default:;
1214 }
1215 expectedVerbs[numIterVerbs++] = nextVerb;
1216 }
1217
1218 iter.setPath(p);
1219 numVerbs = numIterVerbs;
1220 numIterVerbs = 0;
1221 int numIterPts = 0;
1222 SkPoint lastMoveTo;
1223 SkPoint lastPt;
1224 lastMoveTo.set(0, 0);
1225 lastPt.set(0, 0);
1226 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1227 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1228 numIterVerbs++;
1229 switch (nextVerb) {
1230 case SkPath::kMove_Verb:
1231 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1232 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1233 lastPt = lastMoveTo = pts[0];
1234 numIterPts += 1;
1235 break;
1236 case SkPath::kLine_Verb:
1237 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1238 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1239 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1240 lastPt = pts[1];
1241 numIterPts += 1;
1242 break;
1243 case SkPath::kQuad_Verb:
1244 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1245 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1246 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1247 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1248 lastPt = pts[2];
1249 numIterPts += 2;
1250 break;
1251 case SkPath::kCubic_Verb:
1252 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1253 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1254 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1255 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1256 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1257 lastPt = pts[3];
1258 numIterPts += 3;
1259 break;
1260 case SkPath::kClose_Verb:
1261 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1262 lastPt = lastMoveTo;
1263 break;
1264 default:;
1265 }
1266 }
1267 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1268 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1269 }
1270}
1271
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001272static void check_for_circle(skiatest::Reporter* reporter,
1273 const SkPath& path, bool expected) {
1274 SkRect rect;
1275 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1276 if (expected) {
1277 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1278 }
1279}
1280
1281static void test_circle_skew(skiatest::Reporter* reporter,
1282 const SkPath& path) {
1283 SkPath tmp;
1284
1285 SkMatrix m;
1286 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1287 path.transform(m, &tmp);
1288 check_for_circle(reporter, tmp, false);
1289}
1290
1291static void test_circle_translate(skiatest::Reporter* reporter,
1292 const SkPath& path) {
1293 SkPath tmp;
1294
1295 // translate at small offset
1296 SkMatrix m;
1297 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1298 path.transform(m, &tmp);
1299 check_for_circle(reporter, tmp, true);
1300
1301 tmp.reset();
1302 m.reset();
1303
1304 // translate at a relatively big offset
1305 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1306 path.transform(m, &tmp);
1307 check_for_circle(reporter, tmp, true);
1308}
1309
1310static void test_circle_rotate(skiatest::Reporter* reporter,
1311 const SkPath& path) {
1312 for (int angle = 0; angle < 360; ++angle) {
1313 SkPath tmp;
1314 SkMatrix m;
1315 m.setRotate(SkIntToScalar(angle));
1316 path.transform(m, &tmp);
1317
1318 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1319 // degrees is not an oval anymore, this can be improved. we made this
1320 // for the simplicity of our implementation.
1321 if (angle % 90 == 0) {
1322 check_for_circle(reporter, tmp, true);
1323 } else {
1324 check_for_circle(reporter, tmp, false);
1325 }
1326 }
1327}
1328
1329static void test_circle_with_direction(skiatest::Reporter* reporter,
1330 SkPath::Direction dir) {
1331 SkPath path;
1332
1333 // circle at origin
1334 path.addCircle(0, 0, SkIntToScalar(20), dir);
1335 check_for_circle(reporter, path, true);
1336 test_circle_rotate(reporter, path);
1337 test_circle_translate(reporter, path);
1338 test_circle_skew(reporter, path);
1339
1340 // circle at an offset at (10, 10)
1341 path.reset();
1342 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1343 SkIntToScalar(20), dir);
1344 check_for_circle(reporter, path, true);
1345 test_circle_rotate(reporter, path);
1346 test_circle_translate(reporter, path);
1347 test_circle_skew(reporter, path);
1348}
1349
1350static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1351 SkPath path;
1352 SkPath circle;
1353 SkPath rect;
1354 SkPath empty;
1355
1356 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1357 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1358 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1359
1360 SkMatrix translate;
1361 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1362
1363 // For simplicity, all the path concatenation related operations
1364 // would mark it non-circle, though in theory it's still a circle.
1365
1366 // empty + circle (translate)
1367 path = empty;
1368 path.addPath(circle, translate);
1369 check_for_circle(reporter, path, false);
1370
1371 // circle + empty (translate)
1372 path = circle;
1373 path.addPath(empty, translate);
1374 check_for_circle(reporter, path, false);
1375
1376 // test reverseAddPath
1377 path = circle;
1378 path.reverseAddPath(rect);
1379 check_for_circle(reporter, path, false);
1380}
1381
1382static void test_circle(skiatest::Reporter* reporter) {
1383 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1384 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1385
1386 // multiple addCircle()
1387 SkPath path;
1388 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1389 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1390 check_for_circle(reporter, path, false);
1391
1392 // some extra lineTo() would make isOval() fail
1393 path.reset();
1394 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1395 path.lineTo(0, 0);
1396 check_for_circle(reporter, path, false);
1397
1398 // not back to the original point
1399 path.reset();
1400 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1401 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1402 check_for_circle(reporter, path, false);
1403
1404 test_circle_with_add_paths(reporter);
1405}
1406
1407static void test_oval(skiatest::Reporter* reporter) {
1408 SkRect rect;
1409 SkMatrix m;
1410 SkPath path;
1411
1412 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1413 path.addOval(rect);
1414
1415 REPORTER_ASSERT(reporter, path.isOval(NULL));
1416
1417 m.setRotate(SkIntToScalar(90));
1418 SkPath tmp;
1419 path.transform(m, &tmp);
1420 // an oval rotated 90 degrees is still an oval.
1421 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1422
1423 m.reset();
1424 m.setRotate(SkIntToScalar(30));
1425 tmp.reset();
1426 path.transform(m, &tmp);
1427 // an oval rotated 30 degrees is not an oval anymore.
1428 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1429
1430 // since empty path being transformed.
1431 path.reset();
1432 tmp.reset();
1433 m.reset();
1434 path.transform(m, &tmp);
1435 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1436
1437 // empty path is not an oval
1438 tmp.reset();
1439 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1440
1441 // only has moveTo()s
1442 tmp.reset();
1443 tmp.moveTo(0, 0);
1444 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1445 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1446
1447 // mimic WebKit's calling convention,
1448 // call moveTo() first and then call addOval()
1449 path.reset();
1450 path.moveTo(0, 0);
1451 path.addOval(rect);
1452 REPORTER_ASSERT(reporter, path.isOval(NULL));
1453
1454 // copy path
1455 path.reset();
1456 tmp.reset();
1457 tmp.addOval(rect);
1458 path = tmp;
1459 REPORTER_ASSERT(reporter, path.isOval(NULL));
1460}
1461
caryclark@google.com42639cd2012-06-06 12:03:39 +00001462static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001463 {
1464 SkSize size;
1465 size.fWidth = 3.4f;
1466 size.width();
1467 size = SkSize::Make(3,4);
1468 SkISize isize = SkISize::Make(3,4);
1469 }
1470
1471 SkTSize<SkScalar>::Make(3,4);
1472
reed@android.com3abec1d2009-03-02 05:36:20 +00001473 SkPath p, p2;
1474 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001475
reed@android.com3abec1d2009-03-02 05:36:20 +00001476 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001477 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001478 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001479 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001480 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001481 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1482 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1483 REPORTER_ASSERT(reporter, p == p2);
1484 REPORTER_ASSERT(reporter, !(p != p2));
1485
reed@android.comd252db02009-04-01 18:31:44 +00001486 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001487
reed@android.com3abec1d2009-03-02 05:36:20 +00001488 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001489
reed@android.com6b82d1a2009-06-03 02:35:01 +00001490 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1491 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001492 // we have quads or cubics
1493 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001494 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001495
reed@android.com6b82d1a2009-06-03 02:35:01 +00001496 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001497 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001498 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001499
reed@android.com6b82d1a2009-06-03 02:35:01 +00001500 p.addOval(bounds);
1501 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001502 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001503
reed@android.com6b82d1a2009-06-03 02:35:01 +00001504 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001505 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001506 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001507 // we have only lines
1508 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001509 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001510
1511 REPORTER_ASSERT(reporter, p != p2);
1512 REPORTER_ASSERT(reporter, !(p == p2));
1513
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001514 // do getPoints and getVerbs return the right result
1515 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1516 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001517 SkPoint pts[4];
1518 int count = p.getPoints(pts, 4);
1519 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001520 uint8_t verbs[6];
1521 verbs[5] = 0xff;
1522 p.getVerbs(verbs, 5);
1523 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1524 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1525 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1526 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1527 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1528 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001529 bounds2.set(pts, 4);
1530 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001531
reed@android.com3abec1d2009-03-02 05:36:20 +00001532 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1533 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001534 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001535
reed@android.com3abec1d2009-03-02 05:36:20 +00001536 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001537 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001538 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1539 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001540
reed@android.com3abec1d2009-03-02 05:36:20 +00001541 // now force p to not be a rect
1542 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1543 p.addRect(bounds);
1544 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001545
reed@google.com7e6c4d12012-05-10 14:05:43 +00001546 test_isLine(reporter);
1547 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001548 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001549 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001550 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001551 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001552 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001553 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001554 test_flattening(reporter);
1555 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001556 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001557 test_iter(reporter);
1558 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001559 test_circle(reporter);
1560 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001561 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001562 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00001563 test_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001564}
1565
1566#include "TestClassDef.h"
1567DEFINE_TESTCLASS("Path", PathTestClass, TestPath)