blob: ff12e8eb53257795ef48db5eea25db71999e6446 [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);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000703 writer.writePath(p);
reed@google.com53effc52011-09-21 19:05:12 +0000704 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);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000711 reader.readPath(&p1);
reed@google.com53effc52011-09-21 19:05:12 +0000712 REPORTER_ASSERT(reporter, p1 == p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000713
714 // create a buffer that should be much larger than the path so we don't
715 // kill our stack if writer goes too far.
716 char buffer[1024];
717 uint32_t size1 = p.writeToMemory(NULL);
718 uint32_t size2 = p.writeToMemory(buffer);
719 REPORTER_ASSERT(reporter, size1 == size2);
720
721 SkPath p2;
722 uint32_t size3 = p2.readFromMemory(buffer);
723 REPORTER_ASSERT(reporter, size1 == size3);
724 REPORTER_ASSERT(reporter, p == p2);
725
726 char buffer2[1024];
727 size3 = p2.writeToMemory(buffer2);
728 REPORTER_ASSERT(reporter, size1 == size3);
729 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
reed@google.com53effc52011-09-21 19:05:12 +0000730}
731
732static void test_transform(skiatest::Reporter* reporter) {
733 SkPath p, p1;
734
735 static const SkPoint pts[] = {
736 { 0, 0 },
737 { SkIntToScalar(10), SkIntToScalar(10) },
738 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
739 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
740 };
741 p.moveTo(pts[0]);
742 p.lineTo(pts[1]);
743 p.quadTo(pts[2], pts[3]);
744 p.cubicTo(pts[4], pts[5], pts[6]);
745
746 SkMatrix matrix;
747 matrix.reset();
748 p.transform(matrix, &p1);
749 REPORTER_ASSERT(reporter, p == p1);
750
751 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
752 p.transform(matrix, &p1);
753 SkPoint pts1[7];
754 int count = p1.getPoints(pts1, 7);
755 REPORTER_ASSERT(reporter, 7 == count);
756 for (int i = 0; i < count; ++i) {
757 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
758 REPORTER_ASSERT(reporter, newPt == pts1[i]);
759 }
760}
761
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000762static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000763 SkPath p;
764 SkPoint pt;
765 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000766
767 // Lone moveTo case
768 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000769 REPORTER_ASSERT(reporter, !p.isEmpty());
770 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000771 p.getLastPt(&pt);
772 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
773 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
774 bounds.set(0, 0, 0, 0);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000775 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000776 {
777 uint8_t verbs[1];
778 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
779 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
780 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000781
782 // MoveTo-MoveTo case
783 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000784 REPORTER_ASSERT(reporter, !p.isEmpty());
785 REPORTER_ASSERT(reporter, 2 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000786 REPORTER_ASSERT(reporter, 2 == p.countVerbs());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000787 p.getLastPt(&pt);
788 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
789 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
790 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000791 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000792 {
793 uint8_t verbs[2];
794 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
795 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
796 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[1]);
797 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000798
799 // moveTo-close case
800 p.reset();
801 p.moveTo(SK_Scalar1, SK_Scalar1);
802 p.close();
803 bounds.set(0, 0, 0, 0);
804 REPORTER_ASSERT(reporter, !p.isEmpty());
805 REPORTER_ASSERT(reporter, 1 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000806 REPORTER_ASSERT(reporter, 2 == p.countVerbs());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000807 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000808 {
809 uint8_t verbs[2];
810 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
811 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
812 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[1]);
813 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000814
815 // moveTo-close-moveTo-close case
816 p.moveTo(SK_Scalar1*2, SK_Scalar1);
817 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000818 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000819 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[4];
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::kClose_Verb == verbs[1]);
827 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
828 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[1]);
829 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000830
831 // moveTo-line case
832 p.reset();
833 p.moveTo(SK_Scalar1, SK_Scalar1);
834 p.lineTo(SK_Scalar1, SK_Scalar1);
835 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
836 REPORTER_ASSERT(reporter, !p.isEmpty());
837 REPORTER_ASSERT(reporter, 2 == p.countPoints());
838 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000839 {
840 uint8_t verbs[2];
841 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
842 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
843 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
844 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000845
846 // moveTo-lineTo-moveTo-lineTo case
847 p.moveTo(SK_Scalar1*2, SK_Scalar1);
848 p.lineTo(SK_Scalar1*2, SK_Scalar1);
849 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
850 REPORTER_ASSERT(reporter, !p.isEmpty());
851 REPORTER_ASSERT(reporter, 4 == p.countPoints());
852 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000853 {
854 uint8_t verbs[4];
855 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
856 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
857 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
858 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[2]);
859 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
860 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000861
862 // moveTo-line-close case
863 p.reset();
864 p.moveTo(SK_Scalar1, SK_Scalar1);
865 p.lineTo(SK_Scalar1, SK_Scalar1);
866 p.close();
867 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
868 REPORTER_ASSERT(reporter, !p.isEmpty());
869 REPORTER_ASSERT(reporter, 2 == p.countPoints());
870 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000871 {
872 uint8_t verbs[3];
873 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
874 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
875 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
876 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
877 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000878
879 // moveTo-line-close-moveTo-line-close case
880 p.moveTo(SK_Scalar1*2, SK_Scalar1);
881 p.lineTo(SK_Scalar1*2, SK_Scalar1);
882 p.close();
883 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
884 REPORTER_ASSERT(reporter, !p.isEmpty());
885 REPORTER_ASSERT(reporter, 4 == p.countPoints());
886 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000887 {
888 uint8_t verbs[6];
889 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
890 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
891 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
892 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
893 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[3]);
894 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[4]);
895 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[5]);
896 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000897
898 // moveTo-quadTo case
899 p.reset();
900 p.moveTo(SK_Scalar1, SK_Scalar1);
901 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
902 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
903 REPORTER_ASSERT(reporter, !p.isEmpty());
904 REPORTER_ASSERT(reporter, 3 == p.countPoints());
905 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000906 {
907 uint8_t verbs[2];
908 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
909 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
910 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
911 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000912
913 // moveTo-quadTo-close case
914 p.close();
915 REPORTER_ASSERT(reporter, !p.isEmpty());
916 REPORTER_ASSERT(reporter, 3 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000917 REPORTER_ASSERT(reporter, 3 == p.countVerbs());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000918 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000919 {
920 uint8_t verbs[3];
921 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
922 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
923 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
924 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
925 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000926
927 // moveTo-quadTo-moveTo-quadTo case
928 p.reset();
929 p.moveTo(SK_Scalar1, SK_Scalar1);
930 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
931 p.moveTo(SK_Scalar1*2, SK_Scalar1);
932 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
933 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
934 REPORTER_ASSERT(reporter, !p.isEmpty());
935 REPORTER_ASSERT(reporter, 6 == p.countPoints());
936 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000937 {
938 uint8_t verbs[4];
939 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
940 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
941 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
942 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
943 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == verbs[1]);
944 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000945
946 // moveTo-cubicTo case
947 p.reset();
948 p.moveTo(SK_Scalar1, SK_Scalar1);
949 p.cubicTo(SK_Scalar1, SK_Scalar1,
950 SK_Scalar1, SK_Scalar1,
951 SK_Scalar1, SK_Scalar1);
952 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
953 REPORTER_ASSERT(reporter, !p.isEmpty());
954 REPORTER_ASSERT(reporter, 4 == p.countPoints());
955 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000956 {
957 uint8_t verbs[2];
958 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
959 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
960 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
961 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000962
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000963 // moveTo-cubicTo-close case
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000964 p.close();
965 REPORTER_ASSERT(reporter, !p.isEmpty());
966 REPORTER_ASSERT(reporter, 4 == p.countPoints());
967 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000968 {
969 uint8_t verbs[3];
970 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
971 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
972 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
973 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[2]);
974 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000975
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000976 // moveTo-cubicTo-moveTo-cubicTo case
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000977 p.reset();
978 p.moveTo(SK_Scalar1, SK_Scalar1);
979 p.cubicTo(SK_Scalar1, SK_Scalar1,
980 SK_Scalar1, SK_Scalar1,
981 SK_Scalar1, SK_Scalar1);
982 p.moveTo(SK_Scalar1*2, SK_Scalar1);
983 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
984 SK_Scalar1*2, SK_Scalar1,
985 SK_Scalar1*2, SK_Scalar1);
986 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
987 REPORTER_ASSERT(reporter, !p.isEmpty());
988 REPORTER_ASSERT(reporter, 8 == p.countPoints());
989 REPORTER_ASSERT(reporter, bounds == p.getBounds());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000990 {
991 uint8_t verbs[4];
992 REPORTER_ASSERT(reporter, SK_ARRAY_COUNT(verbs) == p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
993 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
994 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[1]);
995 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[2]);
996 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == verbs[3]);
997 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000998}
999
1000struct SegmentInfo {
1001 SkPath fPath;
1002 int fPointCount;
1003};
1004
reed@google.com10296cc2011-09-21 12:29:05 +00001005#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1006
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001007static void test_segment_masks(skiatest::Reporter* reporter) {
1008 SkPath p;
1009 p.moveTo(0, 0);
1010 p.quadTo(100, 100, 200, 200);
1011 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1012 REPORTER_ASSERT(reporter, !p.isEmpty());
1013 p.cubicTo(100, 100, 200, 200, 300, 300);
1014 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1015 REPORTER_ASSERT(reporter, !p.isEmpty());
1016 p.reset();
1017 p.moveTo(0, 0);
1018 p.cubicTo(100, 100, 200, 200, 300, 300);
1019 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
1020 REPORTER_ASSERT(reporter, !p.isEmpty());
1021}
1022
1023static void test_iter(skiatest::Reporter* reporter) {
1024 SkPath p;
1025 SkPoint pts[4];
1026
1027 // Test an iterator with no path
1028 SkPath::Iter noPathIter;
1029 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1030 // Test that setting an empty path works
1031 noPathIter.setPath(p, false);
1032 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1033 // Test that close path makes no difference for an empty path
1034 noPathIter.setPath(p, true);
1035 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1036
1037 // Test an iterator with an initial empty path
1038 SkPath::Iter iter(p, false);
1039 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1040
1041 // Test that close path makes no difference
1042 SkPath::Iter forceCloseIter(p, true);
1043 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1044
1045 // Test that a move-only path produces nothing when iterated.
1046 p.moveTo(SK_Scalar1, 0);
1047 iter.setPath(p, false);
1048 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1049
1050 // No matter how many moves we add, we should still get nothing back.
1051 p.moveTo(SK_Scalar1*2, 0);
1052 p.moveTo(SK_Scalar1*3, 0);
1053 p.moveTo(SK_Scalar1*4, 0);
1054 p.moveTo(SK_Scalar1*5, 0);
1055 iter.setPath(p, false);
1056 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1057
1058 // Nor should force closing
1059 forceCloseIter.setPath(p, true);
1060 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1061
1062 // Initial closes should be ignored
1063 p.reset();
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 // Move/close sequences should also be ignored
1072 p.reset();
1073 p.close();
1074 p.moveTo(SK_Scalar1, 0);
1075 p.close();
1076 p.close();
1077 p.moveTo(SK_Scalar1*2, 0);
1078 p.close();
1079 p.moveTo(SK_Scalar1*3, 0);
1080 p.moveTo(SK_Scalar1*4, 0);
1081 p.close();
1082 iter.setPath(p, false);
1083 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1084 // Even if force closed
1085 forceCloseIter.setPath(p, true);
1086 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
1087
1088 // The GM degeneratesegments.cpp test is more extensive
1089}
1090
1091static void test_raw_iter(skiatest::Reporter* reporter) {
1092 SkPath p;
1093 SkPoint pts[4];
1094
1095 // Test an iterator with no path
1096 SkPath::RawIter noPathIter;
1097 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1098 // Test that setting an empty path works
1099 noPathIter.setPath(p);
1100 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1101
1102 // Test an iterator with an initial empty path
1103 SkPath::RawIter iter(p);
1104 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1105
1106 // Test that a move-only path returns the move.
1107 p.moveTo(SK_Scalar1, 0);
1108 iter.setPath(p);
1109 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1110 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1111 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1112 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1113
1114 // No matter how many moves we add, we should get them all back
1115 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1116 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1117 iter.setPath(p);
1118 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1119 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1120 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1121 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1122 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1123 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1124 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1125 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1126 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1127 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1128
1129 // Initial close is never ever stored
1130 p.reset();
1131 p.close();
1132 iter.setPath(p);
1133 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1134
1135 // Move/close sequences
1136 p.reset();
1137 p.close(); // Not stored, no purpose
1138 p.moveTo(SK_Scalar1, 0);
1139 p.close();
1140 p.close(); // Not stored, no purpose
1141 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1142 p.close();
1143 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1144 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1145 p.close();
1146 iter.setPath(p);
1147 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1148 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1149 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1150 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1151 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1152 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1153 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1154 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1155 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1156 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1157 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1158 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1159 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1160 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1161 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1162 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1163 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1164 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1165 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1166 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1167 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1168 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1169
1170 // Generate random paths and verify
1171 SkPoint randomPts[25];
1172 for (int i = 0; i < 5; ++i) {
1173 for (int j = 0; j < 5; ++j) {
1174 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1175 }
1176 }
1177
1178 // Max of 10 segments, max 3 points per segment
1179 SkRandom rand(9876543);
1180 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001181 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001182 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001183
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001184 for (int i = 0; i < 500; ++i) {
1185 p.reset();
1186 bool lastWasClose = true;
1187 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001188 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001189 int numPoints = 0;
1190 int numVerbs = (rand.nextU() >> 16) % 10;
1191 int numIterVerbs = 0;
1192 for (int j = 0; j < numVerbs; ++j) {
1193 do {
1194 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1195 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001196 switch (nextVerb) {
1197 case SkPath::kMove_Verb:
1198 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1199 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001200 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001201 numPoints += 1;
1202 lastWasClose = false;
1203 haveMoveTo = true;
1204 break;
1205 case SkPath::kLine_Verb:
1206 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001207 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001208 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1209 haveMoveTo = true;
1210 }
1211 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1212 p.lineTo(expectedPts[numPoints]);
1213 numPoints += 1;
1214 lastWasClose = false;
1215 break;
1216 case SkPath::kQuad_Verb:
1217 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001218 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001219 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1220 haveMoveTo = true;
1221 }
1222 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1223 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1224 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1225 numPoints += 2;
1226 lastWasClose = false;
1227 break;
1228 case SkPath::kCubic_Verb:
1229 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001230 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001231 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1232 haveMoveTo = true;
1233 }
1234 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1235 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1236 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1237 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1238 expectedPts[numPoints + 2]);
1239 numPoints += 3;
1240 lastWasClose = false;
1241 break;
1242 case SkPath::kClose_Verb:
1243 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001244 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001245 lastWasClose = true;
1246 break;
1247 default:;
1248 }
1249 expectedVerbs[numIterVerbs++] = nextVerb;
1250 }
1251
1252 iter.setPath(p);
1253 numVerbs = numIterVerbs;
1254 numIterVerbs = 0;
1255 int numIterPts = 0;
1256 SkPoint lastMoveTo;
1257 SkPoint lastPt;
1258 lastMoveTo.set(0, 0);
1259 lastPt.set(0, 0);
1260 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1261 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1262 numIterVerbs++;
1263 switch (nextVerb) {
1264 case SkPath::kMove_Verb:
1265 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1266 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1267 lastPt = lastMoveTo = pts[0];
1268 numIterPts += 1;
1269 break;
1270 case SkPath::kLine_Verb:
1271 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1272 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1273 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1274 lastPt = pts[1];
1275 numIterPts += 1;
1276 break;
1277 case SkPath::kQuad_Verb:
1278 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1279 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1280 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1281 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1282 lastPt = pts[2];
1283 numIterPts += 2;
1284 break;
1285 case SkPath::kCubic_Verb:
1286 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1287 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1288 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1289 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1290 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1291 lastPt = pts[3];
1292 numIterPts += 3;
1293 break;
1294 case SkPath::kClose_Verb:
1295 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1296 lastPt = lastMoveTo;
1297 break;
1298 default:;
1299 }
1300 }
1301 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1302 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1303 }
1304}
1305
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001306static void check_for_circle(skiatest::Reporter* reporter,
1307 const SkPath& path, bool expected) {
1308 SkRect rect;
1309 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1310 if (expected) {
1311 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1312 }
1313}
1314
1315static void test_circle_skew(skiatest::Reporter* reporter,
1316 const SkPath& path) {
1317 SkPath tmp;
1318
1319 SkMatrix m;
1320 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1321 path.transform(m, &tmp);
1322 check_for_circle(reporter, tmp, false);
1323}
1324
1325static void test_circle_translate(skiatest::Reporter* reporter,
1326 const SkPath& path) {
1327 SkPath tmp;
1328
1329 // translate at small offset
1330 SkMatrix m;
1331 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1332 path.transform(m, &tmp);
1333 check_for_circle(reporter, tmp, true);
1334
1335 tmp.reset();
1336 m.reset();
1337
1338 // translate at a relatively big offset
1339 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1340 path.transform(m, &tmp);
1341 check_for_circle(reporter, tmp, true);
1342}
1343
1344static void test_circle_rotate(skiatest::Reporter* reporter,
1345 const SkPath& path) {
1346 for (int angle = 0; angle < 360; ++angle) {
1347 SkPath tmp;
1348 SkMatrix m;
1349 m.setRotate(SkIntToScalar(angle));
1350 path.transform(m, &tmp);
1351
1352 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1353 // degrees is not an oval anymore, this can be improved. we made this
1354 // for the simplicity of our implementation.
1355 if (angle % 90 == 0) {
1356 check_for_circle(reporter, tmp, true);
1357 } else {
1358 check_for_circle(reporter, tmp, false);
1359 }
1360 }
1361}
1362
1363static void test_circle_with_direction(skiatest::Reporter* reporter,
1364 SkPath::Direction dir) {
1365 SkPath path;
1366
1367 // circle at origin
1368 path.addCircle(0, 0, SkIntToScalar(20), dir);
1369 check_for_circle(reporter, path, true);
1370 test_circle_rotate(reporter, path);
1371 test_circle_translate(reporter, path);
1372 test_circle_skew(reporter, path);
1373
1374 // circle at an offset at (10, 10)
1375 path.reset();
1376 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1377 SkIntToScalar(20), dir);
1378 check_for_circle(reporter, path, true);
1379 test_circle_rotate(reporter, path);
1380 test_circle_translate(reporter, path);
1381 test_circle_skew(reporter, path);
1382}
1383
1384static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1385 SkPath path;
1386 SkPath circle;
1387 SkPath rect;
1388 SkPath empty;
1389
1390 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1391 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1392 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1393
1394 SkMatrix translate;
1395 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1396
1397 // For simplicity, all the path concatenation related operations
1398 // would mark it non-circle, though in theory it's still a circle.
1399
1400 // empty + circle (translate)
1401 path = empty;
1402 path.addPath(circle, translate);
1403 check_for_circle(reporter, path, false);
1404
1405 // circle + empty (translate)
1406 path = circle;
1407 path.addPath(empty, translate);
1408 check_for_circle(reporter, path, false);
1409
1410 // test reverseAddPath
1411 path = circle;
1412 path.reverseAddPath(rect);
1413 check_for_circle(reporter, path, false);
1414}
1415
1416static void test_circle(skiatest::Reporter* reporter) {
1417 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1418 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1419
1420 // multiple addCircle()
1421 SkPath path;
1422 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1423 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1424 check_for_circle(reporter, path, false);
1425
1426 // some extra lineTo() would make isOval() fail
1427 path.reset();
1428 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1429 path.lineTo(0, 0);
1430 check_for_circle(reporter, path, false);
1431
1432 // not back to the original point
1433 path.reset();
1434 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1435 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1436 check_for_circle(reporter, path, false);
1437
1438 test_circle_with_add_paths(reporter);
1439}
1440
1441static void test_oval(skiatest::Reporter* reporter) {
1442 SkRect rect;
1443 SkMatrix m;
1444 SkPath path;
1445
1446 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1447 path.addOval(rect);
1448
1449 REPORTER_ASSERT(reporter, path.isOval(NULL));
1450
1451 m.setRotate(SkIntToScalar(90));
1452 SkPath tmp;
1453 path.transform(m, &tmp);
1454 // an oval rotated 90 degrees is still an oval.
1455 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1456
1457 m.reset();
1458 m.setRotate(SkIntToScalar(30));
1459 tmp.reset();
1460 path.transform(m, &tmp);
1461 // an oval rotated 30 degrees is not an oval anymore.
1462 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1463
1464 // since empty path being transformed.
1465 path.reset();
1466 tmp.reset();
1467 m.reset();
1468 path.transform(m, &tmp);
1469 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1470
1471 // empty path is not an oval
1472 tmp.reset();
1473 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1474
1475 // only has moveTo()s
1476 tmp.reset();
1477 tmp.moveTo(0, 0);
1478 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1479 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1480
1481 // mimic WebKit's calling convention,
1482 // call moveTo() first and then call addOval()
1483 path.reset();
1484 path.moveTo(0, 0);
1485 path.addOval(rect);
1486 REPORTER_ASSERT(reporter, path.isOval(NULL));
1487
1488 // copy path
1489 path.reset();
1490 tmp.reset();
1491 tmp.addOval(rect);
1492 path = tmp;
1493 REPORTER_ASSERT(reporter, path.isOval(NULL));
1494}
1495
caryclark@google.com42639cd2012-06-06 12:03:39 +00001496static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001497 {
1498 SkSize size;
1499 size.fWidth = 3.4f;
1500 size.width();
1501 size = SkSize::Make(3,4);
1502 SkISize isize = SkISize::Make(3,4);
1503 }
1504
1505 SkTSize<SkScalar>::Make(3,4);
1506
reed@android.com3abec1d2009-03-02 05:36:20 +00001507 SkPath p, p2;
1508 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001509
reed@android.com3abec1d2009-03-02 05:36:20 +00001510 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001511 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001512 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001513 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001514 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001515 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1516 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1517 REPORTER_ASSERT(reporter, p == p2);
1518 REPORTER_ASSERT(reporter, !(p != p2));
1519
reed@android.comd252db02009-04-01 18:31:44 +00001520 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001521
reed@android.com3abec1d2009-03-02 05:36:20 +00001522 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001523
reed@android.com6b82d1a2009-06-03 02:35:01 +00001524 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1525 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001526 // we have quads or cubics
1527 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001528 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001529
reed@android.com6b82d1a2009-06-03 02:35:01 +00001530 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001531 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001532 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001533
reed@android.com6b82d1a2009-06-03 02:35:01 +00001534 p.addOval(bounds);
1535 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001536 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001537
reed@android.com6b82d1a2009-06-03 02:35:01 +00001538 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001539 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001540 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001541 // we have only lines
1542 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001543 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001544
1545 REPORTER_ASSERT(reporter, p != p2);
1546 REPORTER_ASSERT(reporter, !(p == p2));
1547
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001548 // do getPoints and getVerbs return the right result
1549 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1550 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001551 SkPoint pts[4];
1552 int count = p.getPoints(pts, 4);
1553 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001554 uint8_t verbs[6];
1555 verbs[5] = 0xff;
1556 p.getVerbs(verbs, 5);
1557 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1558 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1559 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1560 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1561 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1562 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001563 bounds2.set(pts, 4);
1564 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001565
reed@android.com3abec1d2009-03-02 05:36:20 +00001566 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1567 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001568 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001569
reed@android.com3abec1d2009-03-02 05:36:20 +00001570 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001571 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001572 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1573 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001574
reed@android.com3abec1d2009-03-02 05:36:20 +00001575 // now force p to not be a rect
1576 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1577 p.addRect(bounds);
1578 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001579
reed@google.com7e6c4d12012-05-10 14:05:43 +00001580 test_isLine(reporter);
1581 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001582 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001583 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001584 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001585 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001586 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001587 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001588 test_flattening(reporter);
1589 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001590 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001591 test_iter(reporter);
1592 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001593 test_circle(reporter);
1594 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001595 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001596 test_addPoly(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001597}
1598
1599#include "TestClassDef.h"
1600DEFINE_TESTCLASS("Path", PathTestClass, TestPath)