blob: 754aa2b9e6e0df97db4ed050731c28ac22c44704 [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[] = {
41 "M 10 10 L 10 10 L 20 10 Q 20 20 30 30",
42 "M 10 10 C 20 10 20 20 20 20",
43 };
44 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
45 path.reset();
46 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
47 REPORTER_ASSERT(reporter, valid);
48 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
49 }
50
51 static const char* gCCW[] = {
52 "M 10 10 L 10 10 L 20 10 Q 20 -20 30 -30",
53 "M 10 10 C 20 10 20 -20 20 -20",
54 };
55 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
56 path.reset();
57 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
58 REPORTER_ASSERT(reporter, valid);
59 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
60 }
61}
62
reed@google.comffdb0182011-11-14 19:29:14 +000063static void add_rect(SkPath* path, const SkRect& r) {
64 path->moveTo(r.fLeft, r.fTop);
65 path->lineTo(r.fRight, r.fTop);
66 path->lineTo(r.fRight, r.fBottom);
67 path->lineTo(r.fLeft, r.fBottom);
68 path->close();
69}
70
71static void test_bounds(skiatest::Reporter* reporter) {
72 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +000073 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
74 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
75 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
76 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +000077 };
78
79 SkPath path0, path1;
80 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
81 path0.addRect(rects[i]);
82 add_rect(&path1, rects[i]);
83 }
84
85 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
86}
87
reed@google.com55b5f4b2011-09-07 12:23:41 +000088static void stroke_cubic(const SkPoint pts[4]) {
89 SkPath path;
90 path.moveTo(pts[0]);
91 path.cubicTo(pts[1], pts[2], pts[3]);
92
93 SkPaint paint;
94 paint.setStyle(SkPaint::kStroke_Style);
95 paint.setStrokeWidth(SK_Scalar1 * 2);
96
97 SkPath fill;
98 paint.getFillPath(path, &fill);
99}
100
101// just ensure this can run w/o any SkASSERTS firing in the debug build
102// we used to assert due to differences in how we determine a degenerate vector
103// but that was fixed with the introduction of SkPoint::CanNormalize
104static void stroke_tiny_cubic() {
105 SkPoint p0[] = {
106 { 372.0f, 92.0f },
107 { 372.0f, 92.0f },
108 { 372.0f, 92.0f },
109 { 372.0f, 92.0f },
110 };
111
112 stroke_cubic(p0);
113
114 SkPoint p1[] = {
115 { 372.0f, 92.0f },
116 { 372.0007f, 92.000755f },
117 { 371.99927f, 92.003922f },
118 { 371.99826f, 92.003899f },
119 };
120
121 stroke_cubic(p1);
122}
123
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000124static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
125 for (int i = 0; i < 2; ++i) {
126 SkPath::Iter iter(path, (bool)i);
127 SkPoint mv;
128 SkPoint pts[4];
129 SkPath::Verb v;
130 int nMT = 0;
131 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000132 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000133 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
134 switch (v) {
135 case SkPath::kMove_Verb:
136 mv = pts[0];
137 ++nMT;
138 break;
139 case SkPath::kClose_Verb:
140 REPORTER_ASSERT(reporter, mv == pts[0]);
141 ++nCL;
142 break;
143 default:
144 break;
145 }
146 }
147 // if we force a close on the interator we should have a close
148 // for every moveTo
149 REPORTER_ASSERT(reporter, !i || nMT == nCL);
150 }
151}
152
153static void test_close(skiatest::Reporter* reporter) {
154 SkPath closePt;
155 closePt.moveTo(0, 0);
156 closePt.close();
157 check_close(reporter, closePt);
158
159 SkPath openPt;
160 openPt.moveTo(0, 0);
161 check_close(reporter, openPt);
162
163 SkPath empty;
164 check_close(reporter, empty);
165 empty.close();
166 check_close(reporter, empty);
167
168 SkPath rect;
169 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
170 check_close(reporter, rect);
171 rect.close();
172 check_close(reporter, rect);
173
174 SkPath quad;
175 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
176 check_close(reporter, quad);
177 quad.close();
178 check_close(reporter, quad);
179
180 SkPath cubic;
181 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
182 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
183 check_close(reporter, cubic);
184 cubic.close();
185 check_close(reporter, cubic);
186
187 SkPath line;
188 line.moveTo(SK_Scalar1, SK_Scalar1);
189 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
190 check_close(reporter, line);
191 line.close();
192 check_close(reporter, line);
193
194 SkPath rect2;
195 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
196 rect2.close();
197 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
198 check_close(reporter, rect2);
199 rect2.close();
200 check_close(reporter, rect2);
201
202 SkPath oval3;
203 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
204 oval3.close();
205 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
206 check_close(reporter, oval3);
207 oval3.close();
208 check_close(reporter, oval3);
209
210 SkPath moves;
211 moves.moveTo(SK_Scalar1, SK_Scalar1);
212 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
213 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
214 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
215 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000216
217 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000218}
219
reed@google.com7c424812011-05-15 04:38:34 +0000220static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
221 SkPath::Convexity expected) {
222 SkPath::Convexity c = SkPath::ComputeConvexity(path);
223 REPORTER_ASSERT(reporter, c == expected);
224}
225
226static void test_convexity2(skiatest::Reporter* reporter) {
227 SkPath pt;
228 pt.moveTo(0, 0);
229 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000230 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000231
232 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000233 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
234 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000235 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000236 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000237
238 SkPath triLeft;
239 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000240 triLeft.lineTo(SK_Scalar1, 0);
241 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000242 triLeft.close();
243 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
244
245 SkPath triRight;
246 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000247 triRight.lineTo(-SK_Scalar1, 0);
248 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000249 triRight.close();
250 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
251
252 SkPath square;
253 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000254 square.lineTo(SK_Scalar1, 0);
255 square.lineTo(SK_Scalar1, SK_Scalar1);
256 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000257 square.close();
258 check_convexity(reporter, square, SkPath::kConvex_Convexity);
259
260 SkPath redundantSquare;
261 redundantSquare.moveTo(0, 0);
262 redundantSquare.lineTo(0, 0);
263 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000264 redundantSquare.lineTo(SK_Scalar1, 0);
265 redundantSquare.lineTo(SK_Scalar1, 0);
266 redundantSquare.lineTo(SK_Scalar1, 0);
267 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
268 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
269 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
270 redundantSquare.lineTo(0, SK_Scalar1);
271 redundantSquare.lineTo(0, SK_Scalar1);
272 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000273 redundantSquare.close();
274 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
275
276 SkPath bowTie;
277 bowTie.moveTo(0, 0);
278 bowTie.lineTo(0, 0);
279 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000280 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
281 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
282 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
283 bowTie.lineTo(SK_Scalar1, 0);
284 bowTie.lineTo(SK_Scalar1, 0);
285 bowTie.lineTo(SK_Scalar1, 0);
286 bowTie.lineTo(0, SK_Scalar1);
287 bowTie.lineTo(0, SK_Scalar1);
288 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000289 bowTie.close();
290 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
291
292 SkPath spiral;
293 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000294 spiral.lineTo(100*SK_Scalar1, 0);
295 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
296 spiral.lineTo(0, 100*SK_Scalar1);
297 spiral.lineTo(0, 50*SK_Scalar1);
298 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
299 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000300 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000301 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000302
303 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000304 dent.moveTo(0, 0);
305 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
306 dent.lineTo(0, 100*SK_Scalar1);
307 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
308 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000309 dent.close();
310 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
311}
312
reed@android.com6b82d1a2009-06-03 02:35:01 +0000313static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
314 const SkRect& bounds) {
315 REPORTER_ASSERT(reporter, p.isConvex());
316 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000317
reed@android.com6b82d1a2009-06-03 02:35:01 +0000318 SkPath p2(p);
319 REPORTER_ASSERT(reporter, p2.isConvex());
320 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
321
322 SkPath other;
323 other.swap(p2);
324 REPORTER_ASSERT(reporter, other.isConvex());
325 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
326}
327
reed@google.com04863fa2011-05-15 04:08:24 +0000328static void setFromString(SkPath* path, const char str[]) {
329 bool first = true;
330 while (str) {
331 SkScalar x, y;
332 str = SkParse::FindScalar(str, &x);
333 if (NULL == str) {
334 break;
335 }
336 str = SkParse::FindScalar(str, &y);
337 SkASSERT(str);
338 if (first) {
339 path->moveTo(x, y);
340 first = false;
341 } else {
342 path->lineTo(x, y);
343 }
344 }
345}
346
347static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000348 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
349 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
350
351 SkPath path;
352
reed@google.comb54455e2011-05-16 14:16:04 +0000353 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000354 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000355 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000356 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000357 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
358 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000359 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000360 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000361 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000362 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000363 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000364 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000365 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000366
367 static const struct {
368 const char* fPathStr;
369 SkPath::Convexity fExpectedConvexity;
370 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000371 { "", SkPath::kConvex_Convexity },
372 { "0 0", SkPath::kConvex_Convexity },
373 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000374 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000375 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
376 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
377 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
378 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
379 };
380
381 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
382 SkPath path;
383 setFromString(&path, gRec[i].fPathStr);
384 SkPath::Convexity c = SkPath::ComputeConvexity(path);
385 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
386 }
387}
388
caryclark@google.comf1316942011-07-26 19:54:45 +0000389// Simple isRect test is inline TestPath, below.
390// test_isRect provides more extensive testing.
391static void test_isRect(skiatest::Reporter* reporter) {
392 // passing tests (all moveTo / lineTo...
393 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
394 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
395 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
396 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
397 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
398 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
399 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
400 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
401 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
402 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
403 {1, 0}, {.5f, 0}};
404 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
405 {0, 1}, {0, .5f}};
406 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
407 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
408 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
409
410 // failing tests
411 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
412 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
413 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
414 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
415 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
416 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
417 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
418 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
419
420 // failing, no close
421 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
422 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
423
424 size_t testLen[] = {
425 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
426 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
427 sizeof(rd), sizeof(re),
428 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
429 sizeof(f7), sizeof(f8),
430 sizeof(c1), sizeof(c2)
431 };
432 SkPoint* tests[] = {
433 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
434 f1, f2, f3, f4, f5, f6, f7, f8,
435 c1, c2
436 };
437 SkPoint* lastPass = re;
438 SkPoint* lastClose = f8;
439 bool fail = false;
440 bool close = true;
441 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
442 size_t index;
443 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
444 SkPath path;
445 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
446 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
447 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
448 }
449 if (close) {
450 path.close();
451 }
452 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
453 if (tests[testIndex] == lastPass) {
454 fail = true;
455 }
456 if (tests[testIndex] == lastClose) {
457 close = false;
458 }
459 }
460
461 // fail, close then line
462 SkPath path1;
463 path1.moveTo(r1[0].fX, r1[0].fY);
464 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
465 path1.lineTo(r1[index].fX, r1[index].fY);
466 }
467 path1.close();
468 path1.lineTo(1, 0);
469 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
470
471 // fail, move in the middle
472 path1.reset();
473 path1.moveTo(r1[0].fX, r1[0].fY);
474 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
475 if (index == 2) {
476 path1.moveTo(1, .5f);
477 }
478 path1.lineTo(r1[index].fX, r1[index].fY);
479 }
480 path1.close();
481 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
482
483 // fail, move on the edge
484 path1.reset();
485 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
486 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
487 path1.lineTo(r1[index].fX, r1[index].fY);
488 }
489 path1.close();
490 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
491
492 // fail, quad
493 path1.reset();
494 path1.moveTo(r1[0].fX, r1[0].fY);
495 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
496 if (index == 2) {
497 path1.quadTo(1, .5f, 1, .5f);
498 }
499 path1.lineTo(r1[index].fX, r1[index].fY);
500 }
501 path1.close();
502 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
503
504 // fail, cubic
505 path1.reset();
506 path1.moveTo(r1[0].fX, r1[0].fY);
507 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
508 if (index == 2) {
509 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
510 }
511 path1.lineTo(r1[index].fX, r1[index].fY);
512 }
513 path1.close();
514 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
515}
516
reed@google.com53effc52011-09-21 19:05:12 +0000517static void test_flattening(skiatest::Reporter* reporter) {
518 SkPath p;
519
520 static const SkPoint pts[] = {
521 { 0, 0 },
522 { SkIntToScalar(10), SkIntToScalar(10) },
523 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
524 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
525 };
526 p.moveTo(pts[0]);
527 p.lineTo(pts[1]);
528 p.quadTo(pts[2], pts[3]);
529 p.cubicTo(pts[4], pts[5], pts[6]);
530
531 SkWriter32 writer(100);
532 p.flatten(writer);
533 size_t size = writer.size();
534 SkAutoMalloc storage(size);
535 writer.flatten(storage.get());
536 SkReader32 reader(storage.get(), size);
537
538 SkPath p1;
539 REPORTER_ASSERT(reporter, p1 != p);
540 p1.unflatten(reader);
541 REPORTER_ASSERT(reporter, p1 == p);
542}
543
544static void test_transform(skiatest::Reporter* reporter) {
545 SkPath p, p1;
546
547 static const SkPoint pts[] = {
548 { 0, 0 },
549 { SkIntToScalar(10), SkIntToScalar(10) },
550 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
551 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
552 };
553 p.moveTo(pts[0]);
554 p.lineTo(pts[1]);
555 p.quadTo(pts[2], pts[3]);
556 p.cubicTo(pts[4], pts[5], pts[6]);
557
558 SkMatrix matrix;
559 matrix.reset();
560 p.transform(matrix, &p1);
561 REPORTER_ASSERT(reporter, p == p1);
562
563 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
564 p.transform(matrix, &p1);
565 SkPoint pts1[7];
566 int count = p1.getPoints(pts1, 7);
567 REPORTER_ASSERT(reporter, 7 == count);
568 for (int i = 0; i < count; ++i) {
569 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
570 REPORTER_ASSERT(reporter, newPt == pts1[i]);
571 }
572}
573
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000574static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000575 SkPath p;
576 SkPoint pt;
577 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000578
579 // Lone moveTo case
580 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000581 REPORTER_ASSERT(reporter, !p.isEmpty());
582 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000583 p.getLastPt(&pt);
584 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
585 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
586 bounds.set(0, 0, 0, 0);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000587 REPORTER_ASSERT(reporter, bounds == p.getBounds());
588
589 // MoveTo-MoveTo case
590 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000591 REPORTER_ASSERT(reporter, !p.isEmpty());
592 REPORTER_ASSERT(reporter, 2 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000593 p.getLastPt(&pt);
594 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
595 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
596 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000597 REPORTER_ASSERT(reporter, bounds == p.getBounds());
598
599 // moveTo-close case
600 p.reset();
601 p.moveTo(SK_Scalar1, SK_Scalar1);
602 p.close();
603 bounds.set(0, 0, 0, 0);
604 REPORTER_ASSERT(reporter, !p.isEmpty());
605 REPORTER_ASSERT(reporter, 1 == p.countPoints());
606 REPORTER_ASSERT(reporter, bounds == p.getBounds());
607
608 // moveTo-close-moveTo-close case
609 p.moveTo(SK_Scalar1*2, SK_Scalar1);
610 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000611 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000612 REPORTER_ASSERT(reporter, !p.isEmpty());
613 REPORTER_ASSERT(reporter, 2 == p.countPoints());
614 REPORTER_ASSERT(reporter, bounds == p.getBounds());
615
616 // moveTo-line case
617 p.reset();
618 p.moveTo(SK_Scalar1, SK_Scalar1);
619 p.lineTo(SK_Scalar1, SK_Scalar1);
620 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
621 REPORTER_ASSERT(reporter, !p.isEmpty());
622 REPORTER_ASSERT(reporter, 2 == p.countPoints());
623 REPORTER_ASSERT(reporter, bounds == p.getBounds());
624
625 // moveTo-lineTo-moveTo-lineTo case
626 p.moveTo(SK_Scalar1*2, SK_Scalar1);
627 p.lineTo(SK_Scalar1*2, SK_Scalar1);
628 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
629 REPORTER_ASSERT(reporter, !p.isEmpty());
630 REPORTER_ASSERT(reporter, 4 == p.countPoints());
631 REPORTER_ASSERT(reporter, bounds == p.getBounds());
632
633 // moveTo-line-close case
634 p.reset();
635 p.moveTo(SK_Scalar1, SK_Scalar1);
636 p.lineTo(SK_Scalar1, SK_Scalar1);
637 p.close();
638 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
639 REPORTER_ASSERT(reporter, !p.isEmpty());
640 REPORTER_ASSERT(reporter, 2 == p.countPoints());
641 REPORTER_ASSERT(reporter, bounds == p.getBounds());
642
643 // moveTo-line-close-moveTo-line-close case
644 p.moveTo(SK_Scalar1*2, SK_Scalar1);
645 p.lineTo(SK_Scalar1*2, SK_Scalar1);
646 p.close();
647 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
648 REPORTER_ASSERT(reporter, !p.isEmpty());
649 REPORTER_ASSERT(reporter, 4 == p.countPoints());
650 REPORTER_ASSERT(reporter, bounds == p.getBounds());
651
652 // moveTo-quadTo case
653 p.reset();
654 p.moveTo(SK_Scalar1, SK_Scalar1);
655 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
656 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
657 REPORTER_ASSERT(reporter, !p.isEmpty());
658 REPORTER_ASSERT(reporter, 3 == p.countPoints());
659 REPORTER_ASSERT(reporter, bounds == p.getBounds());
660
661 // moveTo-quadTo-close case
662 p.close();
663 REPORTER_ASSERT(reporter, !p.isEmpty());
664 REPORTER_ASSERT(reporter, 3 == p.countPoints());
665 REPORTER_ASSERT(reporter, bounds == p.getBounds());
666
667 // moveTo-quadTo-moveTo-quadTo case
668 p.reset();
669 p.moveTo(SK_Scalar1, SK_Scalar1);
670 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
671 p.moveTo(SK_Scalar1*2, SK_Scalar1);
672 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
673 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
674 REPORTER_ASSERT(reporter, !p.isEmpty());
675 REPORTER_ASSERT(reporter, 6 == p.countPoints());
676 REPORTER_ASSERT(reporter, bounds == p.getBounds());
677
678 // moveTo-cubicTo case
679 p.reset();
680 p.moveTo(SK_Scalar1, SK_Scalar1);
681 p.cubicTo(SK_Scalar1, SK_Scalar1,
682 SK_Scalar1, SK_Scalar1,
683 SK_Scalar1, SK_Scalar1);
684 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, 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-close case
690 p.close();
691 REPORTER_ASSERT(reporter, !p.isEmpty());
692 REPORTER_ASSERT(reporter, 4 == p.countPoints());
693 REPORTER_ASSERT(reporter, bounds == p.getBounds());
694
695 // moveTo-quadTo-moveTo-quadTo case
696 p.reset();
697 p.moveTo(SK_Scalar1, SK_Scalar1);
698 p.cubicTo(SK_Scalar1, SK_Scalar1,
699 SK_Scalar1, SK_Scalar1,
700 SK_Scalar1, SK_Scalar1);
701 p.moveTo(SK_Scalar1*2, SK_Scalar1);
702 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
703 SK_Scalar1*2, SK_Scalar1,
704 SK_Scalar1*2, SK_Scalar1);
705 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
706 REPORTER_ASSERT(reporter, !p.isEmpty());
707 REPORTER_ASSERT(reporter, 8 == p.countPoints());
708 REPORTER_ASSERT(reporter, bounds == p.getBounds());
709}
710
711struct SegmentInfo {
712 SkPath fPath;
713 int fPointCount;
714};
715
reed@google.com10296cc2011-09-21 12:29:05 +0000716#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
717
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000718static void test_segment_masks(skiatest::Reporter* reporter) {
719 SkPath p;
720 p.moveTo(0, 0);
721 p.quadTo(100, 100, 200, 200);
722 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
723 REPORTER_ASSERT(reporter, !p.isEmpty());
724 p.cubicTo(100, 100, 200, 200, 300, 300);
725 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
726 REPORTER_ASSERT(reporter, !p.isEmpty());
727 p.reset();
728 p.moveTo(0, 0);
729 p.cubicTo(100, 100, 200, 200, 300, 300);
730 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
731 REPORTER_ASSERT(reporter, !p.isEmpty());
732}
733
734static void test_iter(skiatest::Reporter* reporter) {
735 SkPath p;
736 SkPoint pts[4];
737
738 // Test an iterator with no path
739 SkPath::Iter noPathIter;
740 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
741 // Test that setting an empty path works
742 noPathIter.setPath(p, false);
743 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
744 // Test that close path makes no difference for an empty path
745 noPathIter.setPath(p, true);
746 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
747
748 // Test an iterator with an initial empty path
749 SkPath::Iter iter(p, false);
750 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
751
752 // Test that close path makes no difference
753 SkPath::Iter forceCloseIter(p, true);
754 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
755
756 // Test that a move-only path produces nothing when iterated.
757 p.moveTo(SK_Scalar1, 0);
758 iter.setPath(p, false);
759 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
760
761 // No matter how many moves we add, we should still get nothing back.
762 p.moveTo(SK_Scalar1*2, 0);
763 p.moveTo(SK_Scalar1*3, 0);
764 p.moveTo(SK_Scalar1*4, 0);
765 p.moveTo(SK_Scalar1*5, 0);
766 iter.setPath(p, false);
767 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
768
769 // Nor should force closing
770 forceCloseIter.setPath(p, true);
771 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
772
773 // Initial closes should be ignored
774 p.reset();
775 p.close();
776 iter.setPath(p, false);
777 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
778 // Even if force closed
779 forceCloseIter.setPath(p, true);
780 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
781
782 // Move/close sequences should also be ignored
783 p.reset();
784 p.close();
785 p.moveTo(SK_Scalar1, 0);
786 p.close();
787 p.close();
788 p.moveTo(SK_Scalar1*2, 0);
789 p.close();
790 p.moveTo(SK_Scalar1*3, 0);
791 p.moveTo(SK_Scalar1*4, 0);
792 p.close();
793 iter.setPath(p, false);
794 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
795 // Even if force closed
796 forceCloseIter.setPath(p, true);
797 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
798
799 // The GM degeneratesegments.cpp test is more extensive
800}
801
802static void test_raw_iter(skiatest::Reporter* reporter) {
803 SkPath p;
804 SkPoint pts[4];
805
806 // Test an iterator with no path
807 SkPath::RawIter noPathIter;
808 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
809 // Test that setting an empty path works
810 noPathIter.setPath(p);
811 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
812
813 // Test an iterator with an initial empty path
814 SkPath::RawIter iter(p);
815 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
816
817 // Test that a move-only path returns the move.
818 p.moveTo(SK_Scalar1, 0);
819 iter.setPath(p);
820 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
821 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
822 REPORTER_ASSERT(reporter, pts[0].fY == 0);
823 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
824
825 // No matter how many moves we add, we should get them all back
826 p.moveTo(SK_Scalar1*2, SK_Scalar1);
827 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
828 iter.setPath(p);
829 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
830 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
831 REPORTER_ASSERT(reporter, pts[0].fY == 0);
832 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
833 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
834 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
835 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
836 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
837 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
838 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
839
840 // Initial close is never ever stored
841 p.reset();
842 p.close();
843 iter.setPath(p);
844 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
845
846 // Move/close sequences
847 p.reset();
848 p.close(); // Not stored, no purpose
849 p.moveTo(SK_Scalar1, 0);
850 p.close();
851 p.close(); // Not stored, no purpose
852 p.moveTo(SK_Scalar1*2, SK_Scalar1);
853 p.close();
854 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
855 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
856 p.close();
857 iter.setPath(p);
858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
859 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
860 REPORTER_ASSERT(reporter, pts[0].fY == 0);
861 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
862 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
863 REPORTER_ASSERT(reporter, pts[0].fY == 0);
864 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
865 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
866 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
867 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
868 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
869 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
870 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
871 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
872 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
873 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
874 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
875 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
876 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
877 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
878 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
879 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
880
881 // Generate random paths and verify
882 SkPoint randomPts[25];
883 for (int i = 0; i < 5; ++i) {
884 for (int j = 0; j < 5; ++j) {
885 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
886 }
887 }
888
889 // Max of 10 segments, max 3 points per segment
890 SkRandom rand(9876543);
891 SkPoint expectedPts[31]; // May have leading moveTo
892 SkPath::Verb expectedVerbs[11]; // May have leading moveTo
893 SkPath::Verb nextVerb;
894 for (int i = 0; i < 500; ++i) {
895 p.reset();
896 bool lastWasClose = true;
897 bool haveMoveTo = false;
898 int numPoints = 0;
899 int numVerbs = (rand.nextU() >> 16) % 10;
900 int numIterVerbs = 0;
901 for (int j = 0; j < numVerbs; ++j) {
902 do {
903 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
904 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
905 int numRequiredPts;
906 switch (nextVerb) {
907 case SkPath::kMove_Verb:
908 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
909 p.moveTo(expectedPts[numPoints]);
910 numPoints += 1;
911 lastWasClose = false;
912 haveMoveTo = true;
913 break;
914 case SkPath::kLine_Verb:
915 if (!haveMoveTo) {
916 expectedPts[numPoints++].set(0, 0);
917 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
918 haveMoveTo = true;
919 }
920 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
921 p.lineTo(expectedPts[numPoints]);
922 numPoints += 1;
923 lastWasClose = false;
924 break;
925 case SkPath::kQuad_Verb:
926 if (!haveMoveTo) {
927 expectedPts[numPoints++].set(0, 0);
928 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
929 haveMoveTo = true;
930 }
931 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
932 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
933 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
934 numPoints += 2;
935 lastWasClose = false;
936 break;
937 case SkPath::kCubic_Verb:
938 if (!haveMoveTo) {
939 expectedPts[numPoints++].set(0, 0);
940 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
941 haveMoveTo = true;
942 }
943 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
944 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
945 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
946 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
947 expectedPts[numPoints + 2]);
948 numPoints += 3;
949 lastWasClose = false;
950 break;
951 case SkPath::kClose_Verb:
952 p.close();
953 lastWasClose = true;
954 break;
955 default:;
956 }
957 expectedVerbs[numIterVerbs++] = nextVerb;
958 }
959
960 iter.setPath(p);
961 numVerbs = numIterVerbs;
962 numIterVerbs = 0;
963 int numIterPts = 0;
964 SkPoint lastMoveTo;
965 SkPoint lastPt;
966 lastMoveTo.set(0, 0);
967 lastPt.set(0, 0);
968 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
969 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
970 numIterVerbs++;
971 switch (nextVerb) {
972 case SkPath::kMove_Verb:
973 REPORTER_ASSERT(reporter, numIterPts < numPoints);
974 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
975 lastPt = lastMoveTo = pts[0];
976 numIterPts += 1;
977 break;
978 case SkPath::kLine_Verb:
979 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
980 REPORTER_ASSERT(reporter, pts[0] == lastPt);
981 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
982 lastPt = pts[1];
983 numIterPts += 1;
984 break;
985 case SkPath::kQuad_Verb:
986 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
987 REPORTER_ASSERT(reporter, pts[0] == lastPt);
988 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
989 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
990 lastPt = pts[2];
991 numIterPts += 2;
992 break;
993 case SkPath::kCubic_Verb:
994 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
995 REPORTER_ASSERT(reporter, pts[0] == lastPt);
996 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
997 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
998 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
999 lastPt = pts[3];
1000 numIterPts += 3;
1001 break;
1002 case SkPath::kClose_Verb:
1003 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1004 lastPt = lastMoveTo;
1005 break;
1006 default:;
1007 }
1008 }
1009 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1010 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1011 }
1012}
1013
reed@google.com04863fa2011-05-15 04:08:24 +00001014void TestPath(skiatest::Reporter* reporter);
1015void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001016 {
1017 SkSize size;
1018 size.fWidth = 3.4f;
1019 size.width();
1020 size = SkSize::Make(3,4);
1021 SkISize isize = SkISize::Make(3,4);
1022 }
1023
1024 SkTSize<SkScalar>::Make(3,4);
1025
reed@android.com3abec1d2009-03-02 05:36:20 +00001026 SkPath p, p2;
1027 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001028
reed@android.com3abec1d2009-03-02 05:36:20 +00001029 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001030 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +00001031 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001032 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001033 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1034 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1035 REPORTER_ASSERT(reporter, p == p2);
1036 REPORTER_ASSERT(reporter, !(p != p2));
1037
reed@android.comd252db02009-04-01 18:31:44 +00001038 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001039
reed@android.com3abec1d2009-03-02 05:36:20 +00001040 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001041
reed@android.com6b82d1a2009-06-03 02:35:01 +00001042 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1043 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001044 // we have quads or cubics
1045 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001046 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001047
reed@android.com6b82d1a2009-06-03 02:35:01 +00001048 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001049 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001050 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001051
reed@android.com6b82d1a2009-06-03 02:35:01 +00001052 p.addOval(bounds);
1053 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001054 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001055
reed@android.com6b82d1a2009-06-03 02:35:01 +00001056 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001057 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001058 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001059 // we have only lines
1060 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001061 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001062
1063 REPORTER_ASSERT(reporter, p != p2);
1064 REPORTER_ASSERT(reporter, !(p == p2));
1065
1066 // does getPoints return the right result
1067 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1068 SkPoint pts[4];
1069 int count = p.getPoints(pts, 4);
1070 REPORTER_ASSERT(reporter, count == 4);
1071 bounds2.set(pts, 4);
1072 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001073
reed@android.com3abec1d2009-03-02 05:36:20 +00001074 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1075 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001076 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001077
reed@android.com3abec1d2009-03-02 05:36:20 +00001078 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001079 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001080 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1081 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001082
reed@android.com3abec1d2009-03-02 05:36:20 +00001083 // now force p to not be a rect
1084 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1085 p.addRect(bounds);
1086 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001087 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001088
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001089 test_zero_length_paths(reporter);
reed@google.com3e71a882012-01-10 18:44:37 +00001090 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001091 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001092 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001093 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001094 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001095 test_flattening(reporter);
1096 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001097 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001098 test_iter(reporter);
1099 test_raw_iter(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001100}
1101
1102#include "TestClassDef.h"
1103DEFINE_TESTCLASS("Path", PathTestClass, TestPath)