blob: f37760131acc45a130acc5dfaf3f3d2602b4fd48 [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
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000145 // rect with top two corners replaced by cubics with identical middle
146 // control points
147 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000148 };
149 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
150 path.reset();
151 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
152 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000153 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000154 }
155
156 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000157 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000158 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000159 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000160 // rect with top two corners replaced by cubics with identical middle
161 // control points
162 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000163 };
164 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
165 path.reset();
166 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
167 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000168 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000169 }
reed@google.comac8543f2012-01-30 20:51:25 +0000170
171 // Test two donuts, each wound a different direction. Only the outer contour
172 // determines the cheap direction
173 path.reset();
174 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
175 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000176 check_direction(&path, SkPath::kCW_Direction, reporter);
177
reed@google.comac8543f2012-01-30 20:51:25 +0000178 path.reset();
179 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
180 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000181 check_direction(&path, SkPath::kCCW_Direction, reporter);
182
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000183#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000184 // triangle with one point really far from the origin.
185 path.reset();
186 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000187 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
188 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
189 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
190 check_direction(&path, SkPath::kCCW_Direction, reporter);
191#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000192}
193
reed@google.comffdb0182011-11-14 19:29:14 +0000194static void add_rect(SkPath* path, const SkRect& r) {
195 path->moveTo(r.fLeft, r.fTop);
196 path->lineTo(r.fRight, r.fTop);
197 path->lineTo(r.fRight, r.fBottom);
198 path->lineTo(r.fLeft, r.fBottom);
199 path->close();
200}
201
202static void test_bounds(skiatest::Reporter* reporter) {
203 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000204 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
205 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
206 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
207 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000208 };
209
210 SkPath path0, path1;
211 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
212 path0.addRect(rects[i]);
213 add_rect(&path1, rects[i]);
214 }
215
216 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
217}
218
reed@google.com55b5f4b2011-09-07 12:23:41 +0000219static void stroke_cubic(const SkPoint pts[4]) {
220 SkPath path;
221 path.moveTo(pts[0]);
222 path.cubicTo(pts[1], pts[2], pts[3]);
223
224 SkPaint paint;
225 paint.setStyle(SkPaint::kStroke_Style);
226 paint.setStrokeWidth(SK_Scalar1 * 2);
227
228 SkPath fill;
229 paint.getFillPath(path, &fill);
230}
231
232// just ensure this can run w/o any SkASSERTS firing in the debug build
233// we used to assert due to differences in how we determine a degenerate vector
234// but that was fixed with the introduction of SkPoint::CanNormalize
235static void stroke_tiny_cubic() {
236 SkPoint p0[] = {
237 { 372.0f, 92.0f },
238 { 372.0f, 92.0f },
239 { 372.0f, 92.0f },
240 { 372.0f, 92.0f },
241 };
242
243 stroke_cubic(p0);
244
245 SkPoint p1[] = {
246 { 372.0f, 92.0f },
247 { 372.0007f, 92.000755f },
248 { 371.99927f, 92.003922f },
249 { 371.99826f, 92.003899f },
250 };
251
252 stroke_cubic(p1);
253}
254
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000255static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
256 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000257 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000258 SkPoint mv;
259 SkPoint pts[4];
260 SkPath::Verb v;
261 int nMT = 0;
262 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000263 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000264 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
265 switch (v) {
266 case SkPath::kMove_Verb:
267 mv = pts[0];
268 ++nMT;
269 break;
270 case SkPath::kClose_Verb:
271 REPORTER_ASSERT(reporter, mv == pts[0]);
272 ++nCL;
273 break;
274 default:
275 break;
276 }
277 }
278 // if we force a close on the interator we should have a close
279 // for every moveTo
280 REPORTER_ASSERT(reporter, !i || nMT == nCL);
281 }
282}
283
284static void test_close(skiatest::Reporter* reporter) {
285 SkPath closePt;
286 closePt.moveTo(0, 0);
287 closePt.close();
288 check_close(reporter, closePt);
289
290 SkPath openPt;
291 openPt.moveTo(0, 0);
292 check_close(reporter, openPt);
293
294 SkPath empty;
295 check_close(reporter, empty);
296 empty.close();
297 check_close(reporter, empty);
298
299 SkPath rect;
300 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
301 check_close(reporter, rect);
302 rect.close();
303 check_close(reporter, rect);
304
305 SkPath quad;
306 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
307 check_close(reporter, quad);
308 quad.close();
309 check_close(reporter, quad);
310
311 SkPath cubic;
312 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
313 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
314 check_close(reporter, cubic);
315 cubic.close();
316 check_close(reporter, cubic);
317
318 SkPath line;
319 line.moveTo(SK_Scalar1, SK_Scalar1);
320 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
321 check_close(reporter, line);
322 line.close();
323 check_close(reporter, line);
324
325 SkPath rect2;
326 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
327 rect2.close();
328 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
329 check_close(reporter, rect2);
330 rect2.close();
331 check_close(reporter, rect2);
332
333 SkPath oval3;
334 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
335 oval3.close();
336 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
337 check_close(reporter, oval3);
338 oval3.close();
339 check_close(reporter, oval3);
340
341 SkPath moves;
342 moves.moveTo(SK_Scalar1, SK_Scalar1);
343 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
344 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
345 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
346 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000347
348 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000349}
350
reed@google.com7c424812011-05-15 04:38:34 +0000351static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
352 SkPath::Convexity expected) {
353 SkPath::Convexity c = SkPath::ComputeConvexity(path);
354 REPORTER_ASSERT(reporter, c == expected);
355}
356
357static void test_convexity2(skiatest::Reporter* reporter) {
358 SkPath pt;
359 pt.moveTo(0, 0);
360 pt.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 line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000364 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
365 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000366 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000367 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000368
369 SkPath triLeft;
370 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000371 triLeft.lineTo(SK_Scalar1, 0);
372 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000373 triLeft.close();
374 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
375
376 SkPath triRight;
377 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000378 triRight.lineTo(-SK_Scalar1, 0);
379 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000380 triRight.close();
381 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
382
383 SkPath square;
384 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000385 square.lineTo(SK_Scalar1, 0);
386 square.lineTo(SK_Scalar1, SK_Scalar1);
387 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000388 square.close();
389 check_convexity(reporter, square, SkPath::kConvex_Convexity);
390
391 SkPath redundantSquare;
392 redundantSquare.moveTo(0, 0);
393 redundantSquare.lineTo(0, 0);
394 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000395 redundantSquare.lineTo(SK_Scalar1, 0);
396 redundantSquare.lineTo(SK_Scalar1, 0);
397 redundantSquare.lineTo(SK_Scalar1, 0);
398 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
399 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
400 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
401 redundantSquare.lineTo(0, SK_Scalar1);
402 redundantSquare.lineTo(0, SK_Scalar1);
403 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000404 redundantSquare.close();
405 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
406
407 SkPath bowTie;
408 bowTie.moveTo(0, 0);
409 bowTie.lineTo(0, 0);
410 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000411 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
412 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
413 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
414 bowTie.lineTo(SK_Scalar1, 0);
415 bowTie.lineTo(SK_Scalar1, 0);
416 bowTie.lineTo(SK_Scalar1, 0);
417 bowTie.lineTo(0, SK_Scalar1);
418 bowTie.lineTo(0, SK_Scalar1);
419 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000420 bowTie.close();
421 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
422
423 SkPath spiral;
424 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000425 spiral.lineTo(100*SK_Scalar1, 0);
426 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
427 spiral.lineTo(0, 100*SK_Scalar1);
428 spiral.lineTo(0, 50*SK_Scalar1);
429 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
430 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000431 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000432 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000433
434 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000435 dent.moveTo(0, 0);
436 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
437 dent.lineTo(0, 100*SK_Scalar1);
438 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
439 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000440 dent.close();
441 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
442}
443
reed@android.com6b82d1a2009-06-03 02:35:01 +0000444static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
445 const SkRect& bounds) {
446 REPORTER_ASSERT(reporter, p.isConvex());
447 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000448
reed@android.com6b82d1a2009-06-03 02:35:01 +0000449 SkPath p2(p);
450 REPORTER_ASSERT(reporter, p2.isConvex());
451 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
452
453 SkPath other;
454 other.swap(p2);
455 REPORTER_ASSERT(reporter, other.isConvex());
456 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
457}
458
reed@google.com04863fa2011-05-15 04:08:24 +0000459static void setFromString(SkPath* path, const char str[]) {
460 bool first = true;
461 while (str) {
462 SkScalar x, y;
463 str = SkParse::FindScalar(str, &x);
464 if (NULL == str) {
465 break;
466 }
467 str = SkParse::FindScalar(str, &y);
468 SkASSERT(str);
469 if (first) {
470 path->moveTo(x, y);
471 first = false;
472 } else {
473 path->lineTo(x, y);
474 }
475 }
476}
477
478static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000479 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
480 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
481
482 SkPath path;
483
reed@google.comb54455e2011-05-16 14:16:04 +0000484 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000485 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000486 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000487 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000488 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
489 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000490 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000491 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000492 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000493 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000494 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000495 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000496 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000497
498 static const struct {
499 const char* fPathStr;
500 SkPath::Convexity fExpectedConvexity;
501 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000502 { "", SkPath::kConvex_Convexity },
503 { "0 0", SkPath::kConvex_Convexity },
504 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000505 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000506 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
507 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
508 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
509 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
510 };
511
512 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
513 SkPath path;
514 setFromString(&path, gRec[i].fPathStr);
515 SkPath::Convexity c = SkPath::ComputeConvexity(path);
516 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
517 }
518}
519
reed@google.com7e6c4d12012-05-10 14:05:43 +0000520static void test_isLine(skiatest::Reporter* reporter) {
521 SkPath path;
522 SkPoint pts[2];
523 const SkScalar value = SkIntToScalar(5);
524
525 REPORTER_ASSERT(reporter, !path.isLine(NULL));
526
527 // set some non-zero values
528 pts[0].set(value, value);
529 pts[1].set(value, value);
530 REPORTER_ASSERT(reporter, !path.isLine(pts));
531 // check that pts was untouched
532 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
533 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
534
535 const SkScalar moveX = SkIntToScalar(1);
536 const SkScalar moveY = SkIntToScalar(2);
537 SkASSERT(value != moveX && value != moveY);
538
539 path.moveTo(moveX, moveY);
540 REPORTER_ASSERT(reporter, !path.isLine(NULL));
541 REPORTER_ASSERT(reporter, !path.isLine(pts));
542 // check that pts was untouched
543 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
544 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
545
546 const SkScalar lineX = SkIntToScalar(2);
547 const SkScalar lineY = SkIntToScalar(2);
548 SkASSERT(value != lineX && value != lineY);
549
550 path.lineTo(lineX, lineY);
551 REPORTER_ASSERT(reporter, path.isLine(NULL));
552
553 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
554 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
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 path.lineTo(0, 0); // too many points/verbs
560 REPORTER_ASSERT(reporter, !path.isLine(NULL));
561 REPORTER_ASSERT(reporter, !path.isLine(pts));
562 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
563 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
564}
565
caryclark@google.comf1316942011-07-26 19:54:45 +0000566// Simple isRect test is inline TestPath, below.
567// test_isRect provides more extensive testing.
568static void test_isRect(skiatest::Reporter* reporter) {
569 // passing tests (all moveTo / lineTo...
570 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
571 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
572 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
573 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
574 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
575 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
576 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
577 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
578 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
579 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
580 {1, 0}, {.5f, 0}};
581 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
582 {0, 1}, {0, .5f}};
583 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
584 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
585 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
586
587 // failing tests
588 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
589 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
590 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
591 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
592 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
593 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
594 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
595 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
596
597 // failing, no close
598 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
599 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
600
601 size_t testLen[] = {
602 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
603 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
604 sizeof(rd), sizeof(re),
605 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
606 sizeof(f7), sizeof(f8),
607 sizeof(c1), sizeof(c2)
608 };
609 SkPoint* tests[] = {
610 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
611 f1, f2, f3, f4, f5, f6, f7, f8,
612 c1, c2
613 };
614 SkPoint* lastPass = re;
615 SkPoint* lastClose = f8;
616 bool fail = false;
617 bool close = true;
618 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
619 size_t index;
620 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
621 SkPath path;
622 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
623 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
624 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
625 }
626 if (close) {
627 path.close();
628 }
629 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
630 if (tests[testIndex] == lastPass) {
631 fail = true;
632 }
633 if (tests[testIndex] == lastClose) {
634 close = false;
635 }
636 }
637
638 // fail, close then line
639 SkPath path1;
640 path1.moveTo(r1[0].fX, r1[0].fY);
641 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
642 path1.lineTo(r1[index].fX, r1[index].fY);
643 }
644 path1.close();
645 path1.lineTo(1, 0);
646 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
647
648 // fail, move in the middle
649 path1.reset();
650 path1.moveTo(r1[0].fX, r1[0].fY);
651 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
652 if (index == 2) {
653 path1.moveTo(1, .5f);
654 }
655 path1.lineTo(r1[index].fX, r1[index].fY);
656 }
657 path1.close();
658 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
659
660 // fail, move on the edge
661 path1.reset();
662 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
663 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
664 path1.lineTo(r1[index].fX, r1[index].fY);
665 }
666 path1.close();
667 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
668
669 // fail, quad
670 path1.reset();
671 path1.moveTo(r1[0].fX, r1[0].fY);
672 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
673 if (index == 2) {
674 path1.quadTo(1, .5f, 1, .5f);
675 }
676 path1.lineTo(r1[index].fX, r1[index].fY);
677 }
678 path1.close();
679 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
680
681 // fail, cubic
682 path1.reset();
683 path1.moveTo(r1[0].fX, r1[0].fY);
684 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
685 if (index == 2) {
686 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
687 }
688 path1.lineTo(r1[index].fX, r1[index].fY);
689 }
690 path1.close();
691 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
692}
693
reed@google.com53effc52011-09-21 19:05:12 +0000694static void test_flattening(skiatest::Reporter* reporter) {
695 SkPath p;
696
697 static const SkPoint pts[] = {
698 { 0, 0 },
699 { SkIntToScalar(10), SkIntToScalar(10) },
700 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
701 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
702 };
703 p.moveTo(pts[0]);
704 p.lineTo(pts[1]);
705 p.quadTo(pts[2], pts[3]);
706 p.cubicTo(pts[4], pts[5], pts[6]);
707
708 SkWriter32 writer(100);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000709 writer.writePath(p);
reed@google.com53effc52011-09-21 19:05:12 +0000710 size_t size = writer.size();
711 SkAutoMalloc storage(size);
712 writer.flatten(storage.get());
713 SkReader32 reader(storage.get(), size);
714
715 SkPath p1;
716 REPORTER_ASSERT(reporter, p1 != p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000717 reader.readPath(&p1);
reed@google.com53effc52011-09-21 19:05:12 +0000718 REPORTER_ASSERT(reporter, p1 == p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000719
720 // create a buffer that should be much larger than the path so we don't
721 // kill our stack if writer goes too far.
722 char buffer[1024];
723 uint32_t size1 = p.writeToMemory(NULL);
724 uint32_t size2 = p.writeToMemory(buffer);
725 REPORTER_ASSERT(reporter, size1 == size2);
726
727 SkPath p2;
728 uint32_t size3 = p2.readFromMemory(buffer);
729 REPORTER_ASSERT(reporter, size1 == size3);
730 REPORTER_ASSERT(reporter, p == p2);
731
732 char buffer2[1024];
733 size3 = p2.writeToMemory(buffer2);
734 REPORTER_ASSERT(reporter, size1 == size3);
735 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
reed@google.com53effc52011-09-21 19:05:12 +0000736}
737
738static void test_transform(skiatest::Reporter* reporter) {
739 SkPath p, p1;
740
741 static const SkPoint pts[] = {
742 { 0, 0 },
743 { SkIntToScalar(10), SkIntToScalar(10) },
744 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
745 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
746 };
747 p.moveTo(pts[0]);
748 p.lineTo(pts[1]);
749 p.quadTo(pts[2], pts[3]);
750 p.cubicTo(pts[4], pts[5], pts[6]);
751
752 SkMatrix matrix;
753 matrix.reset();
754 p.transform(matrix, &p1);
755 REPORTER_ASSERT(reporter, p == p1);
756
757 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
758 p.transform(matrix, &p1);
759 SkPoint pts1[7];
760 int count = p1.getPoints(pts1, 7);
761 REPORTER_ASSERT(reporter, 7 == count);
762 for (int i = 0; i < count; ++i) {
763 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
764 REPORTER_ASSERT(reporter, newPt == pts1[i]);
765 }
766}
767
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000768static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000769 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000770 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000771
schenney@chromium.org7e963602012-06-13 17:05:43 +0000772 struct zeroPathTestData {
773 const char* testPath;
774 const size_t numResultPts;
775 const SkRect resultBound;
776 const SkPath::Verb* resultVerbs;
777 const size_t numResultVerbs;
778 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000779
schenney@chromium.org7e963602012-06-13 17:05:43 +0000780 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
781 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
782 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
783 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
784 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
785 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
786 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
787 static const SkPath::Verb resultVerbs8[] = {
788 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
789 };
790 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
791 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
792 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
793 static const SkPath::Verb resultVerbs12[] = {
794 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
795 };
796 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
797 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
798 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
799 static const SkPath::Verb resultVerbs16[] = {
800 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
801 };
802 static const struct zeroPathTestData gZeroLengthTests[] = {
803 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000804 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
schenney@chromium.org7e963602012-06-13 17:05:43 +0000805 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000806 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
807 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
808 { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
809 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
810 { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
811 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
812 { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
813 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
814 { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
815 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
816 { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
schenney@chromium.org7e963602012-06-13 17:05:43 +0000817 SK_ARRAY_COUNT(resultVerbs14)
818 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000819 { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
820 { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
schenney@chromium.org7e963602012-06-13 17:05:43 +0000821 SK_ARRAY_COUNT(resultVerbs16)
822 }
823 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000824
schenney@chromium.org7e963602012-06-13 17:05:43 +0000825 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
826 p.reset();
827 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
828 REPORTER_ASSERT(reporter, valid);
829 REPORTER_ASSERT(reporter, !p.isEmpty());
830 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
831 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
832 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
833 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
834 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
835 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000836 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000837}
838
839struct SegmentInfo {
840 SkPath fPath;
841 int fPointCount;
842};
843
reed@google.com10296cc2011-09-21 12:29:05 +0000844#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
845
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000846static void test_segment_masks(skiatest::Reporter* reporter) {
847 SkPath p;
848 p.moveTo(0, 0);
849 p.quadTo(100, 100, 200, 200);
850 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
851 REPORTER_ASSERT(reporter, !p.isEmpty());
852 p.cubicTo(100, 100, 200, 200, 300, 300);
853 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
854 REPORTER_ASSERT(reporter, !p.isEmpty());
855 p.reset();
856 p.moveTo(0, 0);
857 p.cubicTo(100, 100, 200, 200, 300, 300);
858 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
859 REPORTER_ASSERT(reporter, !p.isEmpty());
860}
861
862static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +0000863 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000864 SkPoint pts[4];
865
866 // Test an iterator with no path
867 SkPath::Iter noPathIter;
868 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000869
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000870 // Test that setting an empty path works
871 noPathIter.setPath(p, false);
872 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000873
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000874 // Test that close path makes no difference for an empty path
875 noPathIter.setPath(p, true);
876 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +0000877
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000878 // Test an iterator with an initial empty path
879 SkPath::Iter iter(p, false);
880 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
881
882 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +0000883 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000884 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
885
schenney@chromium.org7e963602012-06-13 17:05:43 +0000886
887 struct iterTestData {
888 const char* testPath;
889 const bool forceClose;
890 const bool consumeDegenerates;
891 const size_t* numResultPtsPerVerb;
892 const SkPoint* resultPts;
893 const SkPath::Verb* resultVerbs;
894 const size_t numResultVerbs;
895 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000896
schenney@chromium.org7e963602012-06-13 17:05:43 +0000897 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
898 static const SkPath::Verb resultVerbs2[] = {
899 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
900 };
901 static const SkPath::Verb resultVerbs3[] = {
902 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
903 };
904 static const SkPath::Verb resultVerbs4[] = {
905 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
906 };
907 static const SkPath::Verb resultVerbs5[] = {
908 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
909 };
910 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +0000911 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
912 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
913 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
914 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000915 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +0000916 static const SkPoint resultPts2[] = {
917 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
918 };
919 static const SkPoint resultPts3[] = {
920 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
921 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
922 };
923 static const SkPoint resultPts4[] = {
924 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
925 };
926 static const SkPoint resultPts5[] = {
927 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
928 };
929 static const struct iterTestData gIterTests[] = {
930 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000931 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
932 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.org7e963602012-06-13 17:05:43 +0000933 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
934 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
935 { "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) },
936 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +0000937 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
938 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
939 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
940 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
941 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
942 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
schenney@chromium.org7e963602012-06-13 17:05:43 +0000943 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000944
schenney@chromium.org7e963602012-06-13 17:05:43 +0000945 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
946 p.reset();
947 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
948 REPORTER_ASSERT(reporter, valid);
949 iter.setPath(p, gIterTests[i].forceClose);
950 int j = 0, l = 0;
951 do {
952 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
953 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
954 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
955 }
956 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
957 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
958 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000959
960 // The GM degeneratesegments.cpp test is more extensive
961}
962
963static void test_raw_iter(skiatest::Reporter* reporter) {
964 SkPath p;
965 SkPoint pts[4];
966
967 // Test an iterator with no path
968 SkPath::RawIter noPathIter;
969 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
970 // Test that setting an empty path works
971 noPathIter.setPath(p);
972 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
973
974 // Test an iterator with an initial empty path
975 SkPath::RawIter iter(p);
976 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
977
978 // Test that a move-only path returns the move.
979 p.moveTo(SK_Scalar1, 0);
980 iter.setPath(p);
981 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
982 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
983 REPORTER_ASSERT(reporter, pts[0].fY == 0);
984 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
985
986 // No matter how many moves we add, we should get them all back
987 p.moveTo(SK_Scalar1*2, SK_Scalar1);
988 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
989 iter.setPath(p);
990 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
991 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
992 REPORTER_ASSERT(reporter, pts[0].fY == 0);
993 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
994 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
995 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
996 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
997 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
998 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
999 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1000
1001 // Initial close is never ever stored
1002 p.reset();
1003 p.close();
1004 iter.setPath(p);
1005 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1006
1007 // Move/close sequences
1008 p.reset();
1009 p.close(); // Not stored, no purpose
1010 p.moveTo(SK_Scalar1, 0);
1011 p.close();
1012 p.close(); // Not stored, no purpose
1013 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1014 p.close();
1015 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1016 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1017 p.close();
1018 iter.setPath(p);
1019 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1020 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1021 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1022 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1023 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1024 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1025 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1026 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1027 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1028 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1029 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1030 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1031 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1032 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1033 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1034 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1035 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1036 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1037 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1038 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1039 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1040 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1041
1042 // Generate random paths and verify
1043 SkPoint randomPts[25];
1044 for (int i = 0; i < 5; ++i) {
1045 for (int j = 0; j < 5; ++j) {
1046 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1047 }
1048 }
1049
1050 // Max of 10 segments, max 3 points per segment
1051 SkRandom rand(9876543);
1052 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001053 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001054 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001055
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001056 for (int i = 0; i < 500; ++i) {
1057 p.reset();
1058 bool lastWasClose = true;
1059 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001060 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001061 int numPoints = 0;
1062 int numVerbs = (rand.nextU() >> 16) % 10;
1063 int numIterVerbs = 0;
1064 for (int j = 0; j < numVerbs; ++j) {
1065 do {
1066 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1067 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001068 switch (nextVerb) {
1069 case SkPath::kMove_Verb:
1070 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1071 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001072 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001073 numPoints += 1;
1074 lastWasClose = false;
1075 haveMoveTo = true;
1076 break;
1077 case SkPath::kLine_Verb:
1078 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001079 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001080 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1081 haveMoveTo = true;
1082 }
1083 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1084 p.lineTo(expectedPts[numPoints]);
1085 numPoints += 1;
1086 lastWasClose = false;
1087 break;
1088 case SkPath::kQuad_Verb:
1089 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001090 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001091 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1092 haveMoveTo = true;
1093 }
1094 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1095 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1096 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1097 numPoints += 2;
1098 lastWasClose = false;
1099 break;
1100 case SkPath::kCubic_Verb:
1101 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001102 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001103 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1104 haveMoveTo = true;
1105 }
1106 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1107 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1108 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1109 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1110 expectedPts[numPoints + 2]);
1111 numPoints += 3;
1112 lastWasClose = false;
1113 break;
1114 case SkPath::kClose_Verb:
1115 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001116 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001117 lastWasClose = true;
1118 break;
1119 default:;
1120 }
1121 expectedVerbs[numIterVerbs++] = nextVerb;
1122 }
1123
1124 iter.setPath(p);
1125 numVerbs = numIterVerbs;
1126 numIterVerbs = 0;
1127 int numIterPts = 0;
1128 SkPoint lastMoveTo;
1129 SkPoint lastPt;
1130 lastMoveTo.set(0, 0);
1131 lastPt.set(0, 0);
1132 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1133 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1134 numIterVerbs++;
1135 switch (nextVerb) {
1136 case SkPath::kMove_Verb:
1137 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1138 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1139 lastPt = lastMoveTo = pts[0];
1140 numIterPts += 1;
1141 break;
1142 case SkPath::kLine_Verb:
1143 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1144 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1145 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1146 lastPt = pts[1];
1147 numIterPts += 1;
1148 break;
1149 case SkPath::kQuad_Verb:
1150 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1151 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1152 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1153 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1154 lastPt = pts[2];
1155 numIterPts += 2;
1156 break;
1157 case SkPath::kCubic_Verb:
1158 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1159 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1160 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1161 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1162 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1163 lastPt = pts[3];
1164 numIterPts += 3;
1165 break;
1166 case SkPath::kClose_Verb:
1167 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1168 lastPt = lastMoveTo;
1169 break;
1170 default:;
1171 }
1172 }
1173 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1174 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1175 }
1176}
1177
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001178static void check_for_circle(skiatest::Reporter* reporter,
1179 const SkPath& path, bool expected) {
1180 SkRect rect;
1181 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1182 if (expected) {
1183 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1184 }
1185}
1186
1187static void test_circle_skew(skiatest::Reporter* reporter,
1188 const SkPath& path) {
1189 SkPath tmp;
1190
1191 SkMatrix m;
1192 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1193 path.transform(m, &tmp);
1194 check_for_circle(reporter, tmp, false);
1195}
1196
1197static void test_circle_translate(skiatest::Reporter* reporter,
1198 const SkPath& path) {
1199 SkPath tmp;
1200
1201 // translate at small offset
1202 SkMatrix m;
1203 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1204 path.transform(m, &tmp);
1205 check_for_circle(reporter, tmp, true);
1206
1207 tmp.reset();
1208 m.reset();
1209
1210 // translate at a relatively big offset
1211 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1212 path.transform(m, &tmp);
1213 check_for_circle(reporter, tmp, true);
1214}
1215
1216static void test_circle_rotate(skiatest::Reporter* reporter,
1217 const SkPath& path) {
1218 for (int angle = 0; angle < 360; ++angle) {
1219 SkPath tmp;
1220 SkMatrix m;
1221 m.setRotate(SkIntToScalar(angle));
1222 path.transform(m, &tmp);
1223
1224 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1225 // degrees is not an oval anymore, this can be improved. we made this
1226 // for the simplicity of our implementation.
1227 if (angle % 90 == 0) {
1228 check_for_circle(reporter, tmp, true);
1229 } else {
1230 check_for_circle(reporter, tmp, false);
1231 }
1232 }
1233}
1234
1235static void test_circle_with_direction(skiatest::Reporter* reporter,
1236 SkPath::Direction dir) {
1237 SkPath path;
1238
1239 // circle at origin
1240 path.addCircle(0, 0, SkIntToScalar(20), dir);
1241 check_for_circle(reporter, path, true);
1242 test_circle_rotate(reporter, path);
1243 test_circle_translate(reporter, path);
1244 test_circle_skew(reporter, path);
1245
1246 // circle at an offset at (10, 10)
1247 path.reset();
1248 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1249 SkIntToScalar(20), dir);
1250 check_for_circle(reporter, path, true);
1251 test_circle_rotate(reporter, path);
1252 test_circle_translate(reporter, path);
1253 test_circle_skew(reporter, path);
1254}
1255
1256static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1257 SkPath path;
1258 SkPath circle;
1259 SkPath rect;
1260 SkPath empty;
1261
1262 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1263 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1264 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1265
1266 SkMatrix translate;
1267 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1268
1269 // For simplicity, all the path concatenation related operations
1270 // would mark it non-circle, though in theory it's still a circle.
1271
1272 // empty + circle (translate)
1273 path = empty;
1274 path.addPath(circle, translate);
1275 check_for_circle(reporter, path, false);
1276
1277 // circle + empty (translate)
1278 path = circle;
1279 path.addPath(empty, translate);
1280 check_for_circle(reporter, path, false);
1281
1282 // test reverseAddPath
1283 path = circle;
1284 path.reverseAddPath(rect);
1285 check_for_circle(reporter, path, false);
1286}
1287
1288static void test_circle(skiatest::Reporter* reporter) {
1289 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1290 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1291
1292 // multiple addCircle()
1293 SkPath path;
1294 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1295 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1296 check_for_circle(reporter, path, false);
1297
1298 // some extra lineTo() would make isOval() fail
1299 path.reset();
1300 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1301 path.lineTo(0, 0);
1302 check_for_circle(reporter, path, false);
1303
1304 // not back to the original point
1305 path.reset();
1306 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1307 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1308 check_for_circle(reporter, path, false);
1309
1310 test_circle_with_add_paths(reporter);
1311}
1312
1313static void test_oval(skiatest::Reporter* reporter) {
1314 SkRect rect;
1315 SkMatrix m;
1316 SkPath path;
1317
1318 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1319 path.addOval(rect);
1320
1321 REPORTER_ASSERT(reporter, path.isOval(NULL));
1322
1323 m.setRotate(SkIntToScalar(90));
1324 SkPath tmp;
1325 path.transform(m, &tmp);
1326 // an oval rotated 90 degrees is still an oval.
1327 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1328
1329 m.reset();
1330 m.setRotate(SkIntToScalar(30));
1331 tmp.reset();
1332 path.transform(m, &tmp);
1333 // an oval rotated 30 degrees is not an oval anymore.
1334 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1335
1336 // since empty path being transformed.
1337 path.reset();
1338 tmp.reset();
1339 m.reset();
1340 path.transform(m, &tmp);
1341 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1342
1343 // empty path is not an oval
1344 tmp.reset();
1345 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1346
1347 // only has moveTo()s
1348 tmp.reset();
1349 tmp.moveTo(0, 0);
1350 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1351 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1352
1353 // mimic WebKit's calling convention,
1354 // call moveTo() first and then call addOval()
1355 path.reset();
1356 path.moveTo(0, 0);
1357 path.addOval(rect);
1358 REPORTER_ASSERT(reporter, path.isOval(NULL));
1359
1360 // copy path
1361 path.reset();
1362 tmp.reset();
1363 tmp.addOval(rect);
1364 path = tmp;
1365 REPORTER_ASSERT(reporter, path.isOval(NULL));
1366}
1367
caryclark@google.com42639cd2012-06-06 12:03:39 +00001368static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001369 {
1370 SkSize size;
1371 size.fWidth = 3.4f;
1372 size.width();
1373 size = SkSize::Make(3,4);
1374 SkISize isize = SkISize::Make(3,4);
1375 }
1376
1377 SkTSize<SkScalar>::Make(3,4);
1378
reed@android.com3abec1d2009-03-02 05:36:20 +00001379 SkPath p, p2;
1380 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001381
reed@android.com3abec1d2009-03-02 05:36:20 +00001382 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001383 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001384 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001385 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001386 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001387 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1388 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1389 REPORTER_ASSERT(reporter, p == p2);
1390 REPORTER_ASSERT(reporter, !(p != p2));
1391
reed@android.comd252db02009-04-01 18:31:44 +00001392 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001393
reed@android.com3abec1d2009-03-02 05:36:20 +00001394 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001395
reed@android.com6b82d1a2009-06-03 02:35:01 +00001396 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1397 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001398 // we have quads or cubics
1399 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001400 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001401
reed@android.com6b82d1a2009-06-03 02:35:01 +00001402 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001403 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001404 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001405
reed@android.com6b82d1a2009-06-03 02:35:01 +00001406 p.addOval(bounds);
1407 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001408 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001409
reed@android.com6b82d1a2009-06-03 02:35:01 +00001410 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001411 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001412 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001413 // we have only lines
1414 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001415 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001416
1417 REPORTER_ASSERT(reporter, p != p2);
1418 REPORTER_ASSERT(reporter, !(p == p2));
1419
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001420 // do getPoints and getVerbs return the right result
1421 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1422 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001423 SkPoint pts[4];
1424 int count = p.getPoints(pts, 4);
1425 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001426 uint8_t verbs[6];
1427 verbs[5] = 0xff;
1428 p.getVerbs(verbs, 5);
1429 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1430 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1431 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1432 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1433 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1434 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001435 bounds2.set(pts, 4);
1436 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001437
reed@android.com3abec1d2009-03-02 05:36:20 +00001438 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1439 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001440 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001441
reed@android.com3abec1d2009-03-02 05:36:20 +00001442 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001443 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001444 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1445 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001446
reed@android.com3abec1d2009-03-02 05:36:20 +00001447 // now force p to not be a rect
1448 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1449 p.addRect(bounds);
1450 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001451
reed@google.com7e6c4d12012-05-10 14:05:43 +00001452 test_isLine(reporter);
1453 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001454 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001455 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001456 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001457 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001458 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001459 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001460 test_flattening(reporter);
1461 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001462 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001463 test_iter(reporter);
1464 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001465 test_circle(reporter);
1466 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001467 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001468 test_addPoly(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001469}
1470
1471#include "TestClassDef.h"
1472DEFINE_TESTCLASS("Path", PathTestClass, TestPath)