blob: 21f78bf2ce6ccb2679e4bb4d865a2aa8f5046534 [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.com744faba2012-05-29 19:54:52 +000019// assert that we always
20// start with a moveTo
21// only have 1 moveTo
22// only have Lines after that
23// end with a single close
24// only have (at most) 1 close
25//
26static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
27 const SkPoint srcPts[], int count, bool expectClose) {
28 SkPath::RawIter iter(path);
29 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +000030
31 bool firstTime = true;
32 bool foundClose = false;
33 for (;;) {
34 switch (iter.next(pts)) {
35 case SkPath::kMove_Verb:
36 REPORTER_ASSERT(reporter, firstTime);
37 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
38 srcPts++;
39 firstTime = false;
40 break;
41 case SkPath::kLine_Verb:
42 REPORTER_ASSERT(reporter, !firstTime);
43 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
44 srcPts++;
45 break;
46 case SkPath::kQuad_Verb:
47 REPORTER_ASSERT(reporter, !"unexpected quad verb");
48 break;
49 case SkPath::kCubic_Verb:
50 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
51 break;
52 case SkPath::kClose_Verb:
53 REPORTER_ASSERT(reporter, !firstTime);
54 REPORTER_ASSERT(reporter, !foundClose);
55 REPORTER_ASSERT(reporter, expectClose);
56 foundClose = true;
57 break;
58 case SkPath::kDone_Verb:
59 goto DONE;
60 }
61 }
62DONE:
63 REPORTER_ASSERT(reporter, foundClose == expectClose);
64}
65
66static void test_addPoly(skiatest::Reporter* reporter) {
67 SkPoint pts[32];
68 SkRandom rand;
69
70 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
71 pts[i].fX = rand.nextSScalar1();
72 pts[i].fY = rand.nextSScalar1();
73 }
74
75 for (int doClose = 0; doClose <= 1; ++doClose) {
76 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
77 SkPath path;
78 path.addPoly(pts, count, SkToBool(doClose));
79 test_poly(reporter, path, pts, count, SkToBool(doClose));
80 }
81 }
82}
83
reed@google.com8b06f1a2012-05-29 12:03:46 +000084static void test_strokerec(skiatest::Reporter* reporter) {
85 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
86 REPORTER_ASSERT(reporter, rec.isFillStyle());
87
88 rec.setHairlineStyle();
89 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
90
91 rec.setStrokeStyle(SK_Scalar1, false);
92 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
93
94 rec.setStrokeStyle(SK_Scalar1, true);
95 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
96
97 rec.setStrokeStyle(0, false);
98 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
99
100 rec.setStrokeStyle(0, true);
101 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
102}
103
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000104/**
105 * cheapIsDirection can take a shortcut when a path is marked convex.
106 * This function ensures that we always test cheapIsDirection when the path
107 * is flagged with unknown convexity status.
108 */
109static void check_direction(SkPath* path,
110 SkPath::Direction expectedDir,
111 skiatest::Reporter* reporter) {
112 if (SkPath::kConvex_Convexity == path->getConvexity()) {
113 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
114 path->setConvexity(SkPath::kUnknown_Convexity);
115 }
116 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
117}
118
reed@google.com3e71a882012-01-10 18:44:37 +0000119static void test_direction(skiatest::Reporter* reporter) {
120 size_t i;
121 SkPath path;
122 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
123 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
124 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
125
126 static const char* gDegen[] = {
127 "M 10 10",
128 "M 10 10 M 20 20",
129 "M 10 10 L 20 20",
130 "M 10 10 L 10 10 L 10 10",
131 "M 10 10 Q 10 10 10 10",
132 "M 10 10 C 10 10 10 10 10 10",
133 };
134 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
135 path.reset();
136 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
137 REPORTER_ASSERT(reporter, valid);
138 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
139 }
140
141 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000142 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000143 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000144 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
reed@google.com3e71a882012-01-10 18:44:37 +0000145 };
146 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
147 path.reset();
148 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
149 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000150 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000151 }
152
153 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000154 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000155 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000156 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
reed@google.com3e71a882012-01-10 18:44:37 +0000157 };
158 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
159 path.reset();
160 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
161 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000162 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000163 }
reed@google.comac8543f2012-01-30 20:51:25 +0000164
165 // Test two donuts, each wound a different direction. Only the outer contour
166 // determines the cheap direction
167 path.reset();
168 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
169 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000170 check_direction(&path, SkPath::kCW_Direction, reporter);
171
reed@google.comac8543f2012-01-30 20:51:25 +0000172 path.reset();
173 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
174 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000175 check_direction(&path, SkPath::kCCW_Direction, reporter);
176
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000177#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000178 // triangle with one point really far from the origin.
179 path.reset();
180 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000181 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
182 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
183 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
184 check_direction(&path, SkPath::kCCW_Direction, reporter);
185#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000186}
187
reed@google.comffdb0182011-11-14 19:29:14 +0000188static void add_rect(SkPath* path, const SkRect& r) {
189 path->moveTo(r.fLeft, r.fTop);
190 path->lineTo(r.fRight, r.fTop);
191 path->lineTo(r.fRight, r.fBottom);
192 path->lineTo(r.fLeft, r.fBottom);
193 path->close();
194}
195
196static void test_bounds(skiatest::Reporter* reporter) {
197 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000198 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
199 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
200 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
201 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000202 };
203
204 SkPath path0, path1;
205 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
206 path0.addRect(rects[i]);
207 add_rect(&path1, rects[i]);
208 }
209
210 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
211}
212
reed@google.com55b5f4b2011-09-07 12:23:41 +0000213static void stroke_cubic(const SkPoint pts[4]) {
214 SkPath path;
215 path.moveTo(pts[0]);
216 path.cubicTo(pts[1], pts[2], pts[3]);
217
218 SkPaint paint;
219 paint.setStyle(SkPaint::kStroke_Style);
220 paint.setStrokeWidth(SK_Scalar1 * 2);
221
222 SkPath fill;
223 paint.getFillPath(path, &fill);
224}
225
226// just ensure this can run w/o any SkASSERTS firing in the debug build
227// we used to assert due to differences in how we determine a degenerate vector
228// but that was fixed with the introduction of SkPoint::CanNormalize
229static void stroke_tiny_cubic() {
230 SkPoint p0[] = {
231 { 372.0f, 92.0f },
232 { 372.0f, 92.0f },
233 { 372.0f, 92.0f },
234 { 372.0f, 92.0f },
235 };
236
237 stroke_cubic(p0);
238
239 SkPoint p1[] = {
240 { 372.0f, 92.0f },
241 { 372.0007f, 92.000755f },
242 { 371.99927f, 92.003922f },
243 { 371.99826f, 92.003899f },
244 };
245
246 stroke_cubic(p1);
247}
248
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000249static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
250 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000251 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000252 SkPoint mv;
253 SkPoint pts[4];
254 SkPath::Verb v;
255 int nMT = 0;
256 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000257 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000258 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
259 switch (v) {
260 case SkPath::kMove_Verb:
261 mv = pts[0];
262 ++nMT;
263 break;
264 case SkPath::kClose_Verb:
265 REPORTER_ASSERT(reporter, mv == pts[0]);
266 ++nCL;
267 break;
268 default:
269 break;
270 }
271 }
272 // if we force a close on the interator we should have a close
273 // for every moveTo
274 REPORTER_ASSERT(reporter, !i || nMT == nCL);
275 }
276}
277
278static void test_close(skiatest::Reporter* reporter) {
279 SkPath closePt;
280 closePt.moveTo(0, 0);
281 closePt.close();
282 check_close(reporter, closePt);
283
284 SkPath openPt;
285 openPt.moveTo(0, 0);
286 check_close(reporter, openPt);
287
288 SkPath empty;
289 check_close(reporter, empty);
290 empty.close();
291 check_close(reporter, empty);
292
293 SkPath rect;
294 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
295 check_close(reporter, rect);
296 rect.close();
297 check_close(reporter, rect);
298
299 SkPath quad;
300 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
301 check_close(reporter, quad);
302 quad.close();
303 check_close(reporter, quad);
304
305 SkPath cubic;
306 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
307 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
308 check_close(reporter, cubic);
309 cubic.close();
310 check_close(reporter, cubic);
311
312 SkPath line;
313 line.moveTo(SK_Scalar1, SK_Scalar1);
314 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
315 check_close(reporter, line);
316 line.close();
317 check_close(reporter, line);
318
319 SkPath rect2;
320 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
321 rect2.close();
322 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
323 check_close(reporter, rect2);
324 rect2.close();
325 check_close(reporter, rect2);
326
327 SkPath oval3;
328 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
329 oval3.close();
330 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
331 check_close(reporter, oval3);
332 oval3.close();
333 check_close(reporter, oval3);
334
335 SkPath moves;
336 moves.moveTo(SK_Scalar1, SK_Scalar1);
337 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
338 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
339 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
340 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000341
342 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000343}
344
reed@google.com7c424812011-05-15 04:38:34 +0000345static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
346 SkPath::Convexity expected) {
347 SkPath::Convexity c = SkPath::ComputeConvexity(path);
348 REPORTER_ASSERT(reporter, c == expected);
349}
350
351static void test_convexity2(skiatest::Reporter* reporter) {
352 SkPath pt;
353 pt.moveTo(0, 0);
354 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000355 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000356
357 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000358 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
359 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000360 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000361 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000362
363 SkPath triLeft;
364 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000365 triLeft.lineTo(SK_Scalar1, 0);
366 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000367 triLeft.close();
368 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
369
370 SkPath triRight;
371 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000372 triRight.lineTo(-SK_Scalar1, 0);
373 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000374 triRight.close();
375 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
376
377 SkPath square;
378 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000379 square.lineTo(SK_Scalar1, 0);
380 square.lineTo(SK_Scalar1, SK_Scalar1);
381 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000382 square.close();
383 check_convexity(reporter, square, SkPath::kConvex_Convexity);
384
385 SkPath redundantSquare;
386 redundantSquare.moveTo(0, 0);
387 redundantSquare.lineTo(0, 0);
388 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000389 redundantSquare.lineTo(SK_Scalar1, 0);
390 redundantSquare.lineTo(SK_Scalar1, 0);
391 redundantSquare.lineTo(SK_Scalar1, 0);
392 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
393 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
394 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
395 redundantSquare.lineTo(0, SK_Scalar1);
396 redundantSquare.lineTo(0, SK_Scalar1);
397 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000398 redundantSquare.close();
399 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
400
401 SkPath bowTie;
402 bowTie.moveTo(0, 0);
403 bowTie.lineTo(0, 0);
404 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000405 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
406 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
407 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
408 bowTie.lineTo(SK_Scalar1, 0);
409 bowTie.lineTo(SK_Scalar1, 0);
410 bowTie.lineTo(SK_Scalar1, 0);
411 bowTie.lineTo(0, SK_Scalar1);
412 bowTie.lineTo(0, SK_Scalar1);
413 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000414 bowTie.close();
415 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
416
417 SkPath spiral;
418 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000419 spiral.lineTo(100*SK_Scalar1, 0);
420 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
421 spiral.lineTo(0, 100*SK_Scalar1);
422 spiral.lineTo(0, 50*SK_Scalar1);
423 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
424 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000425 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000426 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000427
428 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000429 dent.moveTo(0, 0);
430 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
431 dent.lineTo(0, 100*SK_Scalar1);
432 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
433 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000434 dent.close();
435 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
436}
437
reed@android.com6b82d1a2009-06-03 02:35:01 +0000438static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
439 const SkRect& bounds) {
440 REPORTER_ASSERT(reporter, p.isConvex());
441 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000442
reed@android.com6b82d1a2009-06-03 02:35:01 +0000443 SkPath p2(p);
444 REPORTER_ASSERT(reporter, p2.isConvex());
445 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
446
447 SkPath other;
448 other.swap(p2);
449 REPORTER_ASSERT(reporter, other.isConvex());
450 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
451}
452
reed@google.com04863fa2011-05-15 04:08:24 +0000453static void setFromString(SkPath* path, const char str[]) {
454 bool first = true;
455 while (str) {
456 SkScalar x, y;
457 str = SkParse::FindScalar(str, &x);
458 if (NULL == str) {
459 break;
460 }
461 str = SkParse::FindScalar(str, &y);
462 SkASSERT(str);
463 if (first) {
464 path->moveTo(x, y);
465 first = false;
466 } else {
467 path->lineTo(x, y);
468 }
469 }
470}
471
472static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000473 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
474 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
475
476 SkPath path;
477
reed@google.comb54455e2011-05-16 14:16:04 +0000478 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000479 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000480 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000481 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000482 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
483 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000484 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000485 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000486 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000487 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000488 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000489 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000490 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000491
492 static const struct {
493 const char* fPathStr;
494 SkPath::Convexity fExpectedConvexity;
495 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000496 { "", SkPath::kConvex_Convexity },
497 { "0 0", SkPath::kConvex_Convexity },
498 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000499 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000500 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
501 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
502 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
503 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
504 };
505
506 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
507 SkPath path;
508 setFromString(&path, gRec[i].fPathStr);
509 SkPath::Convexity c = SkPath::ComputeConvexity(path);
510 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
511 }
512}
513
reed@google.com7e6c4d12012-05-10 14:05:43 +0000514static void test_isLine(skiatest::Reporter* reporter) {
515 SkPath path;
516 SkPoint pts[2];
517 const SkScalar value = SkIntToScalar(5);
518
519 REPORTER_ASSERT(reporter, !path.isLine(NULL));
520
521 // set some non-zero values
522 pts[0].set(value, value);
523 pts[1].set(value, value);
524 REPORTER_ASSERT(reporter, !path.isLine(pts));
525 // check that pts was untouched
526 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
527 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
528
529 const SkScalar moveX = SkIntToScalar(1);
530 const SkScalar moveY = SkIntToScalar(2);
531 SkASSERT(value != moveX && value != moveY);
532
533 path.moveTo(moveX, moveY);
534 REPORTER_ASSERT(reporter, !path.isLine(NULL));
535 REPORTER_ASSERT(reporter, !path.isLine(pts));
536 // check that pts was untouched
537 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
538 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
539
540 const SkScalar lineX = SkIntToScalar(2);
541 const SkScalar lineY = SkIntToScalar(2);
542 SkASSERT(value != lineX && value != lineY);
543
544 path.lineTo(lineX, lineY);
545 REPORTER_ASSERT(reporter, path.isLine(NULL));
546
547 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
548 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
549 REPORTER_ASSERT(reporter, path.isLine(pts));
550 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
551 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
552
553 path.lineTo(0, 0); // too many points/verbs
554 REPORTER_ASSERT(reporter, !path.isLine(NULL));
555 REPORTER_ASSERT(reporter, !path.isLine(pts));
556 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
557 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
558}
559
caryclark@google.comf1316942011-07-26 19:54:45 +0000560// Simple isRect test is inline TestPath, below.
561// test_isRect provides more extensive testing.
562static void test_isRect(skiatest::Reporter* reporter) {
563 // passing tests (all moveTo / lineTo...
564 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
565 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
566 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
567 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
568 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
569 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
570 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
571 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
572 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
573 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
574 {1, 0}, {.5f, 0}};
575 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
576 {0, 1}, {0, .5f}};
577 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
578 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
579 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
580
581 // failing tests
582 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
583 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
584 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
585 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
586 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
587 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
588 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
589 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
590
591 // failing, no close
592 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
593 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
594
595 size_t testLen[] = {
596 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
597 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
598 sizeof(rd), sizeof(re),
599 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
600 sizeof(f7), sizeof(f8),
601 sizeof(c1), sizeof(c2)
602 };
603 SkPoint* tests[] = {
604 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
605 f1, f2, f3, f4, f5, f6, f7, f8,
606 c1, c2
607 };
608 SkPoint* lastPass = re;
609 SkPoint* lastClose = f8;
610 bool fail = false;
611 bool close = true;
612 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
613 size_t index;
614 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
615 SkPath path;
616 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
617 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
618 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
619 }
620 if (close) {
621 path.close();
622 }
623 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
624 if (tests[testIndex] == lastPass) {
625 fail = true;
626 }
627 if (tests[testIndex] == lastClose) {
628 close = false;
629 }
630 }
631
632 // fail, close then line
633 SkPath path1;
634 path1.moveTo(r1[0].fX, r1[0].fY);
635 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
636 path1.lineTo(r1[index].fX, r1[index].fY);
637 }
638 path1.close();
639 path1.lineTo(1, 0);
640 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
641
642 // fail, move in the middle
643 path1.reset();
644 path1.moveTo(r1[0].fX, r1[0].fY);
645 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
646 if (index == 2) {
647 path1.moveTo(1, .5f);
648 }
649 path1.lineTo(r1[index].fX, r1[index].fY);
650 }
651 path1.close();
652 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
653
654 // fail, move on the edge
655 path1.reset();
656 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
657 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
658 path1.lineTo(r1[index].fX, r1[index].fY);
659 }
660 path1.close();
661 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
662
663 // fail, quad
664 path1.reset();
665 path1.moveTo(r1[0].fX, r1[0].fY);
666 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
667 if (index == 2) {
668 path1.quadTo(1, .5f, 1, .5f);
669 }
670 path1.lineTo(r1[index].fX, r1[index].fY);
671 }
672 path1.close();
673 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
674
675 // fail, cubic
676 path1.reset();
677 path1.moveTo(r1[0].fX, r1[0].fY);
678 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
679 if (index == 2) {
680 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
681 }
682 path1.lineTo(r1[index].fX, r1[index].fY);
683 }
684 path1.close();
685 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
686}
687
reed@google.com53effc52011-09-21 19:05:12 +0000688static void test_flattening(skiatest::Reporter* reporter) {
689 SkPath p;
690
691 static const SkPoint pts[] = {
692 { 0, 0 },
693 { SkIntToScalar(10), SkIntToScalar(10) },
694 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
695 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
696 };
697 p.moveTo(pts[0]);
698 p.lineTo(pts[1]);
699 p.quadTo(pts[2], pts[3]);
700 p.cubicTo(pts[4], pts[5], pts[6]);
701
702 SkWriter32 writer(100);
703 p.flatten(writer);
704 size_t size = writer.size();
705 SkAutoMalloc storage(size);
706 writer.flatten(storage.get());
707 SkReader32 reader(storage.get(), size);
708
709 SkPath p1;
710 REPORTER_ASSERT(reporter, p1 != p);
711 p1.unflatten(reader);
712 REPORTER_ASSERT(reporter, p1 == p);
713}
714
715static void test_transform(skiatest::Reporter* reporter) {
716 SkPath p, p1;
717
718 static const SkPoint pts[] = {
719 { 0, 0 },
720 { SkIntToScalar(10), SkIntToScalar(10) },
721 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
722 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
723 };
724 p.moveTo(pts[0]);
725 p.lineTo(pts[1]);
726 p.quadTo(pts[2], pts[3]);
727 p.cubicTo(pts[4], pts[5], pts[6]);
728
729 SkMatrix matrix;
730 matrix.reset();
731 p.transform(matrix, &p1);
732 REPORTER_ASSERT(reporter, p == p1);
733
734 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
735 p.transform(matrix, &p1);
736 SkPoint pts1[7];
737 int count = p1.getPoints(pts1, 7);
738 REPORTER_ASSERT(reporter, 7 == count);
739 for (int i = 0; i < count; ++i) {
740 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
741 REPORTER_ASSERT(reporter, newPt == pts1[i]);
742 }
743}
744
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000745static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000746 SkPath p;
747 SkPoint pt;
748 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000749
750 // Lone moveTo case
751 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000752 REPORTER_ASSERT(reporter, !p.isEmpty());
753 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000754 p.getLastPt(&pt);
755 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
756 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
757 bounds.set(0, 0, 0, 0);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000758 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000759 {
760 uint8_t verbs[1];
761 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
762 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
763 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000764
765 // MoveTo-MoveTo case
766 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000767 REPORTER_ASSERT(reporter, !p.isEmpty());
768 REPORTER_ASSERT(reporter, 2 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000769 REPORTER_ASSERT(reporter, 2 == p.countVerbs());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000770 p.getLastPt(&pt);
771 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
772 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
773 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000774 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000775 {
776 uint8_t verbs[2];
777 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
778 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
779 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[1]);
780 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000781
782 // moveTo-close case
783 p.reset();
784 p.moveTo(SK_Scalar1, SK_Scalar1);
785 p.close();
786 bounds.set(0, 0, 0, 0);
787 REPORTER_ASSERT(reporter, !p.isEmpty());
788 REPORTER_ASSERT(reporter, 1 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000789 REPORTER_ASSERT(reporter, 2 == p.countVerbs());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000790 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000791 {
792 uint8_t verbs[2];
793 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
794 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
795 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[1]);
796 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000797
798 // moveTo-close-moveTo-close case
799 p.moveTo(SK_Scalar1*2, SK_Scalar1);
800 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000801 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000802 REPORTER_ASSERT(reporter, !p.isEmpty());
803 REPORTER_ASSERT(reporter, 2 == p.countPoints());
804 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000805 {
806 uint8_t verbs[4];
807 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
808 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
809 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[1]);
810 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
811 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[1]);
812 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000813
814 // moveTo-line case
815 p.reset();
816 p.moveTo(SK_Scalar1, SK_Scalar1);
817 p.lineTo(SK_Scalar1, SK_Scalar1);
818 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
819 REPORTER_ASSERT(reporter, !p.isEmpty());
820 REPORTER_ASSERT(reporter, 2 == p.countPoints());
821 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000822 {
823 uint8_t verbs[2];
824 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
825 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
826 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
827 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000828
829 // moveTo-lineTo-moveTo-lineTo case
830 p.moveTo(SK_Scalar1*2, SK_Scalar1);
831 p.lineTo(SK_Scalar1*2, SK_Scalar1);
832 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
833 REPORTER_ASSERT(reporter, !p.isEmpty());
834 REPORTER_ASSERT(reporter, 4 == p.countPoints());
835 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000836 {
837 uint8_t verbs[4];
838 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
839 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
840 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
841 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[2]);
842 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
843 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000844
845 // moveTo-line-close case
846 p.reset();
847 p.moveTo(SK_Scalar1, SK_Scalar1);
848 p.lineTo(SK_Scalar1, SK_Scalar1);
849 p.close();
850 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
851 REPORTER_ASSERT(reporter, !p.isEmpty());
852 REPORTER_ASSERT(reporter, 2 == p.countPoints());
853 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000854 {
855 uint8_t verbs[3];
856 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
857 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
858 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
859 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
860 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000861
862 // moveTo-line-close-moveTo-line-close case
863 p.moveTo(SK_Scalar1*2, SK_Scalar1);
864 p.lineTo(SK_Scalar1*2, SK_Scalar1);
865 p.close();
866 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
867 REPORTER_ASSERT(reporter, !p.isEmpty());
868 REPORTER_ASSERT(reporter, 4 == p.countPoints());
869 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000870 {
871 uint8_t verbs[6];
872 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
873 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
874 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
875 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
876 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[3]);
877 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[4]);
878 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[5]);
879 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000880
881 // moveTo-quadTo case
882 p.reset();
883 p.moveTo(SK_Scalar1, SK_Scalar1);
884 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
885 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
886 REPORTER_ASSERT(reporter, !p.isEmpty());
887 REPORTER_ASSERT(reporter, 3 == p.countPoints());
888 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000889 {
890 uint8_t verbs[2];
891 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
892 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
893 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
894 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000895
896 // moveTo-quadTo-close case
897 p.close();
898 REPORTER_ASSERT(reporter, !p.isEmpty());
899 REPORTER_ASSERT(reporter, 3 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000900 REPORTER_ASSERT(reporter, 3 == p.countVerbs());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000901 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000902 {
903 uint8_t verbs[3];
904 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
905 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
906 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
907 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
908 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000909
910 // moveTo-quadTo-moveTo-quadTo case
911 p.reset();
912 p.moveTo(SK_Scalar1, SK_Scalar1);
913 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
914 p.moveTo(SK_Scalar1*2, SK_Scalar1);
915 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
916 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
917 REPORTER_ASSERT(reporter, !p.isEmpty());
918 REPORTER_ASSERT(reporter, 6 == p.countPoints());
919 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000920 {
921 uint8_t verbs[4];
922 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
923 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
924 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
925 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
926 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
927 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000928
929 // moveTo-cubicTo case
930 p.reset();
931 p.moveTo(SK_Scalar1, SK_Scalar1);
932 p.cubicTo(SK_Scalar1, SK_Scalar1,
933 SK_Scalar1, SK_Scalar1,
934 SK_Scalar1, SK_Scalar1);
935 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
936 REPORTER_ASSERT(reporter, !p.isEmpty());
937 REPORTER_ASSERT(reporter, 4 == p.countPoints());
938 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000939 {
940 uint8_t verbs[2];
941 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
942 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
943 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
944 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000945
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000946 // moveTo-cubicTo-close case
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000947 p.close();
948 REPORTER_ASSERT(reporter, !p.isEmpty());
949 REPORTER_ASSERT(reporter, 4 == p.countPoints());
950 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000951 {
952 uint8_t verbs[3];
953 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
954 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
955 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
956 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
957 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000958
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000959 // moveTo-cubicTo-moveTo-cubicTo case
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000960 p.reset();
961 p.moveTo(SK_Scalar1, SK_Scalar1);
962 p.cubicTo(SK_Scalar1, SK_Scalar1,
963 SK_Scalar1, SK_Scalar1,
964 SK_Scalar1, SK_Scalar1);
965 p.moveTo(SK_Scalar1*2, SK_Scalar1);
966 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
967 SK_Scalar1*2, SK_Scalar1,
968 SK_Scalar1*2, SK_Scalar1);
969 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
970 REPORTER_ASSERT(reporter, !p.isEmpty());
971 REPORTER_ASSERT(reporter, 8 == p.countPoints());
972 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000973 {
974 uint8_t verbs[4];
975 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
976 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
977 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
978 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[2]);
979 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[3]);
980 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000981}
982
983struct SegmentInfo {
984 SkPath fPath;
985 int fPointCount;
986};
987
reed@google.com10296cc2011-09-21 12:29:05 +0000988#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
989
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000990static void test_segment_masks(skiatest::Reporter* reporter) {
991 SkPath p;
992 p.moveTo(0, 0);
993 p.quadTo(100, 100, 200, 200);
994 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
995 REPORTER_ASSERT(reporter, !p.isEmpty());
996 p.cubicTo(100, 100, 200, 200, 300, 300);
997 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
998 REPORTER_ASSERT(reporter, !p.isEmpty());
999 p.reset();
1000 p.moveTo(0, 0);
1001 p.cubicTo(100, 100, 200, 200, 300, 300);
1002 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
1003 REPORTER_ASSERT(reporter, !p.isEmpty());
1004}
1005
1006static void test_iter(skiatest::Reporter* reporter) {
1007 SkPath p;
1008 SkPoint pts[4];
1009
1010 // Test an iterator with no path
1011 SkPath::Iter noPathIter;
1012 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1013 // Test that setting an empty path works
1014 noPathIter.setPath(p, false);
1015 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1016 // Test that close path makes no difference for an empty path
1017 noPathIter.setPath(p, true);
1018 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1019
1020 // Test an iterator with an initial empty path
1021 SkPath::Iter iter(p, false);
1022 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1023
1024 // Test that close path makes no difference
1025 SkPath::Iter forceCloseIter(p, true);
1026 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1027
1028 // Test that a move-only path produces nothing when iterated.
1029 p.moveTo(SK_Scalar1, 0);
1030 iter.setPath(p, false);
1031 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1032
1033 // No matter how many moves we add, we should still get nothing back.
1034 p.moveTo(SK_Scalar1*2, 0);
1035 p.moveTo(SK_Scalar1*3, 0);
1036 p.moveTo(SK_Scalar1*4, 0);
1037 p.moveTo(SK_Scalar1*5, 0);
1038 iter.setPath(p, false);
1039 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1040
1041 // Nor should force closing
1042 forceCloseIter.setPath(p, true);
1043 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1044
1045 // Initial closes should be ignored
1046 p.reset();
1047 p.close();
1048 iter.setPath(p, false);
1049 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1050 // Even if force closed
1051 forceCloseIter.setPath(p, true);
1052 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1053
1054 // Move/close sequences should also be ignored
1055 p.reset();
1056 p.close();
1057 p.moveTo(SK_Scalar1, 0);
1058 p.close();
1059 p.close();
1060 p.moveTo(SK_Scalar1*2, 0);
1061 p.close();
1062 p.moveTo(SK_Scalar1*3, 0);
1063 p.moveTo(SK_Scalar1*4, 0);
1064 p.close();
1065 iter.setPath(p, false);
1066 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1067 // Even if force closed
1068 forceCloseIter.setPath(p, true);
1069 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1070
1071 // The GM degeneratesegments.cpp test is more extensive
1072}
1073
1074static void test_raw_iter(skiatest::Reporter* reporter) {
1075 SkPath p;
1076 SkPoint pts[4];
1077
1078 // Test an iterator with no path
1079 SkPath::RawIter noPathIter;
1080 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1081 // Test that setting an empty path works
1082 noPathIter.setPath(p);
1083 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1084
1085 // Test an iterator with an initial empty path
1086 SkPath::RawIter iter(p);
1087 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1088
1089 // Test that a move-only path returns the move.
1090 p.moveTo(SK_Scalar1, 0);
1091 iter.setPath(p);
1092 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1093 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1094 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1095 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1096
1097 // No matter how many moves we add, we should get them all back
1098 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1099 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1100 iter.setPath(p);
1101 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1102 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1103 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1104 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1105 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1106 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1107 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1108 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1109 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1110 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1111
1112 // Initial close is never ever stored
1113 p.reset();
1114 p.close();
1115 iter.setPath(p);
1116 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1117
1118 // Move/close sequences
1119 p.reset();
1120 p.close(); // Not stored, no purpose
1121 p.moveTo(SK_Scalar1, 0);
1122 p.close();
1123 p.close(); // Not stored, no purpose
1124 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1125 p.close();
1126 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1127 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1128 p.close();
1129 iter.setPath(p);
1130 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1131 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1132 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1133 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1134 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1135 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1136 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1137 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1138 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1139 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1140 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1141 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1142 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1143 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1144 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1145 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1146 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1147 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1148 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1149 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1150 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1151 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1152
1153 // Generate random paths and verify
1154 SkPoint randomPts[25];
1155 for (int i = 0; i < 5; ++i) {
1156 for (int j = 0; j < 5; ++j) {
1157 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1158 }
1159 }
1160
1161 // Max of 10 segments, max 3 points per segment
1162 SkRandom rand(9876543);
1163 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001164 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001165 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001166
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001167 for (int i = 0; i < 500; ++i) {
1168 p.reset();
1169 bool lastWasClose = true;
1170 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001171 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001172 int numPoints = 0;
1173 int numVerbs = (rand.nextU() >> 16) % 10;
1174 int numIterVerbs = 0;
1175 for (int j = 0; j < numVerbs; ++j) {
1176 do {
1177 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1178 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001179 switch (nextVerb) {
1180 case SkPath::kMove_Verb:
1181 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1182 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001183 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001184 numPoints += 1;
1185 lastWasClose = false;
1186 haveMoveTo = true;
1187 break;
1188 case SkPath::kLine_Verb:
1189 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001190 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001191 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1192 haveMoveTo = true;
1193 }
1194 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1195 p.lineTo(expectedPts[numPoints]);
1196 numPoints += 1;
1197 lastWasClose = false;
1198 break;
1199 case SkPath::kQuad_Verb:
1200 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001201 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001202 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1203 haveMoveTo = true;
1204 }
1205 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1206 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1207 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1208 numPoints += 2;
1209 lastWasClose = false;
1210 break;
1211 case SkPath::kCubic_Verb:
1212 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001213 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001214 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1215 haveMoveTo = true;
1216 }
1217 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1218 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1219 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1220 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1221 expectedPts[numPoints + 2]);
1222 numPoints += 3;
1223 lastWasClose = false;
1224 break;
1225 case SkPath::kClose_Verb:
1226 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001227 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001228 lastWasClose = true;
1229 break;
1230 default:;
1231 }
1232 expectedVerbs[numIterVerbs++] = nextVerb;
1233 }
1234
1235 iter.setPath(p);
1236 numVerbs = numIterVerbs;
1237 numIterVerbs = 0;
1238 int numIterPts = 0;
1239 SkPoint lastMoveTo;
1240 SkPoint lastPt;
1241 lastMoveTo.set(0, 0);
1242 lastPt.set(0, 0);
1243 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1244 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1245 numIterVerbs++;
1246 switch (nextVerb) {
1247 case SkPath::kMove_Verb:
1248 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1249 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1250 lastPt = lastMoveTo = pts[0];
1251 numIterPts += 1;
1252 break;
1253 case SkPath::kLine_Verb:
1254 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1255 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1256 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1257 lastPt = pts[1];
1258 numIterPts += 1;
1259 break;
1260 case SkPath::kQuad_Verb:
1261 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1262 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1263 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1264 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1265 lastPt = pts[2];
1266 numIterPts += 2;
1267 break;
1268 case SkPath::kCubic_Verb:
1269 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1270 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1271 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1272 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1273 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1274 lastPt = pts[3];
1275 numIterPts += 3;
1276 break;
1277 case SkPath::kClose_Verb:
1278 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1279 lastPt = lastMoveTo;
1280 break;
1281 default:;
1282 }
1283 }
1284 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1285 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1286 }
1287}
1288
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001289static void check_for_circle(skiatest::Reporter* reporter,
1290 const SkPath& path, bool expected) {
1291 SkRect rect;
1292 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1293 if (expected) {
1294 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1295 }
1296}
1297
1298static void test_circle_skew(skiatest::Reporter* reporter,
1299 const SkPath& path) {
1300 SkPath tmp;
1301
1302 SkMatrix m;
1303 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1304 path.transform(m, &tmp);
1305 check_for_circle(reporter, tmp, false);
1306}
1307
1308static void test_circle_translate(skiatest::Reporter* reporter,
1309 const SkPath& path) {
1310 SkPath tmp;
1311
1312 // translate at small offset
1313 SkMatrix m;
1314 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1315 path.transform(m, &tmp);
1316 check_for_circle(reporter, tmp, true);
1317
1318 tmp.reset();
1319 m.reset();
1320
1321 // translate at a relatively big offset
1322 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1323 path.transform(m, &tmp);
1324 check_for_circle(reporter, tmp, true);
1325}
1326
1327static void test_circle_rotate(skiatest::Reporter* reporter,
1328 const SkPath& path) {
1329 for (int angle = 0; angle < 360; ++angle) {
1330 SkPath tmp;
1331 SkMatrix m;
1332 m.setRotate(SkIntToScalar(angle));
1333 path.transform(m, &tmp);
1334
1335 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1336 // degrees is not an oval anymore, this can be improved. we made this
1337 // for the simplicity of our implementation.
1338 if (angle % 90 == 0) {
1339 check_for_circle(reporter, tmp, true);
1340 } else {
1341 check_for_circle(reporter, tmp, false);
1342 }
1343 }
1344}
1345
1346static void test_circle_with_direction(skiatest::Reporter* reporter,
1347 SkPath::Direction dir) {
1348 SkPath path;
1349
1350 // circle at origin
1351 path.addCircle(0, 0, SkIntToScalar(20), dir);
1352 check_for_circle(reporter, path, true);
1353 test_circle_rotate(reporter, path);
1354 test_circle_translate(reporter, path);
1355 test_circle_skew(reporter, path);
1356
1357 // circle at an offset at (10, 10)
1358 path.reset();
1359 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1360 SkIntToScalar(20), dir);
1361 check_for_circle(reporter, path, true);
1362 test_circle_rotate(reporter, path);
1363 test_circle_translate(reporter, path);
1364 test_circle_skew(reporter, path);
1365}
1366
1367static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1368 SkPath path;
1369 SkPath circle;
1370 SkPath rect;
1371 SkPath empty;
1372
1373 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1374 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1375 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1376
1377 SkMatrix translate;
1378 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1379
1380 // For simplicity, all the path concatenation related operations
1381 // would mark it non-circle, though in theory it's still a circle.
1382
1383 // empty + circle (translate)
1384 path = empty;
1385 path.addPath(circle, translate);
1386 check_for_circle(reporter, path, false);
1387
1388 // circle + empty (translate)
1389 path = circle;
1390 path.addPath(empty, translate);
1391 check_for_circle(reporter, path, false);
1392
1393 // test reverseAddPath
1394 path = circle;
1395 path.reverseAddPath(rect);
1396 check_for_circle(reporter, path, false);
1397}
1398
1399static void test_circle(skiatest::Reporter* reporter) {
1400 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1401 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1402
1403 // multiple addCircle()
1404 SkPath path;
1405 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1406 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1407 check_for_circle(reporter, path, false);
1408
1409 // some extra lineTo() would make isOval() fail
1410 path.reset();
1411 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1412 path.lineTo(0, 0);
1413 check_for_circle(reporter, path, false);
1414
1415 // not back to the original point
1416 path.reset();
1417 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1418 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1419 check_for_circle(reporter, path, false);
1420
1421 test_circle_with_add_paths(reporter);
1422}
1423
1424static void test_oval(skiatest::Reporter* reporter) {
1425 SkRect rect;
1426 SkMatrix m;
1427 SkPath path;
1428
1429 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1430 path.addOval(rect);
1431
1432 REPORTER_ASSERT(reporter, path.isOval(NULL));
1433
1434 m.setRotate(SkIntToScalar(90));
1435 SkPath tmp;
1436 path.transform(m, &tmp);
1437 // an oval rotated 90 degrees is still an oval.
1438 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1439
1440 m.reset();
1441 m.setRotate(SkIntToScalar(30));
1442 tmp.reset();
1443 path.transform(m, &tmp);
1444 // an oval rotated 30 degrees is not an oval anymore.
1445 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1446
1447 // since empty path being transformed.
1448 path.reset();
1449 tmp.reset();
1450 m.reset();
1451 path.transform(m, &tmp);
1452 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1453
1454 // empty path is not an oval
1455 tmp.reset();
1456 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1457
1458 // only has moveTo()s
1459 tmp.reset();
1460 tmp.moveTo(0, 0);
1461 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1462 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1463
1464 // mimic WebKit's calling convention,
1465 // call moveTo() first and then call addOval()
1466 path.reset();
1467 path.moveTo(0, 0);
1468 path.addOval(rect);
1469 REPORTER_ASSERT(reporter, path.isOval(NULL));
1470
1471 // copy path
1472 path.reset();
1473 tmp.reset();
1474 tmp.addOval(rect);
1475 path = tmp;
1476 REPORTER_ASSERT(reporter, path.isOval(NULL));
1477}
1478
caryclark@google.com42639cd2012-06-06 12:03:39 +00001479static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001480 {
1481 SkSize size;
1482 size.fWidth = 3.4f;
1483 size.width();
1484 size = SkSize::Make(3,4);
1485 SkISize isize = SkISize::Make(3,4);
1486 }
1487
1488 SkTSize<SkScalar>::Make(3,4);
1489
reed@android.com3abec1d2009-03-02 05:36:20 +00001490 SkPath p, p2;
1491 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001492
reed@android.com3abec1d2009-03-02 05:36:20 +00001493 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001494 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001495 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001496 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001497 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001498 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1499 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1500 REPORTER_ASSERT(reporter, p == p2);
1501 REPORTER_ASSERT(reporter, !(p != p2));
1502
reed@android.comd252db02009-04-01 18:31:44 +00001503 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001504
reed@android.com3abec1d2009-03-02 05:36:20 +00001505 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001506
reed@android.com6b82d1a2009-06-03 02:35:01 +00001507 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1508 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001509 // we have quads or cubics
1510 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001511 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001512
reed@android.com6b82d1a2009-06-03 02:35:01 +00001513 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001514 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001515 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001516
reed@android.com6b82d1a2009-06-03 02:35:01 +00001517 p.addOval(bounds);
1518 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001519 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001520
reed@android.com6b82d1a2009-06-03 02:35:01 +00001521 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001522 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001523 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001524 // we have only lines
1525 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001526 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001527
1528 REPORTER_ASSERT(reporter, p != p2);
1529 REPORTER_ASSERT(reporter, !(p == p2));
1530
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001531 // do getPoints and getVerbs return the right result
1532 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1533 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001534 SkPoint pts[4];
1535 int count = p.getPoints(pts, 4);
1536 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001537 uint8_t verbs[6];
1538 verbs[5] = 0xff;
1539 p.getVerbs(verbs, 5);
1540 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1541 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1542 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1543 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1544 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1545 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001546 bounds2.set(pts, 4);
1547 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001548
reed@android.com3abec1d2009-03-02 05:36:20 +00001549 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1550 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001551 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001552
reed@android.com3abec1d2009-03-02 05:36:20 +00001553 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001554 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001555 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1556 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001557
reed@android.com3abec1d2009-03-02 05:36:20 +00001558 // now force p to not be a rect
1559 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1560 p.addRect(bounds);
1561 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001562
reed@google.com7e6c4d12012-05-10 14:05:43 +00001563 test_isLine(reporter);
1564 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001565 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001566 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001567 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001568 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001569 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001570 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001571 test_flattening(reporter);
1572 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001573 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001574 test_iter(reporter);
1575 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001576 test_circle(reporter);
1577 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001578 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001579 test_addPoly(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001580}
1581
1582#include "TestClassDef.h"
1583DEFINE_TESTCLASS("Path", PathTestClass, TestPath)