blob: d156e5796d2a7dc609f8fb1b91224e7060ba987e [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
reed@google.com3e71a882012-01-10 18:44:37 +000018static void test_direction(skiatest::Reporter* reporter) {
19 size_t i;
20 SkPath path;
21 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
22 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
23 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
24
25 static const char* gDegen[] = {
26 "M 10 10",
27 "M 10 10 M 20 20",
28 "M 10 10 L 20 20",
29 "M 10 10 L 10 10 L 10 10",
30 "M 10 10 Q 10 10 10 10",
31 "M 10 10 C 10 10 10 10 10 10",
32 };
33 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
34 path.reset();
35 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
36 REPORTER_ASSERT(reporter, valid);
37 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
38 }
39
40 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000041 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +000042 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +000043 "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 +000044 };
45 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
46 path.reset();
47 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
48 REPORTER_ASSERT(reporter, valid);
49 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
50 }
51
52 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000053 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +000054 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +000055 "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 +000056 };
57 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
58 path.reset();
59 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
60 REPORTER_ASSERT(reporter, valid);
61 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
62 }
reed@google.comac8543f2012-01-30 20:51:25 +000063
64 // Test two donuts, each wound a different direction. Only the outer contour
65 // determines the cheap direction
66 path.reset();
67 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
68 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
69 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
70
71 path.reset();
72 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
73 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
74 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +000075}
76
reed@google.comffdb0182011-11-14 19:29:14 +000077static void add_rect(SkPath* path, const SkRect& r) {
78 path->moveTo(r.fLeft, r.fTop);
79 path->lineTo(r.fRight, r.fTop);
80 path->lineTo(r.fRight, r.fBottom);
81 path->lineTo(r.fLeft, r.fBottom);
82 path->close();
83}
84
85static void test_bounds(skiatest::Reporter* reporter) {
86 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +000087 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
88 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
89 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
90 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +000091 };
92
93 SkPath path0, path1;
94 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
95 path0.addRect(rects[i]);
96 add_rect(&path1, rects[i]);
97 }
98
99 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
100}
101
reed@google.com55b5f4b2011-09-07 12:23:41 +0000102static void stroke_cubic(const SkPoint pts[4]) {
103 SkPath path;
104 path.moveTo(pts[0]);
105 path.cubicTo(pts[1], pts[2], pts[3]);
106
107 SkPaint paint;
108 paint.setStyle(SkPaint::kStroke_Style);
109 paint.setStrokeWidth(SK_Scalar1 * 2);
110
111 SkPath fill;
112 paint.getFillPath(path, &fill);
113}
114
115// just ensure this can run w/o any SkASSERTS firing in the debug build
116// we used to assert due to differences in how we determine a degenerate vector
117// but that was fixed with the introduction of SkPoint::CanNormalize
118static void stroke_tiny_cubic() {
119 SkPoint p0[] = {
120 { 372.0f, 92.0f },
121 { 372.0f, 92.0f },
122 { 372.0f, 92.0f },
123 { 372.0f, 92.0f },
124 };
125
126 stroke_cubic(p0);
127
128 SkPoint p1[] = {
129 { 372.0f, 92.0f },
130 { 372.0007f, 92.000755f },
131 { 371.99927f, 92.003922f },
132 { 371.99826f, 92.003899f },
133 };
134
135 stroke_cubic(p1);
136}
137
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000138static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
139 for (int i = 0; i < 2; ++i) {
140 SkPath::Iter iter(path, (bool)i);
141 SkPoint mv;
142 SkPoint pts[4];
143 SkPath::Verb v;
144 int nMT = 0;
145 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000146 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000147 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
148 switch (v) {
149 case SkPath::kMove_Verb:
150 mv = pts[0];
151 ++nMT;
152 break;
153 case SkPath::kClose_Verb:
154 REPORTER_ASSERT(reporter, mv == pts[0]);
155 ++nCL;
156 break;
157 default:
158 break;
159 }
160 }
161 // if we force a close on the interator we should have a close
162 // for every moveTo
163 REPORTER_ASSERT(reporter, !i || nMT == nCL);
164 }
165}
166
167static void test_close(skiatest::Reporter* reporter) {
168 SkPath closePt;
169 closePt.moveTo(0, 0);
170 closePt.close();
171 check_close(reporter, closePt);
172
173 SkPath openPt;
174 openPt.moveTo(0, 0);
175 check_close(reporter, openPt);
176
177 SkPath empty;
178 check_close(reporter, empty);
179 empty.close();
180 check_close(reporter, empty);
181
182 SkPath rect;
183 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
184 check_close(reporter, rect);
185 rect.close();
186 check_close(reporter, rect);
187
188 SkPath quad;
189 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
190 check_close(reporter, quad);
191 quad.close();
192 check_close(reporter, quad);
193
194 SkPath cubic;
195 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
196 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
197 check_close(reporter, cubic);
198 cubic.close();
199 check_close(reporter, cubic);
200
201 SkPath line;
202 line.moveTo(SK_Scalar1, SK_Scalar1);
203 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
204 check_close(reporter, line);
205 line.close();
206 check_close(reporter, line);
207
208 SkPath rect2;
209 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
210 rect2.close();
211 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
212 check_close(reporter, rect2);
213 rect2.close();
214 check_close(reporter, rect2);
215
216 SkPath oval3;
217 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
218 oval3.close();
219 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
220 check_close(reporter, oval3);
221 oval3.close();
222 check_close(reporter, oval3);
223
224 SkPath moves;
225 moves.moveTo(SK_Scalar1, SK_Scalar1);
226 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
227 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
228 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
229 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000230
231 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000232}
233
reed@google.com7c424812011-05-15 04:38:34 +0000234static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
235 SkPath::Convexity expected) {
236 SkPath::Convexity c = SkPath::ComputeConvexity(path);
237 REPORTER_ASSERT(reporter, c == expected);
238}
239
240static void test_convexity2(skiatest::Reporter* reporter) {
241 SkPath pt;
242 pt.moveTo(0, 0);
243 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000244 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000245
246 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000247 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
248 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000249 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000250 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000251
252 SkPath triLeft;
253 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000254 triLeft.lineTo(SK_Scalar1, 0);
255 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000256 triLeft.close();
257 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
258
259 SkPath triRight;
260 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000261 triRight.lineTo(-SK_Scalar1, 0);
262 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000263 triRight.close();
264 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
265
266 SkPath square;
267 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000268 square.lineTo(SK_Scalar1, 0);
269 square.lineTo(SK_Scalar1, SK_Scalar1);
270 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000271 square.close();
272 check_convexity(reporter, square, SkPath::kConvex_Convexity);
273
274 SkPath redundantSquare;
275 redundantSquare.moveTo(0, 0);
276 redundantSquare.lineTo(0, 0);
277 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000278 redundantSquare.lineTo(SK_Scalar1, 0);
279 redundantSquare.lineTo(SK_Scalar1, 0);
280 redundantSquare.lineTo(SK_Scalar1, 0);
281 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
282 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
283 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
284 redundantSquare.lineTo(0, SK_Scalar1);
285 redundantSquare.lineTo(0, SK_Scalar1);
286 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000287 redundantSquare.close();
288 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
289
290 SkPath bowTie;
291 bowTie.moveTo(0, 0);
292 bowTie.lineTo(0, 0);
293 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000294 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
295 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
296 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
297 bowTie.lineTo(SK_Scalar1, 0);
298 bowTie.lineTo(SK_Scalar1, 0);
299 bowTie.lineTo(SK_Scalar1, 0);
300 bowTie.lineTo(0, SK_Scalar1);
301 bowTie.lineTo(0, SK_Scalar1);
302 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000303 bowTie.close();
304 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
305
306 SkPath spiral;
307 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000308 spiral.lineTo(100*SK_Scalar1, 0);
309 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
310 spiral.lineTo(0, 100*SK_Scalar1);
311 spiral.lineTo(0, 50*SK_Scalar1);
312 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
313 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000314 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000315 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000316
317 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000318 dent.moveTo(0, 0);
319 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
320 dent.lineTo(0, 100*SK_Scalar1);
321 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
322 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000323 dent.close();
324 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
325}
326
reed@android.com6b82d1a2009-06-03 02:35:01 +0000327static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
328 const SkRect& bounds) {
329 REPORTER_ASSERT(reporter, p.isConvex());
330 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000331
reed@android.com6b82d1a2009-06-03 02:35:01 +0000332 SkPath p2(p);
333 REPORTER_ASSERT(reporter, p2.isConvex());
334 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
335
336 SkPath other;
337 other.swap(p2);
338 REPORTER_ASSERT(reporter, other.isConvex());
339 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
340}
341
reed@google.com04863fa2011-05-15 04:08:24 +0000342static void setFromString(SkPath* path, const char str[]) {
343 bool first = true;
344 while (str) {
345 SkScalar x, y;
346 str = SkParse::FindScalar(str, &x);
347 if (NULL == str) {
348 break;
349 }
350 str = SkParse::FindScalar(str, &y);
351 SkASSERT(str);
352 if (first) {
353 path->moveTo(x, y);
354 first = false;
355 } else {
356 path->lineTo(x, y);
357 }
358 }
359}
360
361static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000362 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
363 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
364
365 SkPath path;
366
reed@google.comb54455e2011-05-16 14:16:04 +0000367 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000368 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000369 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000370 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000371 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
372 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000373 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000374 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000375 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000376 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000377 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000378 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000379 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000380
381 static const struct {
382 const char* fPathStr;
383 SkPath::Convexity fExpectedConvexity;
384 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000385 { "", SkPath::kConvex_Convexity },
386 { "0 0", SkPath::kConvex_Convexity },
387 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000388 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000389 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
390 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
391 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
392 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
393 };
394
395 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
396 SkPath path;
397 setFromString(&path, gRec[i].fPathStr);
398 SkPath::Convexity c = SkPath::ComputeConvexity(path);
399 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
400 }
401}
402
caryclark@google.comf1316942011-07-26 19:54:45 +0000403// Simple isRect test is inline TestPath, below.
404// test_isRect provides more extensive testing.
405static void test_isRect(skiatest::Reporter* reporter) {
406 // passing tests (all moveTo / lineTo...
407 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
408 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
409 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
410 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
411 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
412 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
413 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
414 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
415 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
416 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
417 {1, 0}, {.5f, 0}};
418 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
419 {0, 1}, {0, .5f}};
420 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
421 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
422 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
423
424 // failing tests
425 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
426 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
427 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
428 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
429 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
430 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
431 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
432 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
433
434 // failing, no close
435 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
436 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
437
438 size_t testLen[] = {
439 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
440 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
441 sizeof(rd), sizeof(re),
442 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
443 sizeof(f7), sizeof(f8),
444 sizeof(c1), sizeof(c2)
445 };
446 SkPoint* tests[] = {
447 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
448 f1, f2, f3, f4, f5, f6, f7, f8,
449 c1, c2
450 };
451 SkPoint* lastPass = re;
452 SkPoint* lastClose = f8;
453 bool fail = false;
454 bool close = true;
455 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
456 size_t index;
457 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
458 SkPath path;
459 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
460 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
461 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
462 }
463 if (close) {
464 path.close();
465 }
466 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
467 if (tests[testIndex] == lastPass) {
468 fail = true;
469 }
470 if (tests[testIndex] == lastClose) {
471 close = false;
472 }
473 }
474
475 // fail, close then line
476 SkPath path1;
477 path1.moveTo(r1[0].fX, r1[0].fY);
478 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
479 path1.lineTo(r1[index].fX, r1[index].fY);
480 }
481 path1.close();
482 path1.lineTo(1, 0);
483 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
484
485 // fail, move in the middle
486 path1.reset();
487 path1.moveTo(r1[0].fX, r1[0].fY);
488 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
489 if (index == 2) {
490 path1.moveTo(1, .5f);
491 }
492 path1.lineTo(r1[index].fX, r1[index].fY);
493 }
494 path1.close();
495 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
496
497 // fail, move on the edge
498 path1.reset();
499 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
500 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
501 path1.lineTo(r1[index].fX, r1[index].fY);
502 }
503 path1.close();
504 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
505
506 // fail, quad
507 path1.reset();
508 path1.moveTo(r1[0].fX, r1[0].fY);
509 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
510 if (index == 2) {
511 path1.quadTo(1, .5f, 1, .5f);
512 }
513 path1.lineTo(r1[index].fX, r1[index].fY);
514 }
515 path1.close();
516 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
517
518 // fail, cubic
519 path1.reset();
520 path1.moveTo(r1[0].fX, r1[0].fY);
521 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
522 if (index == 2) {
523 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
524 }
525 path1.lineTo(r1[index].fX, r1[index].fY);
526 }
527 path1.close();
528 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
529}
530
reed@google.com53effc52011-09-21 19:05:12 +0000531static void test_flattening(skiatest::Reporter* reporter) {
532 SkPath p;
533
534 static const SkPoint pts[] = {
535 { 0, 0 },
536 { SkIntToScalar(10), SkIntToScalar(10) },
537 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
538 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
539 };
540 p.moveTo(pts[0]);
541 p.lineTo(pts[1]);
542 p.quadTo(pts[2], pts[3]);
543 p.cubicTo(pts[4], pts[5], pts[6]);
544
545 SkWriter32 writer(100);
546 p.flatten(writer);
547 size_t size = writer.size();
548 SkAutoMalloc storage(size);
549 writer.flatten(storage.get());
550 SkReader32 reader(storage.get(), size);
551
552 SkPath p1;
553 REPORTER_ASSERT(reporter, p1 != p);
554 p1.unflatten(reader);
555 REPORTER_ASSERT(reporter, p1 == p);
556}
557
558static void test_transform(skiatest::Reporter* reporter) {
559 SkPath p, p1;
560
561 static const SkPoint pts[] = {
562 { 0, 0 },
563 { SkIntToScalar(10), SkIntToScalar(10) },
564 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
565 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
566 };
567 p.moveTo(pts[0]);
568 p.lineTo(pts[1]);
569 p.quadTo(pts[2], pts[3]);
570 p.cubicTo(pts[4], pts[5], pts[6]);
571
572 SkMatrix matrix;
573 matrix.reset();
574 p.transform(matrix, &p1);
575 REPORTER_ASSERT(reporter, p == p1);
576
577 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
578 p.transform(matrix, &p1);
579 SkPoint pts1[7];
580 int count = p1.getPoints(pts1, 7);
581 REPORTER_ASSERT(reporter, 7 == count);
582 for (int i = 0; i < count; ++i) {
583 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
584 REPORTER_ASSERT(reporter, newPt == pts1[i]);
585 }
586}
587
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000588static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000589 SkPath p;
590 SkPoint pt;
591 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000592
593 // Lone moveTo case
594 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000595 REPORTER_ASSERT(reporter, !p.isEmpty());
596 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000597 p.getLastPt(&pt);
598 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
599 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
600 bounds.set(0, 0, 0, 0);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000601 REPORTER_ASSERT(reporter, bounds == p.getBounds());
602
603 // MoveTo-MoveTo case
604 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000605 REPORTER_ASSERT(reporter, !p.isEmpty());
606 REPORTER_ASSERT(reporter, 2 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000607 p.getLastPt(&pt);
608 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
609 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
610 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000611 REPORTER_ASSERT(reporter, bounds == p.getBounds());
612
613 // moveTo-close case
614 p.reset();
615 p.moveTo(SK_Scalar1, SK_Scalar1);
616 p.close();
617 bounds.set(0, 0, 0, 0);
618 REPORTER_ASSERT(reporter, !p.isEmpty());
619 REPORTER_ASSERT(reporter, 1 == p.countPoints());
620 REPORTER_ASSERT(reporter, bounds == p.getBounds());
621
622 // moveTo-close-moveTo-close case
623 p.moveTo(SK_Scalar1*2, SK_Scalar1);
624 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000625 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000626 REPORTER_ASSERT(reporter, !p.isEmpty());
627 REPORTER_ASSERT(reporter, 2 == p.countPoints());
628 REPORTER_ASSERT(reporter, bounds == p.getBounds());
629
630 // moveTo-line case
631 p.reset();
632 p.moveTo(SK_Scalar1, SK_Scalar1);
633 p.lineTo(SK_Scalar1, SK_Scalar1);
634 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
635 REPORTER_ASSERT(reporter, !p.isEmpty());
636 REPORTER_ASSERT(reporter, 2 == p.countPoints());
637 REPORTER_ASSERT(reporter, bounds == p.getBounds());
638
639 // moveTo-lineTo-moveTo-lineTo case
640 p.moveTo(SK_Scalar1*2, SK_Scalar1);
641 p.lineTo(SK_Scalar1*2, SK_Scalar1);
642 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
643 REPORTER_ASSERT(reporter, !p.isEmpty());
644 REPORTER_ASSERT(reporter, 4 == p.countPoints());
645 REPORTER_ASSERT(reporter, bounds == p.getBounds());
646
647 // moveTo-line-close case
648 p.reset();
649 p.moveTo(SK_Scalar1, SK_Scalar1);
650 p.lineTo(SK_Scalar1, SK_Scalar1);
651 p.close();
652 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
653 REPORTER_ASSERT(reporter, !p.isEmpty());
654 REPORTER_ASSERT(reporter, 2 == p.countPoints());
655 REPORTER_ASSERT(reporter, bounds == p.getBounds());
656
657 // moveTo-line-close-moveTo-line-close case
658 p.moveTo(SK_Scalar1*2, SK_Scalar1);
659 p.lineTo(SK_Scalar1*2, SK_Scalar1);
660 p.close();
661 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
662 REPORTER_ASSERT(reporter, !p.isEmpty());
663 REPORTER_ASSERT(reporter, 4 == p.countPoints());
664 REPORTER_ASSERT(reporter, bounds == p.getBounds());
665
666 // moveTo-quadTo case
667 p.reset();
668 p.moveTo(SK_Scalar1, SK_Scalar1);
669 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
670 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
671 REPORTER_ASSERT(reporter, !p.isEmpty());
672 REPORTER_ASSERT(reporter, 3 == p.countPoints());
673 REPORTER_ASSERT(reporter, bounds == p.getBounds());
674
675 // moveTo-quadTo-close case
676 p.close();
677 REPORTER_ASSERT(reporter, !p.isEmpty());
678 REPORTER_ASSERT(reporter, 3 == p.countPoints());
679 REPORTER_ASSERT(reporter, bounds == p.getBounds());
680
681 // moveTo-quadTo-moveTo-quadTo case
682 p.reset();
683 p.moveTo(SK_Scalar1, SK_Scalar1);
684 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
685 p.moveTo(SK_Scalar1*2, SK_Scalar1);
686 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
687 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
688 REPORTER_ASSERT(reporter, !p.isEmpty());
689 REPORTER_ASSERT(reporter, 6 == p.countPoints());
690 REPORTER_ASSERT(reporter, bounds == p.getBounds());
691
692 // moveTo-cubicTo case
693 p.reset();
694 p.moveTo(SK_Scalar1, SK_Scalar1);
695 p.cubicTo(SK_Scalar1, SK_Scalar1,
696 SK_Scalar1, SK_Scalar1,
697 SK_Scalar1, SK_Scalar1);
698 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
699 REPORTER_ASSERT(reporter, !p.isEmpty());
700 REPORTER_ASSERT(reporter, 4 == p.countPoints());
701 REPORTER_ASSERT(reporter, bounds == p.getBounds());
702
703 // moveTo-quadTo-close case
704 p.close();
705 REPORTER_ASSERT(reporter, !p.isEmpty());
706 REPORTER_ASSERT(reporter, 4 == p.countPoints());
707 REPORTER_ASSERT(reporter, bounds == p.getBounds());
708
709 // moveTo-quadTo-moveTo-quadTo case
710 p.reset();
711 p.moveTo(SK_Scalar1, SK_Scalar1);
712 p.cubicTo(SK_Scalar1, SK_Scalar1,
713 SK_Scalar1, SK_Scalar1,
714 SK_Scalar1, SK_Scalar1);
715 p.moveTo(SK_Scalar1*2, SK_Scalar1);
716 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
717 SK_Scalar1*2, SK_Scalar1,
718 SK_Scalar1*2, SK_Scalar1);
719 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
720 REPORTER_ASSERT(reporter, !p.isEmpty());
721 REPORTER_ASSERT(reporter, 8 == p.countPoints());
722 REPORTER_ASSERT(reporter, bounds == p.getBounds());
723}
724
725struct SegmentInfo {
726 SkPath fPath;
727 int fPointCount;
728};
729
reed@google.com10296cc2011-09-21 12:29:05 +0000730#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
731
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000732static void test_segment_masks(skiatest::Reporter* reporter) {
733 SkPath p;
734 p.moveTo(0, 0);
735 p.quadTo(100, 100, 200, 200);
736 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
737 REPORTER_ASSERT(reporter, !p.isEmpty());
738 p.cubicTo(100, 100, 200, 200, 300, 300);
739 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
740 REPORTER_ASSERT(reporter, !p.isEmpty());
741 p.reset();
742 p.moveTo(0, 0);
743 p.cubicTo(100, 100, 200, 200, 300, 300);
744 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
745 REPORTER_ASSERT(reporter, !p.isEmpty());
746}
747
748static void test_iter(skiatest::Reporter* reporter) {
749 SkPath p;
750 SkPoint pts[4];
751
752 // Test an iterator with no path
753 SkPath::Iter noPathIter;
754 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
755 // Test that setting an empty path works
756 noPathIter.setPath(p, false);
757 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
758 // Test that close path makes no difference for an empty path
759 noPathIter.setPath(p, true);
760 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
761
762 // Test an iterator with an initial empty path
763 SkPath::Iter iter(p, false);
764 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
765
766 // Test that close path makes no difference
767 SkPath::Iter forceCloseIter(p, true);
768 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
769
770 // Test that a move-only path produces nothing when iterated.
771 p.moveTo(SK_Scalar1, 0);
772 iter.setPath(p, false);
773 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
774
775 // No matter how many moves we add, we should still get nothing back.
776 p.moveTo(SK_Scalar1*2, 0);
777 p.moveTo(SK_Scalar1*3, 0);
778 p.moveTo(SK_Scalar1*4, 0);
779 p.moveTo(SK_Scalar1*5, 0);
780 iter.setPath(p, false);
781 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
782
783 // Nor should force closing
784 forceCloseIter.setPath(p, true);
785 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
786
787 // Initial closes should be ignored
788 p.reset();
789 p.close();
790 iter.setPath(p, false);
791 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
792 // Even if force closed
793 forceCloseIter.setPath(p, true);
794 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
795
796 // Move/close sequences should also be ignored
797 p.reset();
798 p.close();
799 p.moveTo(SK_Scalar1, 0);
800 p.close();
801 p.close();
802 p.moveTo(SK_Scalar1*2, 0);
803 p.close();
804 p.moveTo(SK_Scalar1*3, 0);
805 p.moveTo(SK_Scalar1*4, 0);
806 p.close();
807 iter.setPath(p, false);
808 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
809 // Even if force closed
810 forceCloseIter.setPath(p, true);
811 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
812
813 // The GM degeneratesegments.cpp test is more extensive
814}
815
816static void test_raw_iter(skiatest::Reporter* reporter) {
817 SkPath p;
818 SkPoint pts[4];
819
820 // Test an iterator with no path
821 SkPath::RawIter noPathIter;
822 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
823 // Test that setting an empty path works
824 noPathIter.setPath(p);
825 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
826
827 // Test an iterator with an initial empty path
828 SkPath::RawIter iter(p);
829 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
830
831 // Test that a move-only path returns the move.
832 p.moveTo(SK_Scalar1, 0);
833 iter.setPath(p);
834 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
835 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
836 REPORTER_ASSERT(reporter, pts[0].fY == 0);
837 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
838
839 // No matter how many moves we add, we should get them all back
840 p.moveTo(SK_Scalar1*2, SK_Scalar1);
841 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
842 iter.setPath(p);
843 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
844 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
845 REPORTER_ASSERT(reporter, pts[0].fY == 0);
846 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
847 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
848 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
849 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
850 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
851 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
852 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
853
854 // Initial close is never ever stored
855 p.reset();
856 p.close();
857 iter.setPath(p);
858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
859
860 // Move/close sequences
861 p.reset();
862 p.close(); // Not stored, no purpose
863 p.moveTo(SK_Scalar1, 0);
864 p.close();
865 p.close(); // Not stored, no purpose
866 p.moveTo(SK_Scalar1*2, SK_Scalar1);
867 p.close();
868 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
869 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
870 p.close();
871 iter.setPath(p);
872 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
873 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
874 REPORTER_ASSERT(reporter, pts[0].fY == 0);
875 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
876 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
877 REPORTER_ASSERT(reporter, pts[0].fY == 0);
878 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
879 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
880 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
881 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
882 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
883 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
884 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
885 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
886 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
887 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
888 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
889 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
890 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
891 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
892 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
893 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
894
895 // Generate random paths and verify
896 SkPoint randomPts[25];
897 for (int i = 0; i < 5; ++i) {
898 for (int j = 0; j < 5; ++j) {
899 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
900 }
901 }
902
903 // Max of 10 segments, max 3 points per segment
904 SkRandom rand(9876543);
905 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +0000906 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000907 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +0000908
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000909 for (int i = 0; i < 500; ++i) {
910 p.reset();
911 bool lastWasClose = true;
912 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +0000913 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000914 int numPoints = 0;
915 int numVerbs = (rand.nextU() >> 16) % 10;
916 int numIterVerbs = 0;
917 for (int j = 0; j < numVerbs; ++j) {
918 do {
919 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
920 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
921 int numRequiredPts;
922 switch (nextVerb) {
923 case SkPath::kMove_Verb:
924 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
925 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +0000926 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000927 numPoints += 1;
928 lastWasClose = false;
929 haveMoveTo = true;
930 break;
931 case SkPath::kLine_Verb:
932 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000933 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000934 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
935 haveMoveTo = true;
936 }
937 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
938 p.lineTo(expectedPts[numPoints]);
939 numPoints += 1;
940 lastWasClose = false;
941 break;
942 case SkPath::kQuad_Verb:
943 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000944 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000945 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
946 haveMoveTo = true;
947 }
948 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
949 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
950 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
951 numPoints += 2;
952 lastWasClose = false;
953 break;
954 case SkPath::kCubic_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 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
962 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
963 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
964 expectedPts[numPoints + 2]);
965 numPoints += 3;
966 lastWasClose = false;
967 break;
968 case SkPath::kClose_Verb:
969 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +0000970 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000971 lastWasClose = true;
972 break;
973 default:;
974 }
975 expectedVerbs[numIterVerbs++] = nextVerb;
976 }
977
978 iter.setPath(p);
979 numVerbs = numIterVerbs;
980 numIterVerbs = 0;
981 int numIterPts = 0;
982 SkPoint lastMoveTo;
983 SkPoint lastPt;
984 lastMoveTo.set(0, 0);
985 lastPt.set(0, 0);
986 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
987 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
988 numIterVerbs++;
989 switch (nextVerb) {
990 case SkPath::kMove_Verb:
991 REPORTER_ASSERT(reporter, numIterPts < numPoints);
992 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
993 lastPt = lastMoveTo = pts[0];
994 numIterPts += 1;
995 break;
996 case SkPath::kLine_Verb:
997 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
998 REPORTER_ASSERT(reporter, pts[0] == lastPt);
999 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1000 lastPt = pts[1];
1001 numIterPts += 1;
1002 break;
1003 case SkPath::kQuad_Verb:
1004 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1005 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1006 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1007 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1008 lastPt = pts[2];
1009 numIterPts += 2;
1010 break;
1011 case SkPath::kCubic_Verb:
1012 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1013 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1014 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1015 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1016 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1017 lastPt = pts[3];
1018 numIterPts += 3;
1019 break;
1020 case SkPath::kClose_Verb:
1021 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1022 lastPt = lastMoveTo;
1023 break;
1024 default:;
1025 }
1026 }
1027 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1028 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1029 }
1030}
1031
reed@google.com04863fa2011-05-15 04:08:24 +00001032void TestPath(skiatest::Reporter* reporter);
1033void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001034 {
1035 SkSize size;
1036 size.fWidth = 3.4f;
1037 size.width();
1038 size = SkSize::Make(3,4);
1039 SkISize isize = SkISize::Make(3,4);
1040 }
1041
1042 SkTSize<SkScalar>::Make(3,4);
1043
reed@android.com3abec1d2009-03-02 05:36:20 +00001044 SkPath p, p2;
1045 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001046
reed@android.com3abec1d2009-03-02 05:36:20 +00001047 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001048 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +00001049 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001050 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001051 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1052 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1053 REPORTER_ASSERT(reporter, p == p2);
1054 REPORTER_ASSERT(reporter, !(p != p2));
1055
reed@android.comd252db02009-04-01 18:31:44 +00001056 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001057
reed@android.com3abec1d2009-03-02 05:36:20 +00001058 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001059
reed@android.com6b82d1a2009-06-03 02:35:01 +00001060 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1061 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001062 // we have quads or cubics
1063 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001064 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001065
reed@android.com6b82d1a2009-06-03 02:35:01 +00001066 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001067 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001068 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001069
reed@android.com6b82d1a2009-06-03 02:35:01 +00001070 p.addOval(bounds);
1071 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001072 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001073
reed@android.com6b82d1a2009-06-03 02:35:01 +00001074 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001075 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001076 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001077 // we have only lines
1078 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001079 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001080
1081 REPORTER_ASSERT(reporter, p != p2);
1082 REPORTER_ASSERT(reporter, !(p == p2));
1083
1084 // does getPoints return the right result
1085 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1086 SkPoint pts[4];
1087 int count = p.getPoints(pts, 4);
1088 REPORTER_ASSERT(reporter, count == 4);
1089 bounds2.set(pts, 4);
1090 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001091
reed@android.com3abec1d2009-03-02 05:36:20 +00001092 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1093 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001094 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001095
reed@android.com3abec1d2009-03-02 05:36:20 +00001096 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001097 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001098 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1099 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001100
reed@android.com3abec1d2009-03-02 05:36:20 +00001101 // now force p to not be a rect
1102 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1103 p.addRect(bounds);
1104 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001105 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001106
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001107 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001108 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001109 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001110 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001111 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001112 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001113 test_flattening(reporter);
1114 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001115 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001116 test_iter(reporter);
1117 test_raw_iter(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001118}
1119
1120#include "TestClassDef.h"
1121DEFINE_TESTCLASS("Path", PathTestClass, TestPath)