blob: affc2232005f0d87f3e80a9e77e28aad78185305 [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"
schenney@chromium.org6630d8d2012-01-04 21:05:51 +000013#include "SkRandom.h"
reed@google.com53effc52011-09-21 19:05:12 +000014#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000015#include "SkSize.h"
reed@google.com53effc52011-09-21 19:05:12 +000016#include "SkWriter32.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000017
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000018/**
19 * cheapIsDirection can take a shortcut when a path is marked convex.
20 * This function ensures that we always test cheapIsDirection when the path
21 * is flagged with unknown convexity status.
22 */
23static void check_direction(SkPath* path,
24 SkPath::Direction expectedDir,
25 skiatest::Reporter* reporter) {
26 if (SkPath::kConvex_Convexity == path->getConvexity()) {
27 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
28 path->setConvexity(SkPath::kUnknown_Convexity);
29 }
30 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
31}
32
reed@google.com3e71a882012-01-10 18:44:37 +000033static void test_direction(skiatest::Reporter* reporter) {
34 size_t i;
35 SkPath path;
36 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
37 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
38 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
39
40 static const char* gDegen[] = {
41 "M 10 10",
42 "M 10 10 M 20 20",
43 "M 10 10 L 20 20",
44 "M 10 10 L 10 10 L 10 10",
45 "M 10 10 Q 10 10 10 10",
46 "M 10 10 C 10 10 10 10 10 10",
47 };
48 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
49 path.reset();
50 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
51 REPORTER_ASSERT(reporter, valid);
52 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
53 }
54
55 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000056 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +000057 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +000058 "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 +000059 };
60 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
61 path.reset();
62 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
63 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000064 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +000065 }
66
67 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000068 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +000069 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +000070 "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 +000071 };
72 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
73 path.reset();
74 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
75 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000076 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +000077 }
reed@google.comac8543f2012-01-30 20:51:25 +000078
79 // Test two donuts, each wound a different direction. Only the outer contour
80 // determines the cheap direction
81 path.reset();
82 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
83 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000084 check_direction(&path, SkPath::kCW_Direction, reporter);
85
reed@google.comac8543f2012-01-30 20:51:25 +000086 path.reset();
87 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
88 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000089 check_direction(&path, SkPath::kCCW_Direction, reporter);
90
91 // triangle with one point really far from the origin.
92 path.reset();
93 // the first point is roughly 1.05e10, 1.05e10
94 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
95 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
96 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
97 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +000098}
99
reed@google.comffdb0182011-11-14 19:29:14 +0000100static void add_rect(SkPath* path, const SkRect& r) {
101 path->moveTo(r.fLeft, r.fTop);
102 path->lineTo(r.fRight, r.fTop);
103 path->lineTo(r.fRight, r.fBottom);
104 path->lineTo(r.fLeft, r.fBottom);
105 path->close();
106}
107
108static void test_bounds(skiatest::Reporter* reporter) {
109 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000110 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
111 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
112 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
113 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000114 };
115
116 SkPath path0, path1;
117 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
118 path0.addRect(rects[i]);
119 add_rect(&path1, rects[i]);
120 }
121
122 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
123}
124
reed@google.com55b5f4b2011-09-07 12:23:41 +0000125static void stroke_cubic(const SkPoint pts[4]) {
126 SkPath path;
127 path.moveTo(pts[0]);
128 path.cubicTo(pts[1], pts[2], pts[3]);
129
130 SkPaint paint;
131 paint.setStyle(SkPaint::kStroke_Style);
132 paint.setStrokeWidth(SK_Scalar1 * 2);
133
134 SkPath fill;
135 paint.getFillPath(path, &fill);
136}
137
138// just ensure this can run w/o any SkASSERTS firing in the debug build
139// we used to assert due to differences in how we determine a degenerate vector
140// but that was fixed with the introduction of SkPoint::CanNormalize
141static void stroke_tiny_cubic() {
142 SkPoint p0[] = {
143 { 372.0f, 92.0f },
144 { 372.0f, 92.0f },
145 { 372.0f, 92.0f },
146 { 372.0f, 92.0f },
147 };
148
149 stroke_cubic(p0);
150
151 SkPoint p1[] = {
152 { 372.0f, 92.0f },
153 { 372.0007f, 92.000755f },
154 { 371.99927f, 92.003922f },
155 { 371.99826f, 92.003899f },
156 };
157
158 stroke_cubic(p1);
159}
160
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000161static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
162 for (int i = 0; i < 2; ++i) {
163 SkPath::Iter iter(path, (bool)i);
164 SkPoint mv;
165 SkPoint pts[4];
166 SkPath::Verb v;
167 int nMT = 0;
168 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000169 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000170 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
171 switch (v) {
172 case SkPath::kMove_Verb:
173 mv = pts[0];
174 ++nMT;
175 break;
176 case SkPath::kClose_Verb:
177 REPORTER_ASSERT(reporter, mv == pts[0]);
178 ++nCL;
179 break;
180 default:
181 break;
182 }
183 }
184 // if we force a close on the interator we should have a close
185 // for every moveTo
186 REPORTER_ASSERT(reporter, !i || nMT == nCL);
187 }
188}
189
190static void test_close(skiatest::Reporter* reporter) {
191 SkPath closePt;
192 closePt.moveTo(0, 0);
193 closePt.close();
194 check_close(reporter, closePt);
195
196 SkPath openPt;
197 openPt.moveTo(0, 0);
198 check_close(reporter, openPt);
199
200 SkPath empty;
201 check_close(reporter, empty);
202 empty.close();
203 check_close(reporter, empty);
204
205 SkPath rect;
206 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
207 check_close(reporter, rect);
208 rect.close();
209 check_close(reporter, rect);
210
211 SkPath quad;
212 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
213 check_close(reporter, quad);
214 quad.close();
215 check_close(reporter, quad);
216
217 SkPath cubic;
218 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
219 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
220 check_close(reporter, cubic);
221 cubic.close();
222 check_close(reporter, cubic);
223
224 SkPath line;
225 line.moveTo(SK_Scalar1, SK_Scalar1);
226 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
227 check_close(reporter, line);
228 line.close();
229 check_close(reporter, line);
230
231 SkPath rect2;
232 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
233 rect2.close();
234 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
235 check_close(reporter, rect2);
236 rect2.close();
237 check_close(reporter, rect2);
238
239 SkPath oval3;
240 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
241 oval3.close();
242 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
243 check_close(reporter, oval3);
244 oval3.close();
245 check_close(reporter, oval3);
246
247 SkPath moves;
248 moves.moveTo(SK_Scalar1, SK_Scalar1);
249 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
250 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
251 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
252 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000253
254 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000255}
256
reed@google.com7c424812011-05-15 04:38:34 +0000257static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
258 SkPath::Convexity expected) {
259 SkPath::Convexity c = SkPath::ComputeConvexity(path);
260 REPORTER_ASSERT(reporter, c == expected);
261}
262
263static void test_convexity2(skiatest::Reporter* reporter) {
264 SkPath pt;
265 pt.moveTo(0, 0);
266 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000267 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000268
269 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000270 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
271 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000272 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000273 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000274
275 SkPath triLeft;
276 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000277 triLeft.lineTo(SK_Scalar1, 0);
278 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000279 triLeft.close();
280 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
281
282 SkPath triRight;
283 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000284 triRight.lineTo(-SK_Scalar1, 0);
285 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000286 triRight.close();
287 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
288
289 SkPath square;
290 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000291 square.lineTo(SK_Scalar1, 0);
292 square.lineTo(SK_Scalar1, SK_Scalar1);
293 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000294 square.close();
295 check_convexity(reporter, square, SkPath::kConvex_Convexity);
296
297 SkPath redundantSquare;
298 redundantSquare.moveTo(0, 0);
299 redundantSquare.lineTo(0, 0);
300 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000301 redundantSquare.lineTo(SK_Scalar1, 0);
302 redundantSquare.lineTo(SK_Scalar1, 0);
303 redundantSquare.lineTo(SK_Scalar1, 0);
304 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
305 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
306 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
307 redundantSquare.lineTo(0, SK_Scalar1);
308 redundantSquare.lineTo(0, SK_Scalar1);
309 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000310 redundantSquare.close();
311 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
312
313 SkPath bowTie;
314 bowTie.moveTo(0, 0);
315 bowTie.lineTo(0, 0);
316 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000317 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
318 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
319 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
320 bowTie.lineTo(SK_Scalar1, 0);
321 bowTie.lineTo(SK_Scalar1, 0);
322 bowTie.lineTo(SK_Scalar1, 0);
323 bowTie.lineTo(0, SK_Scalar1);
324 bowTie.lineTo(0, SK_Scalar1);
325 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000326 bowTie.close();
327 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
328
329 SkPath spiral;
330 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000331 spiral.lineTo(100*SK_Scalar1, 0);
332 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
333 spiral.lineTo(0, 100*SK_Scalar1);
334 spiral.lineTo(0, 50*SK_Scalar1);
335 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
336 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000337 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000338 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000339
340 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000341 dent.moveTo(0, 0);
342 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
343 dent.lineTo(0, 100*SK_Scalar1);
344 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
345 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000346 dent.close();
347 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
348}
349
reed@android.com6b82d1a2009-06-03 02:35:01 +0000350static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
351 const SkRect& bounds) {
352 REPORTER_ASSERT(reporter, p.isConvex());
353 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000354
reed@android.com6b82d1a2009-06-03 02:35:01 +0000355 SkPath p2(p);
356 REPORTER_ASSERT(reporter, p2.isConvex());
357 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
358
359 SkPath other;
360 other.swap(p2);
361 REPORTER_ASSERT(reporter, other.isConvex());
362 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
363}
364
reed@google.com04863fa2011-05-15 04:08:24 +0000365static void setFromString(SkPath* path, const char str[]) {
366 bool first = true;
367 while (str) {
368 SkScalar x, y;
369 str = SkParse::FindScalar(str, &x);
370 if (NULL == str) {
371 break;
372 }
373 str = SkParse::FindScalar(str, &y);
374 SkASSERT(str);
375 if (first) {
376 path->moveTo(x, y);
377 first = false;
378 } else {
379 path->lineTo(x, y);
380 }
381 }
382}
383
384static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000385 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
386 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
387
388 SkPath path;
389
reed@google.comb54455e2011-05-16 14:16:04 +0000390 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000391 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000392 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000393 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000394 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
395 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000396 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000397 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000398 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000399 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000400 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000401 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000402 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000403
404 static const struct {
405 const char* fPathStr;
406 SkPath::Convexity fExpectedConvexity;
407 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000408 { "", SkPath::kConvex_Convexity },
409 { "0 0", SkPath::kConvex_Convexity },
410 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000411 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000412 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
413 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
414 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
415 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
416 };
417
418 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
419 SkPath path;
420 setFromString(&path, gRec[i].fPathStr);
421 SkPath::Convexity c = SkPath::ComputeConvexity(path);
422 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
423 }
424}
425
caryclark@google.comf1316942011-07-26 19:54:45 +0000426// Simple isRect test is inline TestPath, below.
427// test_isRect provides more extensive testing.
428static void test_isRect(skiatest::Reporter* reporter) {
429 // passing tests (all moveTo / lineTo...
430 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
431 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
432 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
433 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
434 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
435 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
436 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
437 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
438 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
439 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
440 {1, 0}, {.5f, 0}};
441 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
442 {0, 1}, {0, .5f}};
443 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
444 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
445 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
446
447 // failing tests
448 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
449 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
450 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
451 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
452 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
453 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
454 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
455 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
456
457 // failing, no close
458 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
459 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
460
461 size_t testLen[] = {
462 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
463 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
464 sizeof(rd), sizeof(re),
465 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
466 sizeof(f7), sizeof(f8),
467 sizeof(c1), sizeof(c2)
468 };
469 SkPoint* tests[] = {
470 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
471 f1, f2, f3, f4, f5, f6, f7, f8,
472 c1, c2
473 };
474 SkPoint* lastPass = re;
475 SkPoint* lastClose = f8;
476 bool fail = false;
477 bool close = true;
478 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
479 size_t index;
480 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
481 SkPath path;
482 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
483 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
484 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
485 }
486 if (close) {
487 path.close();
488 }
489 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
490 if (tests[testIndex] == lastPass) {
491 fail = true;
492 }
493 if (tests[testIndex] == lastClose) {
494 close = false;
495 }
496 }
497
498 // fail, close then line
499 SkPath path1;
500 path1.moveTo(r1[0].fX, r1[0].fY);
501 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
502 path1.lineTo(r1[index].fX, r1[index].fY);
503 }
504 path1.close();
505 path1.lineTo(1, 0);
506 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
507
508 // fail, move in the middle
509 path1.reset();
510 path1.moveTo(r1[0].fX, r1[0].fY);
511 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
512 if (index == 2) {
513 path1.moveTo(1, .5f);
514 }
515 path1.lineTo(r1[index].fX, r1[index].fY);
516 }
517 path1.close();
518 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
519
520 // fail, move on the edge
521 path1.reset();
522 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
523 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
524 path1.lineTo(r1[index].fX, r1[index].fY);
525 }
526 path1.close();
527 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
528
529 // fail, quad
530 path1.reset();
531 path1.moveTo(r1[0].fX, r1[0].fY);
532 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
533 if (index == 2) {
534 path1.quadTo(1, .5f, 1, .5f);
535 }
536 path1.lineTo(r1[index].fX, r1[index].fY);
537 }
538 path1.close();
539 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
540
541 // fail, cubic
542 path1.reset();
543 path1.moveTo(r1[0].fX, r1[0].fY);
544 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
545 if (index == 2) {
546 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
547 }
548 path1.lineTo(r1[index].fX, r1[index].fY);
549 }
550 path1.close();
551 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
552}
553
reed@google.com53effc52011-09-21 19:05:12 +0000554static void test_flattening(skiatest::Reporter* reporter) {
555 SkPath p;
556
557 static const SkPoint pts[] = {
558 { 0, 0 },
559 { SkIntToScalar(10), SkIntToScalar(10) },
560 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
561 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
562 };
563 p.moveTo(pts[0]);
564 p.lineTo(pts[1]);
565 p.quadTo(pts[2], pts[3]);
566 p.cubicTo(pts[4], pts[5], pts[6]);
567
568 SkWriter32 writer(100);
569 p.flatten(writer);
570 size_t size = writer.size();
571 SkAutoMalloc storage(size);
572 writer.flatten(storage.get());
573 SkReader32 reader(storage.get(), size);
574
575 SkPath p1;
576 REPORTER_ASSERT(reporter, p1 != p);
577 p1.unflatten(reader);
578 REPORTER_ASSERT(reporter, p1 == p);
579}
580
581static void test_transform(skiatest::Reporter* reporter) {
582 SkPath p, p1;
583
584 static const SkPoint pts[] = {
585 { 0, 0 },
586 { SkIntToScalar(10), SkIntToScalar(10) },
587 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
588 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
589 };
590 p.moveTo(pts[0]);
591 p.lineTo(pts[1]);
592 p.quadTo(pts[2], pts[3]);
593 p.cubicTo(pts[4], pts[5], pts[6]);
594
595 SkMatrix matrix;
596 matrix.reset();
597 p.transform(matrix, &p1);
598 REPORTER_ASSERT(reporter, p == p1);
599
600 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
601 p.transform(matrix, &p1);
602 SkPoint pts1[7];
603 int count = p1.getPoints(pts1, 7);
604 REPORTER_ASSERT(reporter, 7 == count);
605 for (int i = 0; i < count; ++i) {
606 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
607 REPORTER_ASSERT(reporter, newPt == pts1[i]);
608 }
609}
610
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000611static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000612 SkPath p;
613 SkPoint pt;
614 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000615
616 // Lone moveTo case
617 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000618 REPORTER_ASSERT(reporter, !p.isEmpty());
619 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000620 p.getLastPt(&pt);
621 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
622 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
623 bounds.set(0, 0, 0, 0);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000624 REPORTER_ASSERT(reporter, bounds == p.getBounds());
625
626 // MoveTo-MoveTo case
627 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000628 REPORTER_ASSERT(reporter, !p.isEmpty());
629 REPORTER_ASSERT(reporter, 2 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000630 p.getLastPt(&pt);
631 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
632 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
633 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000634 REPORTER_ASSERT(reporter, bounds == p.getBounds());
635
636 // moveTo-close case
637 p.reset();
638 p.moveTo(SK_Scalar1, SK_Scalar1);
639 p.close();
640 bounds.set(0, 0, 0, 0);
641 REPORTER_ASSERT(reporter, !p.isEmpty());
642 REPORTER_ASSERT(reporter, 1 == p.countPoints());
643 REPORTER_ASSERT(reporter, bounds == p.getBounds());
644
645 // moveTo-close-moveTo-close case
646 p.moveTo(SK_Scalar1*2, SK_Scalar1);
647 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000648 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000649 REPORTER_ASSERT(reporter, !p.isEmpty());
650 REPORTER_ASSERT(reporter, 2 == p.countPoints());
651 REPORTER_ASSERT(reporter, bounds == p.getBounds());
652
653 // moveTo-line case
654 p.reset();
655 p.moveTo(SK_Scalar1, SK_Scalar1);
656 p.lineTo(SK_Scalar1, SK_Scalar1);
657 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
658 REPORTER_ASSERT(reporter, !p.isEmpty());
659 REPORTER_ASSERT(reporter, 2 == p.countPoints());
660 REPORTER_ASSERT(reporter, bounds == p.getBounds());
661
662 // moveTo-lineTo-moveTo-lineTo case
663 p.moveTo(SK_Scalar1*2, SK_Scalar1);
664 p.lineTo(SK_Scalar1*2, SK_Scalar1);
665 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
666 REPORTER_ASSERT(reporter, !p.isEmpty());
667 REPORTER_ASSERT(reporter, 4 == p.countPoints());
668 REPORTER_ASSERT(reporter, bounds == p.getBounds());
669
670 // moveTo-line-close case
671 p.reset();
672 p.moveTo(SK_Scalar1, SK_Scalar1);
673 p.lineTo(SK_Scalar1, SK_Scalar1);
674 p.close();
675 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
676 REPORTER_ASSERT(reporter, !p.isEmpty());
677 REPORTER_ASSERT(reporter, 2 == p.countPoints());
678 REPORTER_ASSERT(reporter, bounds == p.getBounds());
679
680 // moveTo-line-close-moveTo-line-close case
681 p.moveTo(SK_Scalar1*2, SK_Scalar1);
682 p.lineTo(SK_Scalar1*2, SK_Scalar1);
683 p.close();
684 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
685 REPORTER_ASSERT(reporter, !p.isEmpty());
686 REPORTER_ASSERT(reporter, 4 == p.countPoints());
687 REPORTER_ASSERT(reporter, bounds == p.getBounds());
688
689 // moveTo-quadTo case
690 p.reset();
691 p.moveTo(SK_Scalar1, SK_Scalar1);
692 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
693 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
694 REPORTER_ASSERT(reporter, !p.isEmpty());
695 REPORTER_ASSERT(reporter, 3 == p.countPoints());
696 REPORTER_ASSERT(reporter, bounds == p.getBounds());
697
698 // moveTo-quadTo-close case
699 p.close();
700 REPORTER_ASSERT(reporter, !p.isEmpty());
701 REPORTER_ASSERT(reporter, 3 == p.countPoints());
702 REPORTER_ASSERT(reporter, bounds == p.getBounds());
703
704 // moveTo-quadTo-moveTo-quadTo case
705 p.reset();
706 p.moveTo(SK_Scalar1, SK_Scalar1);
707 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
708 p.moveTo(SK_Scalar1*2, SK_Scalar1);
709 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
710 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
711 REPORTER_ASSERT(reporter, !p.isEmpty());
712 REPORTER_ASSERT(reporter, 6 == p.countPoints());
713 REPORTER_ASSERT(reporter, bounds == p.getBounds());
714
715 // moveTo-cubicTo case
716 p.reset();
717 p.moveTo(SK_Scalar1, SK_Scalar1);
718 p.cubicTo(SK_Scalar1, SK_Scalar1,
719 SK_Scalar1, SK_Scalar1,
720 SK_Scalar1, SK_Scalar1);
721 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
722 REPORTER_ASSERT(reporter, !p.isEmpty());
723 REPORTER_ASSERT(reporter, 4 == p.countPoints());
724 REPORTER_ASSERT(reporter, bounds == p.getBounds());
725
726 // moveTo-quadTo-close case
727 p.close();
728 REPORTER_ASSERT(reporter, !p.isEmpty());
729 REPORTER_ASSERT(reporter, 4 == p.countPoints());
730 REPORTER_ASSERT(reporter, bounds == p.getBounds());
731
732 // moveTo-quadTo-moveTo-quadTo case
733 p.reset();
734 p.moveTo(SK_Scalar1, SK_Scalar1);
735 p.cubicTo(SK_Scalar1, SK_Scalar1,
736 SK_Scalar1, SK_Scalar1,
737 SK_Scalar1, SK_Scalar1);
738 p.moveTo(SK_Scalar1*2, SK_Scalar1);
739 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
740 SK_Scalar1*2, SK_Scalar1,
741 SK_Scalar1*2, SK_Scalar1);
742 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
743 REPORTER_ASSERT(reporter, !p.isEmpty());
744 REPORTER_ASSERT(reporter, 8 == p.countPoints());
745 REPORTER_ASSERT(reporter, bounds == p.getBounds());
746}
747
748struct SegmentInfo {
749 SkPath fPath;
750 int fPointCount;
751};
752
reed@google.com10296cc2011-09-21 12:29:05 +0000753#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
754
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000755static void test_segment_masks(skiatest::Reporter* reporter) {
756 SkPath p;
757 p.moveTo(0, 0);
758 p.quadTo(100, 100, 200, 200);
759 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
760 REPORTER_ASSERT(reporter, !p.isEmpty());
761 p.cubicTo(100, 100, 200, 200, 300, 300);
762 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
763 REPORTER_ASSERT(reporter, !p.isEmpty());
764 p.reset();
765 p.moveTo(0, 0);
766 p.cubicTo(100, 100, 200, 200, 300, 300);
767 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
768 REPORTER_ASSERT(reporter, !p.isEmpty());
769}
770
771static void test_iter(skiatest::Reporter* reporter) {
772 SkPath p;
773 SkPoint pts[4];
774
775 // Test an iterator with no path
776 SkPath::Iter noPathIter;
777 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
778 // Test that setting an empty path works
779 noPathIter.setPath(p, false);
780 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
781 // Test that close path makes no difference for an empty path
782 noPathIter.setPath(p, true);
783 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
784
785 // Test an iterator with an initial empty path
786 SkPath::Iter iter(p, false);
787 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
788
789 // Test that close path makes no difference
790 SkPath::Iter forceCloseIter(p, true);
791 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
792
793 // Test that a move-only path produces nothing when iterated.
794 p.moveTo(SK_Scalar1, 0);
795 iter.setPath(p, false);
796 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
797
798 // No matter how many moves we add, we should still get nothing back.
799 p.moveTo(SK_Scalar1*2, 0);
800 p.moveTo(SK_Scalar1*3, 0);
801 p.moveTo(SK_Scalar1*4, 0);
802 p.moveTo(SK_Scalar1*5, 0);
803 iter.setPath(p, false);
804 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
805
806 // Nor should force closing
807 forceCloseIter.setPath(p, true);
808 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
809
810 // Initial closes should be ignored
811 p.reset();
812 p.close();
813 iter.setPath(p, false);
814 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
815 // Even if force closed
816 forceCloseIter.setPath(p, true);
817 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
818
819 // Move/close sequences should also be ignored
820 p.reset();
821 p.close();
822 p.moveTo(SK_Scalar1, 0);
823 p.close();
824 p.close();
825 p.moveTo(SK_Scalar1*2, 0);
826 p.close();
827 p.moveTo(SK_Scalar1*3, 0);
828 p.moveTo(SK_Scalar1*4, 0);
829 p.close();
830 iter.setPath(p, false);
831 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
832 // Even if force closed
833 forceCloseIter.setPath(p, true);
834 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
835
836 // The GM degeneratesegments.cpp test is more extensive
837}
838
839static void test_raw_iter(skiatest::Reporter* reporter) {
840 SkPath p;
841 SkPoint pts[4];
842
843 // Test an iterator with no path
844 SkPath::RawIter noPathIter;
845 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
846 // Test that setting an empty path works
847 noPathIter.setPath(p);
848 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
849
850 // Test an iterator with an initial empty path
851 SkPath::RawIter iter(p);
852 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
853
854 // Test that a move-only path returns the move.
855 p.moveTo(SK_Scalar1, 0);
856 iter.setPath(p);
857 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
858 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
859 REPORTER_ASSERT(reporter, pts[0].fY == 0);
860 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
861
862 // No matter how many moves we add, we should get them all back
863 p.moveTo(SK_Scalar1*2, SK_Scalar1);
864 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
865 iter.setPath(p);
866 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
867 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
868 REPORTER_ASSERT(reporter, pts[0].fY == 0);
869 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
870 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
871 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
872 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
873 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
874 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
875 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
876
877 // Initial close is never ever stored
878 p.reset();
879 p.close();
880 iter.setPath(p);
881 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
882
883 // Move/close sequences
884 p.reset();
885 p.close(); // Not stored, no purpose
886 p.moveTo(SK_Scalar1, 0);
887 p.close();
888 p.close(); // Not stored, no purpose
889 p.moveTo(SK_Scalar1*2, SK_Scalar1);
890 p.close();
891 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
892 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
893 p.close();
894 iter.setPath(p);
895 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
896 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
897 REPORTER_ASSERT(reporter, pts[0].fY == 0);
898 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
899 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
900 REPORTER_ASSERT(reporter, pts[0].fY == 0);
901 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
902 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
903 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
904 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
905 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
906 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
907 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
908 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
909 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
910 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
911 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
912 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
913 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
914 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
915 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
916 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
917
918 // Generate random paths and verify
919 SkPoint randomPts[25];
920 for (int i = 0; i < 5; ++i) {
921 for (int j = 0; j < 5; ++j) {
922 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
923 }
924 }
925
926 // Max of 10 segments, max 3 points per segment
927 SkRandom rand(9876543);
928 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +0000929 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000930 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +0000931
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000932 for (int i = 0; i < 500; ++i) {
933 p.reset();
934 bool lastWasClose = true;
935 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +0000936 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000937 int numPoints = 0;
938 int numVerbs = (rand.nextU() >> 16) % 10;
939 int numIterVerbs = 0;
940 for (int j = 0; j < numVerbs; ++j) {
941 do {
942 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
943 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
944 int numRequiredPts;
945 switch (nextVerb) {
946 case SkPath::kMove_Verb:
947 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
948 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +0000949 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000950 numPoints += 1;
951 lastWasClose = false;
952 haveMoveTo = true;
953 break;
954 case SkPath::kLine_Verb:
955 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000956 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000957 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
958 haveMoveTo = true;
959 }
960 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
961 p.lineTo(expectedPts[numPoints]);
962 numPoints += 1;
963 lastWasClose = false;
964 break;
965 case SkPath::kQuad_Verb:
966 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000967 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000968 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
969 haveMoveTo = true;
970 }
971 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
972 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
973 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
974 numPoints += 2;
975 lastWasClose = false;
976 break;
977 case SkPath::kCubic_Verb:
978 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000979 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000980 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
981 haveMoveTo = true;
982 }
983 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
984 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
985 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
986 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
987 expectedPts[numPoints + 2]);
988 numPoints += 3;
989 lastWasClose = false;
990 break;
991 case SkPath::kClose_Verb:
992 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +0000993 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000994 lastWasClose = true;
995 break;
996 default:;
997 }
998 expectedVerbs[numIterVerbs++] = nextVerb;
999 }
1000
1001 iter.setPath(p);
1002 numVerbs = numIterVerbs;
1003 numIterVerbs = 0;
1004 int numIterPts = 0;
1005 SkPoint lastMoveTo;
1006 SkPoint lastPt;
1007 lastMoveTo.set(0, 0);
1008 lastPt.set(0, 0);
1009 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1010 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1011 numIterVerbs++;
1012 switch (nextVerb) {
1013 case SkPath::kMove_Verb:
1014 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1015 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1016 lastPt = lastMoveTo = pts[0];
1017 numIterPts += 1;
1018 break;
1019 case SkPath::kLine_Verb:
1020 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1021 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1022 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1023 lastPt = pts[1];
1024 numIterPts += 1;
1025 break;
1026 case SkPath::kQuad_Verb:
1027 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1028 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1029 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1030 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1031 lastPt = pts[2];
1032 numIterPts += 2;
1033 break;
1034 case SkPath::kCubic_Verb:
1035 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1036 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1037 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1038 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1039 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1040 lastPt = pts[3];
1041 numIterPts += 3;
1042 break;
1043 case SkPath::kClose_Verb:
1044 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1045 lastPt = lastMoveTo;
1046 break;
1047 default:;
1048 }
1049 }
1050 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1051 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1052 }
1053}
1054
reed@google.com04863fa2011-05-15 04:08:24 +00001055void TestPath(skiatest::Reporter* reporter);
1056void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001057 {
1058 SkSize size;
1059 size.fWidth = 3.4f;
1060 size.width();
1061 size = SkSize::Make(3,4);
1062 SkISize isize = SkISize::Make(3,4);
1063 }
1064
1065 SkTSize<SkScalar>::Make(3,4);
1066
reed@android.com3abec1d2009-03-02 05:36:20 +00001067 SkPath p, p2;
1068 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001069
reed@android.com3abec1d2009-03-02 05:36:20 +00001070 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001071 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +00001072 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001073 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001074 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1075 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1076 REPORTER_ASSERT(reporter, p == p2);
1077 REPORTER_ASSERT(reporter, !(p != p2));
1078
reed@android.comd252db02009-04-01 18:31:44 +00001079 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001080
reed@android.com3abec1d2009-03-02 05:36:20 +00001081 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001082
reed@android.com6b82d1a2009-06-03 02:35:01 +00001083 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1084 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001085 // we have quads or cubics
1086 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001087 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001088
reed@android.com6b82d1a2009-06-03 02:35:01 +00001089 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001090 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001091 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001092
reed@android.com6b82d1a2009-06-03 02:35:01 +00001093 p.addOval(bounds);
1094 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001095 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001096
reed@android.com6b82d1a2009-06-03 02:35:01 +00001097 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001098 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001099 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001100 // we have only lines
1101 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001102 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001103
1104 REPORTER_ASSERT(reporter, p != p2);
1105 REPORTER_ASSERT(reporter, !(p == p2));
1106
1107 // does getPoints return the right result
1108 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1109 SkPoint pts[4];
1110 int count = p.getPoints(pts, 4);
1111 REPORTER_ASSERT(reporter, count == 4);
1112 bounds2.set(pts, 4);
1113 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001114
reed@android.com3abec1d2009-03-02 05:36:20 +00001115 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1116 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001117 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001118
reed@android.com3abec1d2009-03-02 05:36:20 +00001119 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001120 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001121 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1122 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001123
reed@android.com3abec1d2009-03-02 05:36:20 +00001124 // now force p to not be a rect
1125 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1126 p.addRect(bounds);
1127 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001128 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001129
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001130 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001131 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001132 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001133 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001134 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001135 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001136 test_flattening(reporter);
1137 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001138 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001139 test_iter(reporter);
1140 test_raw_iter(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001141}
1142
1143#include "TestClassDef.h"
1144DEFINE_TESTCLASS("Path", PathTestClass, TestPath)