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