blob: d3e7408ac240c0535ee787b5904c8516250b8ed5 [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;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000764 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000765
schenney@chromium.org7e963602012-06-13 17:05:43 +0000766 struct zeroPathTestData {
767 const char* testPath;
768 const size_t numResultPts;
769 const SkRect resultBound;
770 const SkPath::Verb* resultVerbs;
771 const size_t numResultVerbs;
772 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000773
schenney@chromium.org7e963602012-06-13 17:05:43 +0000774 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
775 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
776 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
777 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
778 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
779 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
780 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
781 static const SkPath::Verb resultVerbs8[] = {
782 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
783 };
784 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
785 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
786 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
787 static const SkPath::Verb resultVerbs12[] = {
788 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
789 };
790 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
791 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
792 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
793 static const SkPath::Verb resultVerbs16[] = {
794 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
795 };
796 static const struct zeroPathTestData gZeroLengthTests[] = {
797 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
798 { "M 1 1 m 1 0", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
799 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
800 { "M 1 1 z m 1 0 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
801 { "M 1 1 l 0 0", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
802 { "M 1 1 l 0 0 m 1 0 l 0 0", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
803 { "M 1 1 l 0 0 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
804 { "M 1 1 l 0 0 z m 1 0 l 0 0 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
805 { "M 1 1 q 0 0 0 0", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
806 { "M 1 1 q 0 0 0 0 m 1 0 q 0 0 0 0", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
807 { "M 1 1 q 0 0 0 0 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
808 { "M 1 1 q 0 0 0 0 z m 1 0 q 0 0 0 0 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
809 { "M 1 1 c 0 0 0 0 0 0", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
810 { "M 1 1 c 0 0 0 0 0 0 m 1 0 c 0 0 0 0 0 0", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
811 SK_ARRAY_COUNT(resultVerbs14)
812 },
813 { "M 1 1 c 0 0 0 0 0 0 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
814 { "M 1 1 c 0 0 0 0 0 0 z m 1 0 c 0 0 0 0 0 0 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
815 SK_ARRAY_COUNT(resultVerbs16)
816 }
817 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000818
schenney@chromium.org7e963602012-06-13 17:05:43 +0000819 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
820 p.reset();
821 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
822 REPORTER_ASSERT(reporter, valid);
823 REPORTER_ASSERT(reporter, !p.isEmpty());
824 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
825 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
826 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
827 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
828 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
829 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000830 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000831}
832
833struct SegmentInfo {
834 SkPath fPath;
835 int fPointCount;
836};
837
reed@google.com10296cc2011-09-21 12:29:05 +0000838#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
839
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000840static void test_segment_masks(skiatest::Reporter* reporter) {
841 SkPath p;
842 p.moveTo(0, 0);
843 p.quadTo(100, 100, 200, 200);
844 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
845 REPORTER_ASSERT(reporter, !p.isEmpty());
846 p.cubicTo(100, 100, 200, 200, 300, 300);
847 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
848 REPORTER_ASSERT(reporter, !p.isEmpty());
849 p.reset();
850 p.moveTo(0, 0);
851 p.cubicTo(100, 100, 200, 200, 300, 300);
852 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
853 REPORTER_ASSERT(reporter, !p.isEmpty());
854}
855
856static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000857 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000858 SkPoint pts[4];
859
860 // Test an iterator with no path
861 SkPath::Iter noPathIter;
862 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000863
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000864 // Test that setting an empty path works
865 noPathIter.setPath(p, false);
866 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000867
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000868 // Test that close path makes no difference for an empty path
869 noPathIter.setPath(p, true);
870 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000871
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000872 // Test an iterator with an initial empty path
873 SkPath::Iter iter(p, false);
874 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
875
876 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +0000877 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000878 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
879
schenney@chromium.org7e963602012-06-13 17:05:43 +0000880
881 struct iterTestData {
882 const char* testPath;
883 const bool forceClose;
884 const bool consumeDegenerates;
885 const size_t* numResultPtsPerVerb;
886 const SkPoint* resultPts;
887 const SkPath::Verb* resultVerbs;
888 const size_t numResultVerbs;
889 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000890
schenney@chromium.org7e963602012-06-13 17:05:43 +0000891 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
892 static const SkPath::Verb resultVerbs2[] = {
893 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
894 };
895 static const SkPath::Verb resultVerbs3[] = {
896 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
897 };
898 static const SkPath::Verb resultVerbs4[] = {
899 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
900 };
901 static const SkPath::Verb resultVerbs5[] = {
902 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
903 };
904 static const size_t resultPtsSizes1[] = { 0 };
905 static const size_t resultPtsSizes2[] = { 1, 2, 2 };
906 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1 };
907 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1 };
908 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1 };
909 static const SkPoint resultPts1[] = { };
910 static const SkPoint resultPts2[] = {
911 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
912 };
913 static const SkPoint resultPts3[] = {
914 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
915 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
916 };
917 static const SkPoint resultPts4[] = {
918 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
919 };
920 static const SkPoint resultPts5[] = {
921 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
922 };
923 static const struct iterTestData gIterTests[] = {
924 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
925 { "M 1 0 m 1 0 m 1 0 m 1 0 m 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
926 { "M 1 0 m 1 0 m 1 0 m 1 0 m 1 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
927 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
928 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
929 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
930 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
931 { "M 1 0 l 0 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
932 { "M 1 0 l 0 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
933 { "M 1 0 l 0 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
934 { "M 1 0 l 0 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
935 { "M 1 0 l 0 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
936 { "M 1 0 l 0 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
937 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000938
schenney@chromium.org7e963602012-06-13 17:05:43 +0000939 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
940 p.reset();
941 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
942 REPORTER_ASSERT(reporter, valid);
943 iter.setPath(p, gIterTests[i].forceClose);
944 int j = 0, l = 0;
945 do {
946 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
947 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
948 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
949 }
950 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
951 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
952 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000953
954 // The GM degeneratesegments.cpp test is more extensive
955}
956
957static void test_raw_iter(skiatest::Reporter* reporter) {
958 SkPath p;
959 SkPoint pts[4];
960
961 // Test an iterator with no path
962 SkPath::RawIter noPathIter;
963 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
964 // Test that setting an empty path works
965 noPathIter.setPath(p);
966 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
967
968 // Test an iterator with an initial empty path
969 SkPath::RawIter iter(p);
970 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
971
972 // Test that a move-only path returns the move.
973 p.moveTo(SK_Scalar1, 0);
974 iter.setPath(p);
975 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
976 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
977 REPORTER_ASSERT(reporter, pts[0].fY == 0);
978 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
979
980 // No matter how many moves we add, we should get them all back
981 p.moveTo(SK_Scalar1*2, SK_Scalar1);
982 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
983 iter.setPath(p);
984 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
985 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
986 REPORTER_ASSERT(reporter, pts[0].fY == 0);
987 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
988 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
989 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
990 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
991 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
992 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
993 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
994
995 // Initial close is never ever stored
996 p.reset();
997 p.close();
998 iter.setPath(p);
999 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1000
1001 // Move/close sequences
1002 p.reset();
1003 p.close(); // Not stored, no purpose
1004 p.moveTo(SK_Scalar1, 0);
1005 p.close();
1006 p.close(); // Not stored, no purpose
1007 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1008 p.close();
1009 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1010 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1011 p.close();
1012 iter.setPath(p);
1013 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1014 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1015 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1016 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1017 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1018 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1019 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1020 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1021 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1022 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1023 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1024 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1025 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1026 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1027 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1028 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1029 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1030 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1031 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1032 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1033 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1034 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1035
1036 // Generate random paths and verify
1037 SkPoint randomPts[25];
1038 for (int i = 0; i < 5; ++i) {
1039 for (int j = 0; j < 5; ++j) {
1040 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1041 }
1042 }
1043
1044 // Max of 10 segments, max 3 points per segment
1045 SkRandom rand(9876543);
1046 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001047 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001048 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001049
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001050 for (int i = 0; i < 500; ++i) {
1051 p.reset();
1052 bool lastWasClose = true;
1053 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001054 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001055 int numPoints = 0;
1056 int numVerbs = (rand.nextU() >> 16) % 10;
1057 int numIterVerbs = 0;
1058 for (int j = 0; j < numVerbs; ++j) {
1059 do {
1060 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1061 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001062 switch (nextVerb) {
1063 case SkPath::kMove_Verb:
1064 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1065 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001066 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001067 numPoints += 1;
1068 lastWasClose = false;
1069 haveMoveTo = true;
1070 break;
1071 case SkPath::kLine_Verb:
1072 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001073 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001074 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1075 haveMoveTo = true;
1076 }
1077 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1078 p.lineTo(expectedPts[numPoints]);
1079 numPoints += 1;
1080 lastWasClose = false;
1081 break;
1082 case SkPath::kQuad_Verb:
1083 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001084 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001085 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1086 haveMoveTo = true;
1087 }
1088 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1089 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1090 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1091 numPoints += 2;
1092 lastWasClose = false;
1093 break;
1094 case SkPath::kCubic_Verb:
1095 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001096 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001097 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1098 haveMoveTo = true;
1099 }
1100 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1101 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1102 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1103 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1104 expectedPts[numPoints + 2]);
1105 numPoints += 3;
1106 lastWasClose = false;
1107 break;
1108 case SkPath::kClose_Verb:
1109 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001110 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001111 lastWasClose = true;
1112 break;
1113 default:;
1114 }
1115 expectedVerbs[numIterVerbs++] = nextVerb;
1116 }
1117
1118 iter.setPath(p);
1119 numVerbs = numIterVerbs;
1120 numIterVerbs = 0;
1121 int numIterPts = 0;
1122 SkPoint lastMoveTo;
1123 SkPoint lastPt;
1124 lastMoveTo.set(0, 0);
1125 lastPt.set(0, 0);
1126 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1127 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1128 numIterVerbs++;
1129 switch (nextVerb) {
1130 case SkPath::kMove_Verb:
1131 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1132 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1133 lastPt = lastMoveTo = pts[0];
1134 numIterPts += 1;
1135 break;
1136 case SkPath::kLine_Verb:
1137 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1138 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1139 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1140 lastPt = pts[1];
1141 numIterPts += 1;
1142 break;
1143 case SkPath::kQuad_Verb:
1144 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1145 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1146 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1147 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1148 lastPt = pts[2];
1149 numIterPts += 2;
1150 break;
1151 case SkPath::kCubic_Verb:
1152 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1153 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1154 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1155 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1156 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1157 lastPt = pts[3];
1158 numIterPts += 3;
1159 break;
1160 case SkPath::kClose_Verb:
1161 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1162 lastPt = lastMoveTo;
1163 break;
1164 default:;
1165 }
1166 }
1167 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1168 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1169 }
1170}
1171
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001172static void check_for_circle(skiatest::Reporter* reporter,
1173 const SkPath& path, bool expected) {
1174 SkRect rect;
1175 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1176 if (expected) {
1177 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1178 }
1179}
1180
1181static void test_circle_skew(skiatest::Reporter* reporter,
1182 const SkPath& path) {
1183 SkPath tmp;
1184
1185 SkMatrix m;
1186 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1187 path.transform(m, &tmp);
1188 check_for_circle(reporter, tmp, false);
1189}
1190
1191static void test_circle_translate(skiatest::Reporter* reporter,
1192 const SkPath& path) {
1193 SkPath tmp;
1194
1195 // translate at small offset
1196 SkMatrix m;
1197 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1198 path.transform(m, &tmp);
1199 check_for_circle(reporter, tmp, true);
1200
1201 tmp.reset();
1202 m.reset();
1203
1204 // translate at a relatively big offset
1205 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1206 path.transform(m, &tmp);
1207 check_for_circle(reporter, tmp, true);
1208}
1209
1210static void test_circle_rotate(skiatest::Reporter* reporter,
1211 const SkPath& path) {
1212 for (int angle = 0; angle < 360; ++angle) {
1213 SkPath tmp;
1214 SkMatrix m;
1215 m.setRotate(SkIntToScalar(angle));
1216 path.transform(m, &tmp);
1217
1218 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1219 // degrees is not an oval anymore, this can be improved. we made this
1220 // for the simplicity of our implementation.
1221 if (angle % 90 == 0) {
1222 check_for_circle(reporter, tmp, true);
1223 } else {
1224 check_for_circle(reporter, tmp, false);
1225 }
1226 }
1227}
1228
1229static void test_circle_with_direction(skiatest::Reporter* reporter,
1230 SkPath::Direction dir) {
1231 SkPath path;
1232
1233 // circle at origin
1234 path.addCircle(0, 0, SkIntToScalar(20), dir);
1235 check_for_circle(reporter, path, true);
1236 test_circle_rotate(reporter, path);
1237 test_circle_translate(reporter, path);
1238 test_circle_skew(reporter, path);
1239
1240 // circle at an offset at (10, 10)
1241 path.reset();
1242 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1243 SkIntToScalar(20), dir);
1244 check_for_circle(reporter, path, true);
1245 test_circle_rotate(reporter, path);
1246 test_circle_translate(reporter, path);
1247 test_circle_skew(reporter, path);
1248}
1249
1250static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1251 SkPath path;
1252 SkPath circle;
1253 SkPath rect;
1254 SkPath empty;
1255
1256 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1257 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1258 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1259
1260 SkMatrix translate;
1261 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1262
1263 // For simplicity, all the path concatenation related operations
1264 // would mark it non-circle, though in theory it's still a circle.
1265
1266 // empty + circle (translate)
1267 path = empty;
1268 path.addPath(circle, translate);
1269 check_for_circle(reporter, path, false);
1270
1271 // circle + empty (translate)
1272 path = circle;
1273 path.addPath(empty, translate);
1274 check_for_circle(reporter, path, false);
1275
1276 // test reverseAddPath
1277 path = circle;
1278 path.reverseAddPath(rect);
1279 check_for_circle(reporter, path, false);
1280}
1281
1282static void test_circle(skiatest::Reporter* reporter) {
1283 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1284 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1285
1286 // multiple addCircle()
1287 SkPath path;
1288 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1289 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1290 check_for_circle(reporter, path, false);
1291
1292 // some extra lineTo() would make isOval() fail
1293 path.reset();
1294 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1295 path.lineTo(0, 0);
1296 check_for_circle(reporter, path, false);
1297
1298 // not back to the original point
1299 path.reset();
1300 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1301 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1302 check_for_circle(reporter, path, false);
1303
1304 test_circle_with_add_paths(reporter);
1305}
1306
1307static void test_oval(skiatest::Reporter* reporter) {
1308 SkRect rect;
1309 SkMatrix m;
1310 SkPath path;
1311
1312 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1313 path.addOval(rect);
1314
1315 REPORTER_ASSERT(reporter, path.isOval(NULL));
1316
1317 m.setRotate(SkIntToScalar(90));
1318 SkPath tmp;
1319 path.transform(m, &tmp);
1320 // an oval rotated 90 degrees is still an oval.
1321 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1322
1323 m.reset();
1324 m.setRotate(SkIntToScalar(30));
1325 tmp.reset();
1326 path.transform(m, &tmp);
1327 // an oval rotated 30 degrees is not an oval anymore.
1328 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1329
1330 // since empty path being transformed.
1331 path.reset();
1332 tmp.reset();
1333 m.reset();
1334 path.transform(m, &tmp);
1335 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1336
1337 // empty path is not an oval
1338 tmp.reset();
1339 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1340
1341 // only has moveTo()s
1342 tmp.reset();
1343 tmp.moveTo(0, 0);
1344 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1345 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1346
1347 // mimic WebKit's calling convention,
1348 // call moveTo() first and then call addOval()
1349 path.reset();
1350 path.moveTo(0, 0);
1351 path.addOval(rect);
1352 REPORTER_ASSERT(reporter, path.isOval(NULL));
1353
1354 // copy path
1355 path.reset();
1356 tmp.reset();
1357 tmp.addOval(rect);
1358 path = tmp;
1359 REPORTER_ASSERT(reporter, path.isOval(NULL));
1360}
1361
caryclark@google.com42639cd2012-06-06 12:03:39 +00001362static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001363 {
1364 SkSize size;
1365 size.fWidth = 3.4f;
1366 size.width();
1367 size = SkSize::Make(3,4);
1368 SkISize isize = SkISize::Make(3,4);
1369 }
1370
1371 SkTSize<SkScalar>::Make(3,4);
1372
reed@android.com3abec1d2009-03-02 05:36:20 +00001373 SkPath p, p2;
1374 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001375
reed@android.com3abec1d2009-03-02 05:36:20 +00001376 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001377 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001378 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001379 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001380 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001381 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1382 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1383 REPORTER_ASSERT(reporter, p == p2);
1384 REPORTER_ASSERT(reporter, !(p != p2));
1385
reed@android.comd252db02009-04-01 18:31:44 +00001386 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001387
reed@android.com3abec1d2009-03-02 05:36:20 +00001388 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001389
reed@android.com6b82d1a2009-06-03 02:35:01 +00001390 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1391 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001392 // we have quads or cubics
1393 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001394 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001395
reed@android.com6b82d1a2009-06-03 02:35:01 +00001396 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001397 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001398 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001399
reed@android.com6b82d1a2009-06-03 02:35:01 +00001400 p.addOval(bounds);
1401 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001402 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001403
reed@android.com6b82d1a2009-06-03 02:35:01 +00001404 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001405 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001406 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001407 // we have only lines
1408 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001409 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001410
1411 REPORTER_ASSERT(reporter, p != p2);
1412 REPORTER_ASSERT(reporter, !(p == p2));
1413
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001414 // do getPoints and getVerbs return the right result
1415 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1416 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001417 SkPoint pts[4];
1418 int count = p.getPoints(pts, 4);
1419 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001420 uint8_t verbs[6];
1421 verbs[5] = 0xff;
1422 p.getVerbs(verbs, 5);
1423 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1424 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1425 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1426 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1427 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1428 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001429 bounds2.set(pts, 4);
1430 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001431
reed@android.com3abec1d2009-03-02 05:36:20 +00001432 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1433 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001434 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001435
reed@android.com3abec1d2009-03-02 05:36:20 +00001436 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001437 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001438 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1439 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001440
reed@android.com3abec1d2009-03-02 05:36:20 +00001441 // now force p to not be a rect
1442 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1443 p.addRect(bounds);
1444 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001445
reed@google.com7e6c4d12012-05-10 14:05:43 +00001446 test_isLine(reporter);
1447 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001448 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001449 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001450 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001451 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001452 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001453 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001454 test_flattening(reporter);
1455 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001456 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001457 test_iter(reporter);
1458 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001459 test_circle(reporter);
1460 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001461 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001462 test_addPoly(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001463}
1464
1465#include "TestClassDef.h"
1466DEFINE_TESTCLASS("Path", PathTestClass, TestPath)