1
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 */
8 #include "Test.h"
9 #include "SkPaint.h"
10 #include "SkPath.h"
11 #include "SkParse.h"
12 #include "SkParsePath.h"
13 #include "SkRandom.h"
14 #include "SkReader32.h"
15 #include "SkSize.h"
16 #include "SkWriter32.h"
17
18 /**
19 * cheapIsDirection can take a shortcut when a path is marked convex.
20 * This function ensures that we always test cheapIsDirection when the path
21 * is flagged with unknown convexity status.
22 */
check_direction(SkPath * path,SkPath::Direction expectedDir,skiatest::Reporter * reporter)23 static void check_direction(SkPath* path,
24 SkPath::Direction expectedDir,
25 skiatest::Reporter* reporter) {
26 if (SkPath::kConvex_Convexity == path->getConvexity()) {
27 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
28 path->setConvexity(SkPath::kUnknown_Convexity);
29 }
30 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
31 }
32
test_direction(skiatest::Reporter * reporter)33 static void test_direction(skiatest::Reporter* reporter) {
34 size_t i;
35 SkPath path;
36 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
37 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
38 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
39
40 static const char* gDegen[] = {
41 "M 10 10",
42 "M 10 10 M 20 20",
43 "M 10 10 L 20 20",
44 "M 10 10 L 10 10 L 10 10",
45 "M 10 10 Q 10 10 10 10",
46 "M 10 10 C 10 10 10 10 10 10",
47 };
48 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
49 path.reset();
50 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
51 REPORTER_ASSERT(reporter, valid);
52 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
53 }
54
55 static const char* gCW[] = {
56 "M 10 10 L 10 10 Q 20 10 20 20",
57 "M 10 10 C 20 10 20 20 20 20",
58 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
59 };
60 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
61 path.reset();
62 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
63 REPORTER_ASSERT(reporter, valid);
64 check_direction(&path, SkPath::kCW_Direction, reporter);
65 }
66
67 static const char* gCCW[] = {
68 "M 10 10 L 10 10 Q 20 10 20 -20",
69 "M 10 10 C 20 10 20 -20 20 -20",
70 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
71 };
72 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
73 path.reset();
74 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
75 REPORTER_ASSERT(reporter, valid);
76 check_direction(&path, SkPath::kCCW_Direction, reporter);
77 }
78
79 // Test two donuts, each wound a different direction. Only the outer contour
80 // determines the cheap direction
81 path.reset();
82 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
83 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
84 check_direction(&path, SkPath::kCW_Direction, reporter);
85
86 path.reset();
87 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
88 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
89 check_direction(&path, SkPath::kCCW_Direction, reporter);
90
91 #ifdef SK_SCALAR_IS_FLOAT
92 // triangle with one point really far from the origin.
93 path.reset();
94 // the first point is roughly 1.05e10, 1.05e10
95 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
96 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
97 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
98 check_direction(&path, SkPath::kCCW_Direction, reporter);
99 #endif
100 }
101
add_rect(SkPath * path,const SkRect & r)102 static void add_rect(SkPath* path, const SkRect& r) {
103 path->moveTo(r.fLeft, r.fTop);
104 path->lineTo(r.fRight, r.fTop);
105 path->lineTo(r.fRight, r.fBottom);
106 path->lineTo(r.fLeft, r.fBottom);
107 path->close();
108 }
109
test_bounds(skiatest::Reporter * reporter)110 static void test_bounds(skiatest::Reporter* reporter) {
111 static const SkRect rects[] = {
112 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
113 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
114 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
115 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
116 };
117
118 SkPath path0, path1;
119 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
120 path0.addRect(rects[i]);
121 add_rect(&path1, rects[i]);
122 }
123
124 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
125 }
126
stroke_cubic(const SkPoint pts[4])127 static void stroke_cubic(const SkPoint pts[4]) {
128 SkPath path;
129 path.moveTo(pts[0]);
130 path.cubicTo(pts[1], pts[2], pts[3]);
131
132 SkPaint paint;
133 paint.setStyle(SkPaint::kStroke_Style);
134 paint.setStrokeWidth(SK_Scalar1 * 2);
135
136 SkPath fill;
137 paint.getFillPath(path, &fill);
138 }
139
140 // just ensure this can run w/o any SkASSERTS firing in the debug build
141 // we used to assert due to differences in how we determine a degenerate vector
142 // but that was fixed with the introduction of SkPoint::CanNormalize
stroke_tiny_cubic()143 static void stroke_tiny_cubic() {
144 SkPoint p0[] = {
145 { 372.0f, 92.0f },
146 { 372.0f, 92.0f },
147 { 372.0f, 92.0f },
148 { 372.0f, 92.0f },
149 };
150
151 stroke_cubic(p0);
152
153 SkPoint p1[] = {
154 { 372.0f, 92.0f },
155 { 372.0007f, 92.000755f },
156 { 371.99927f, 92.003922f },
157 { 371.99826f, 92.003899f },
158 };
159
160 stroke_cubic(p1);
161 }
162
check_close(skiatest::Reporter * reporter,const SkPath & path)163 static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
164 for (int i = 0; i < 2; ++i) {
165 SkPath::Iter iter(path, (bool)i);
166 SkPoint mv;
167 SkPoint pts[4];
168 SkPath::Verb v;
169 int nMT = 0;
170 int nCL = 0;
171 mv.set(0, 0);
172 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
173 switch (v) {
174 case SkPath::kMove_Verb:
175 mv = pts[0];
176 ++nMT;
177 break;
178 case SkPath::kClose_Verb:
179 REPORTER_ASSERT(reporter, mv == pts[0]);
180 ++nCL;
181 break;
182 default:
183 break;
184 }
185 }
186 // if we force a close on the interator we should have a close
187 // for every moveTo
188 REPORTER_ASSERT(reporter, !i || nMT == nCL);
189 }
190 }
191
test_close(skiatest::Reporter * reporter)192 static void test_close(skiatest::Reporter* reporter) {
193 SkPath closePt;
194 closePt.moveTo(0, 0);
195 closePt.close();
196 check_close(reporter, closePt);
197
198 SkPath openPt;
199 openPt.moveTo(0, 0);
200 check_close(reporter, openPt);
201
202 SkPath empty;
203 check_close(reporter, empty);
204 empty.close();
205 check_close(reporter, empty);
206
207 SkPath rect;
208 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
209 check_close(reporter, rect);
210 rect.close();
211 check_close(reporter, rect);
212
213 SkPath quad;
214 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
215 check_close(reporter, quad);
216 quad.close();
217 check_close(reporter, quad);
218
219 SkPath cubic;
220 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
221 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
222 check_close(reporter, cubic);
223 cubic.close();
224 check_close(reporter, cubic);
225
226 SkPath line;
227 line.moveTo(SK_Scalar1, SK_Scalar1);
228 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
229 check_close(reporter, line);
230 line.close();
231 check_close(reporter, line);
232
233 SkPath rect2;
234 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
235 rect2.close();
236 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
237 check_close(reporter, rect2);
238 rect2.close();
239 check_close(reporter, rect2);
240
241 SkPath oval3;
242 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
243 oval3.close();
244 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
245 check_close(reporter, oval3);
246 oval3.close();
247 check_close(reporter, oval3);
248
249 SkPath moves;
250 moves.moveTo(SK_Scalar1, SK_Scalar1);
251 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
252 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
253 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
254 check_close(reporter, moves);
255
256 stroke_tiny_cubic();
257 }
258
check_convexity(skiatest::Reporter * reporter,const SkPath & path,SkPath::Convexity expected)259 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
260 SkPath::Convexity expected) {
261 SkPath::Convexity c = SkPath::ComputeConvexity(path);
262 REPORTER_ASSERT(reporter, c == expected);
263 }
264
test_convexity2(skiatest::Reporter * reporter)265 static void test_convexity2(skiatest::Reporter* reporter) {
266 SkPath pt;
267 pt.moveTo(0, 0);
268 pt.close();
269 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
270
271 SkPath line;
272 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
273 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
274 line.close();
275 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
276
277 SkPath triLeft;
278 triLeft.moveTo(0, 0);
279 triLeft.lineTo(SK_Scalar1, 0);
280 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
281 triLeft.close();
282 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
283
284 SkPath triRight;
285 triRight.moveTo(0, 0);
286 triRight.lineTo(-SK_Scalar1, 0);
287 triRight.lineTo(SK_Scalar1, SK_Scalar1);
288 triRight.close();
289 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
290
291 SkPath square;
292 square.moveTo(0, 0);
293 square.lineTo(SK_Scalar1, 0);
294 square.lineTo(SK_Scalar1, SK_Scalar1);
295 square.lineTo(0, SK_Scalar1);
296 square.close();
297 check_convexity(reporter, square, SkPath::kConvex_Convexity);
298
299 SkPath redundantSquare;
300 redundantSquare.moveTo(0, 0);
301 redundantSquare.lineTo(0, 0);
302 redundantSquare.lineTo(0, 0);
303 redundantSquare.lineTo(SK_Scalar1, 0);
304 redundantSquare.lineTo(SK_Scalar1, 0);
305 redundantSquare.lineTo(SK_Scalar1, 0);
306 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
307 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
308 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
309 redundantSquare.lineTo(0, SK_Scalar1);
310 redundantSquare.lineTo(0, SK_Scalar1);
311 redundantSquare.lineTo(0, SK_Scalar1);
312 redundantSquare.close();
313 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
314
315 SkPath bowTie;
316 bowTie.moveTo(0, 0);
317 bowTie.lineTo(0, 0);
318 bowTie.lineTo(0, 0);
319 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
320 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
321 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
322 bowTie.lineTo(SK_Scalar1, 0);
323 bowTie.lineTo(SK_Scalar1, 0);
324 bowTie.lineTo(SK_Scalar1, 0);
325 bowTie.lineTo(0, SK_Scalar1);
326 bowTie.lineTo(0, SK_Scalar1);
327 bowTie.lineTo(0, SK_Scalar1);
328 bowTie.close();
329 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
330
331 SkPath spiral;
332 spiral.moveTo(0, 0);
333 spiral.lineTo(100*SK_Scalar1, 0);
334 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
335 spiral.lineTo(0, 100*SK_Scalar1);
336 spiral.lineTo(0, 50*SK_Scalar1);
337 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
338 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
339 spiral.close();
340 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
341
342 SkPath dent;
343 dent.moveTo(0, 0);
344 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
345 dent.lineTo(0, 100*SK_Scalar1);
346 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
347 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
348 dent.close();
349 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
350 }
351
check_convex_bounds(skiatest::Reporter * reporter,const SkPath & p,const SkRect & bounds)352 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
353 const SkRect& bounds) {
354 REPORTER_ASSERT(reporter, p.isConvex());
355 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
356
357 SkPath p2(p);
358 REPORTER_ASSERT(reporter, p2.isConvex());
359 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
360
361 SkPath other;
362 other.swap(p2);
363 REPORTER_ASSERT(reporter, other.isConvex());
364 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
365 }
366
setFromString(SkPath * path,const char str[])367 static void setFromString(SkPath* path, const char str[]) {
368 bool first = true;
369 while (str) {
370 SkScalar x, y;
371 str = SkParse::FindScalar(str, &x);
372 if (NULL == str) {
373 break;
374 }
375 str = SkParse::FindScalar(str, &y);
376 SkASSERT(str);
377 if (first) {
378 path->moveTo(x, y);
379 first = false;
380 } else {
381 path->lineTo(x, y);
382 }
383 }
384 }
385
test_convexity(skiatest::Reporter * reporter)386 static void test_convexity(skiatest::Reporter* reporter) {
387 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
388 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
389
390 SkPath path;
391
392 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
393 path.addCircle(0, 0, SkIntToScalar(10));
394 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
395 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
396 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
397 path.reset();
398 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
399 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
400 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
401 path.reset();
402 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
403 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
404 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
405
406 static const struct {
407 const char* fPathStr;
408 SkPath::Convexity fExpectedConvexity;
409 } gRec[] = {
410 { "", SkPath::kConvex_Convexity },
411 { "0 0", SkPath::kConvex_Convexity },
412 { "0 0 10 10", SkPath::kConvex_Convexity },
413 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
414 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
415 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
416 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
417 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
418 };
419
420 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
421 SkPath path;
422 setFromString(&path, gRec[i].fPathStr);
423 SkPath::Convexity c = SkPath::ComputeConvexity(path);
424 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
425 }
426 }
427
428 // Simple isRect test is inline TestPath, below.
429 // test_isRect provides more extensive testing.
test_isRect(skiatest::Reporter * reporter)430 static void test_isRect(skiatest::Reporter* reporter) {
431 // passing tests (all moveTo / lineTo...
432 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
433 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
434 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
435 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
436 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
437 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
438 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
439 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
440 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
441 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
442 {1, 0}, {.5f, 0}};
443 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
444 {0, 1}, {0, .5f}};
445 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
446 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
447 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
448
449 // failing tests
450 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
451 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
452 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
453 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
454 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
455 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
456 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
457 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
458
459 // failing, no close
460 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
461 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
462
463 size_t testLen[] = {
464 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
465 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
466 sizeof(rd), sizeof(re),
467 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
468 sizeof(f7), sizeof(f8),
469 sizeof(c1), sizeof(c2)
470 };
471 SkPoint* tests[] = {
472 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
473 f1, f2, f3, f4, f5, f6, f7, f8,
474 c1, c2
475 };
476 SkPoint* lastPass = re;
477 SkPoint* lastClose = f8;
478 bool fail = false;
479 bool close = true;
480 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
481 size_t index;
482 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
483 SkPath path;
484 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
485 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
486 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
487 }
488 if (close) {
489 path.close();
490 }
491 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
492 if (tests[testIndex] == lastPass) {
493 fail = true;
494 }
495 if (tests[testIndex] == lastClose) {
496 close = false;
497 }
498 }
499
500 // fail, close then line
501 SkPath path1;
502 path1.moveTo(r1[0].fX, r1[0].fY);
503 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
504 path1.lineTo(r1[index].fX, r1[index].fY);
505 }
506 path1.close();
507 path1.lineTo(1, 0);
508 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
509
510 // fail, move in the middle
511 path1.reset();
512 path1.moveTo(r1[0].fX, r1[0].fY);
513 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
514 if (index == 2) {
515 path1.moveTo(1, .5f);
516 }
517 path1.lineTo(r1[index].fX, r1[index].fY);
518 }
519 path1.close();
520 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
521
522 // fail, move on the edge
523 path1.reset();
524 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
525 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
526 path1.lineTo(r1[index].fX, r1[index].fY);
527 }
528 path1.close();
529 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
530
531 // fail, quad
532 path1.reset();
533 path1.moveTo(r1[0].fX, r1[0].fY);
534 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
535 if (index == 2) {
536 path1.quadTo(1, .5f, 1, .5f);
537 }
538 path1.lineTo(r1[index].fX, r1[index].fY);
539 }
540 path1.close();
541 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
542
543 // fail, cubic
544 path1.reset();
545 path1.moveTo(r1[0].fX, r1[0].fY);
546 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
547 if (index == 2) {
548 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
549 }
550 path1.lineTo(r1[index].fX, r1[index].fY);
551 }
552 path1.close();
553 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
554 }
555
test_flattening(skiatest::Reporter * reporter)556 static void test_flattening(skiatest::Reporter* reporter) {
557 SkPath p;
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 SkWriter32 writer(100);
571 p.flatten(writer);
572 size_t size = writer.size();
573 SkAutoMalloc storage(size);
574 writer.flatten(storage.get());
575 SkReader32 reader(storage.get(), size);
576
577 SkPath p1;
578 REPORTER_ASSERT(reporter, p1 != p);
579 p1.unflatten(reader);
580 REPORTER_ASSERT(reporter, p1 == p);
581 }
582
test_transform(skiatest::Reporter * reporter)583 static void test_transform(skiatest::Reporter* reporter) {
584 SkPath p, p1;
585
586 static const SkPoint pts[] = {
587 { 0, 0 },
588 { SkIntToScalar(10), SkIntToScalar(10) },
589 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
590 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
591 };
592 p.moveTo(pts[0]);
593 p.lineTo(pts[1]);
594 p.quadTo(pts[2], pts[3]);
595 p.cubicTo(pts[4], pts[5], pts[6]);
596
597 SkMatrix matrix;
598 matrix.reset();
599 p.transform(matrix, &p1);
600 REPORTER_ASSERT(reporter, p == p1);
601
602 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
603 p.transform(matrix, &p1);
604 SkPoint pts1[7];
605 int count = p1.getPoints(pts1, 7);
606 REPORTER_ASSERT(reporter, 7 == count);
607 for (int i = 0; i < count; ++i) {
608 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
609 REPORTER_ASSERT(reporter, newPt == pts1[i]);
610 }
611 }
612
test_zero_length_paths(skiatest::Reporter * reporter)613 static void test_zero_length_paths(skiatest::Reporter* reporter) {
614 SkPath p;
615 SkPoint pt;
616 SkRect bounds;
617
618 // Lone moveTo case
619 p.moveTo(SK_Scalar1, SK_Scalar1);
620 REPORTER_ASSERT(reporter, !p.isEmpty());
621 REPORTER_ASSERT(reporter, 1 == p.countPoints());
622 p.getLastPt(&pt);
623 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
624 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
625 bounds.set(0, 0, 0, 0);
626 REPORTER_ASSERT(reporter, bounds == p.getBounds());
627
628 // MoveTo-MoveTo case
629 p.moveTo(SK_Scalar1*2, SK_Scalar1);
630 REPORTER_ASSERT(reporter, !p.isEmpty());
631 REPORTER_ASSERT(reporter, 2 == p.countPoints());
632 p.getLastPt(&pt);
633 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
634 REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
635 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
636 REPORTER_ASSERT(reporter, bounds == p.getBounds());
637
638 // moveTo-close case
639 p.reset();
640 p.moveTo(SK_Scalar1, SK_Scalar1);
641 p.close();
642 bounds.set(0, 0, 0, 0);
643 REPORTER_ASSERT(reporter, !p.isEmpty());
644 REPORTER_ASSERT(reporter, 1 == p.countPoints());
645 REPORTER_ASSERT(reporter, bounds == p.getBounds());
646
647 // moveTo-close-moveTo-close case
648 p.moveTo(SK_Scalar1*2, SK_Scalar1);
649 p.close();
650 bounds.set(SK_Scalar1, SK_Scalar1, 2*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 case
656 p.reset();
657 p.moveTo(SK_Scalar1, SK_Scalar1);
658 p.lineTo(SK_Scalar1, SK_Scalar1);
659 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
660 REPORTER_ASSERT(reporter, !p.isEmpty());
661 REPORTER_ASSERT(reporter, 2 == p.countPoints());
662 REPORTER_ASSERT(reporter, bounds == p.getBounds());
663
664 // moveTo-lineTo-moveTo-lineTo case
665 p.moveTo(SK_Scalar1*2, SK_Scalar1);
666 p.lineTo(SK_Scalar1*2, SK_Scalar1);
667 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
668 REPORTER_ASSERT(reporter, !p.isEmpty());
669 REPORTER_ASSERT(reporter, 4 == p.countPoints());
670 REPORTER_ASSERT(reporter, bounds == p.getBounds());
671
672 // moveTo-line-close case
673 p.reset();
674 p.moveTo(SK_Scalar1, SK_Scalar1);
675 p.lineTo(SK_Scalar1, SK_Scalar1);
676 p.close();
677 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
678 REPORTER_ASSERT(reporter, !p.isEmpty());
679 REPORTER_ASSERT(reporter, 2 == p.countPoints());
680 REPORTER_ASSERT(reporter, bounds == p.getBounds());
681
682 // moveTo-line-close-moveTo-line-close case
683 p.moveTo(SK_Scalar1*2, SK_Scalar1);
684 p.lineTo(SK_Scalar1*2, SK_Scalar1);
685 p.close();
686 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
687 REPORTER_ASSERT(reporter, !p.isEmpty());
688 REPORTER_ASSERT(reporter, 4 == p.countPoints());
689 REPORTER_ASSERT(reporter, bounds == p.getBounds());
690
691 // moveTo-quadTo case
692 p.reset();
693 p.moveTo(SK_Scalar1, SK_Scalar1);
694 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
695 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
696 REPORTER_ASSERT(reporter, !p.isEmpty());
697 REPORTER_ASSERT(reporter, 3 == p.countPoints());
698 REPORTER_ASSERT(reporter, bounds == p.getBounds());
699
700 // moveTo-quadTo-close case
701 p.close();
702 REPORTER_ASSERT(reporter, !p.isEmpty());
703 REPORTER_ASSERT(reporter, 3 == p.countPoints());
704 REPORTER_ASSERT(reporter, bounds == p.getBounds());
705
706 // moveTo-quadTo-moveTo-quadTo case
707 p.reset();
708 p.moveTo(SK_Scalar1, SK_Scalar1);
709 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
710 p.moveTo(SK_Scalar1*2, SK_Scalar1);
711 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
712 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
713 REPORTER_ASSERT(reporter, !p.isEmpty());
714 REPORTER_ASSERT(reporter, 6 == p.countPoints());
715 REPORTER_ASSERT(reporter, bounds == p.getBounds());
716
717 // moveTo-cubicTo case
718 p.reset();
719 p.moveTo(SK_Scalar1, SK_Scalar1);
720 p.cubicTo(SK_Scalar1, SK_Scalar1,
721 SK_Scalar1, SK_Scalar1,
722 SK_Scalar1, SK_Scalar1);
723 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
724 REPORTER_ASSERT(reporter, !p.isEmpty());
725 REPORTER_ASSERT(reporter, 4 == p.countPoints());
726 REPORTER_ASSERT(reporter, bounds == p.getBounds());
727
728 // moveTo-quadTo-close case
729 p.close();
730 REPORTER_ASSERT(reporter, !p.isEmpty());
731 REPORTER_ASSERT(reporter, 4 == p.countPoints());
732 REPORTER_ASSERT(reporter, bounds == p.getBounds());
733
734 // moveTo-quadTo-moveTo-quadTo case
735 p.reset();
736 p.moveTo(SK_Scalar1, SK_Scalar1);
737 p.cubicTo(SK_Scalar1, SK_Scalar1,
738 SK_Scalar1, SK_Scalar1,
739 SK_Scalar1, SK_Scalar1);
740 p.moveTo(SK_Scalar1*2, SK_Scalar1);
741 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
742 SK_Scalar1*2, SK_Scalar1,
743 SK_Scalar1*2, SK_Scalar1);
744 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
745 REPORTER_ASSERT(reporter, !p.isEmpty());
746 REPORTER_ASSERT(reporter, 8 == p.countPoints());
747 REPORTER_ASSERT(reporter, bounds == p.getBounds());
748 }
749
750 struct SegmentInfo {
751 SkPath fPath;
752 int fPointCount;
753 };
754
755 #define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
756
test_segment_masks(skiatest::Reporter * reporter)757 static void test_segment_masks(skiatest::Reporter* reporter) {
758 SkPath p;
759 p.moveTo(0, 0);
760 p.quadTo(100, 100, 200, 200);
761 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
762 REPORTER_ASSERT(reporter, !p.isEmpty());
763 p.cubicTo(100, 100, 200, 200, 300, 300);
764 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
765 REPORTER_ASSERT(reporter, !p.isEmpty());
766 p.reset();
767 p.moveTo(0, 0);
768 p.cubicTo(100, 100, 200, 200, 300, 300);
769 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
770 REPORTER_ASSERT(reporter, !p.isEmpty());
771 }
772
test_iter(skiatest::Reporter * reporter)773 static void test_iter(skiatest::Reporter* reporter) {
774 SkPath p;
775 SkPoint pts[4];
776
777 // Test an iterator with no path
778 SkPath::Iter noPathIter;
779 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
780 // Test that setting an empty path works
781 noPathIter.setPath(p, false);
782 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
783 // Test that close path makes no difference for an empty path
784 noPathIter.setPath(p, true);
785 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
786
787 // Test an iterator with an initial empty path
788 SkPath::Iter iter(p, false);
789 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
790
791 // Test that close path makes no difference
792 SkPath::Iter forceCloseIter(p, true);
793 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
794
795 // Test that a move-only path produces nothing when iterated.
796 p.moveTo(SK_Scalar1, 0);
797 iter.setPath(p, false);
798 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
799
800 // No matter how many moves we add, we should still get nothing back.
801 p.moveTo(SK_Scalar1*2, 0);
802 p.moveTo(SK_Scalar1*3, 0);
803 p.moveTo(SK_Scalar1*4, 0);
804 p.moveTo(SK_Scalar1*5, 0);
805 iter.setPath(p, false);
806 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
807
808 // Nor should force closing
809 forceCloseIter.setPath(p, true);
810 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
811
812 // Initial closes should be ignored
813 p.reset();
814 p.close();
815 iter.setPath(p, false);
816 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
817 // Even if force closed
818 forceCloseIter.setPath(p, true);
819 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
820
821 // Move/close sequences should also be ignored
822 p.reset();
823 p.close();
824 p.moveTo(SK_Scalar1, 0);
825 p.close();
826 p.close();
827 p.moveTo(SK_Scalar1*2, 0);
828 p.close();
829 p.moveTo(SK_Scalar1*3, 0);
830 p.moveTo(SK_Scalar1*4, 0);
831 p.close();
832 iter.setPath(p, false);
833 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
834 // Even if force closed
835 forceCloseIter.setPath(p, true);
836 REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
837
838 // The GM degeneratesegments.cpp test is more extensive
839 }
840
test_raw_iter(skiatest::Reporter * reporter)841 static void test_raw_iter(skiatest::Reporter* reporter) {
842 SkPath p;
843 SkPoint pts[4];
844
845 // Test an iterator with no path
846 SkPath::RawIter noPathIter;
847 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
848 // Test that setting an empty path works
849 noPathIter.setPath(p);
850 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
851
852 // Test an iterator with an initial empty path
853 SkPath::RawIter iter(p);
854 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
855
856 // Test that a move-only path returns the move.
857 p.moveTo(SK_Scalar1, 0);
858 iter.setPath(p);
859 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
860 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
861 REPORTER_ASSERT(reporter, pts[0].fY == 0);
862 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
863
864 // No matter how many moves we add, we should get them all back
865 p.moveTo(SK_Scalar1*2, SK_Scalar1);
866 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
867 iter.setPath(p);
868 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
869 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
870 REPORTER_ASSERT(reporter, pts[0].fY == 0);
871 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
872 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
873 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
874 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
875 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
876 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
877 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
878
879 // Initial close is never ever stored
880 p.reset();
881 p.close();
882 iter.setPath(p);
883 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
884
885 // Move/close sequences
886 p.reset();
887 p.close(); // Not stored, no purpose
888 p.moveTo(SK_Scalar1, 0);
889 p.close();
890 p.close(); // Not stored, no purpose
891 p.moveTo(SK_Scalar1*2, SK_Scalar1);
892 p.close();
893 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
894 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
895 p.close();
896 iter.setPath(p);
897 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
898 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
899 REPORTER_ASSERT(reporter, pts[0].fY == 0);
900 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
901 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
902 REPORTER_ASSERT(reporter, pts[0].fY == 0);
903 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
904 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
905 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
906 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
907 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
908 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
909 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
910 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
911 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
912 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
913 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
914 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
915 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
916 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
917 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
918 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
919
920 // Generate random paths and verify
921 SkPoint randomPts[25];
922 for (int i = 0; i < 5; ++i) {
923 for (int j = 0; j < 5; ++j) {
924 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
925 }
926 }
927
928 // Max of 10 segments, max 3 points per segment
929 SkRandom rand(9876543);
930 SkPoint expectedPts[31]; // May have leading moveTo
931 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
932 SkPath::Verb nextVerb;
933
934 for (int i = 0; i < 500; ++i) {
935 p.reset();
936 bool lastWasClose = true;
937 bool haveMoveTo = false;
938 SkPoint lastMoveToPt = { 0, 0 };
939 int numPoints = 0;
940 int numVerbs = (rand.nextU() >> 16) % 10;
941 int numIterVerbs = 0;
942 for (int j = 0; j < numVerbs; ++j) {
943 do {
944 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
945 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
946 int numRequiredPts;
947 switch (nextVerb) {
948 case SkPath::kMove_Verb:
949 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
950 p.moveTo(expectedPts[numPoints]);
951 lastMoveToPt = expectedPts[numPoints];
952 numPoints += 1;
953 lastWasClose = false;
954 haveMoveTo = true;
955 break;
956 case SkPath::kLine_Verb:
957 if (!haveMoveTo) {
958 expectedPts[numPoints++] = lastMoveToPt;
959 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
960 haveMoveTo = true;
961 }
962 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
963 p.lineTo(expectedPts[numPoints]);
964 numPoints += 1;
965 lastWasClose = false;
966 break;
967 case SkPath::kQuad_Verb:
968 if (!haveMoveTo) {
969 expectedPts[numPoints++] = lastMoveToPt;
970 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
971 haveMoveTo = true;
972 }
973 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
974 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
975 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
976 numPoints += 2;
977 lastWasClose = false;
978 break;
979 case SkPath::kCubic_Verb:
980 if (!haveMoveTo) {
981 expectedPts[numPoints++] = lastMoveToPt;
982 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
983 haveMoveTo = true;
984 }
985 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
986 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
987 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
988 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
989 expectedPts[numPoints + 2]);
990 numPoints += 3;
991 lastWasClose = false;
992 break;
993 case SkPath::kClose_Verb:
994 p.close();
995 haveMoveTo = false;
996 lastWasClose = true;
997 break;
998 default:;
999 }
1000 expectedVerbs[numIterVerbs++] = nextVerb;
1001 }
1002
1003 iter.setPath(p);
1004 numVerbs = numIterVerbs;
1005 numIterVerbs = 0;
1006 int numIterPts = 0;
1007 SkPoint lastMoveTo;
1008 SkPoint lastPt;
1009 lastMoveTo.set(0, 0);
1010 lastPt.set(0, 0);
1011 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1012 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1013 numIterVerbs++;
1014 switch (nextVerb) {
1015 case SkPath::kMove_Verb:
1016 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1017 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1018 lastPt = lastMoveTo = pts[0];
1019 numIterPts += 1;
1020 break;
1021 case SkPath::kLine_Verb:
1022 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1023 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1024 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1025 lastPt = pts[1];
1026 numIterPts += 1;
1027 break;
1028 case SkPath::kQuad_Verb:
1029 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1030 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1031 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1032 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1033 lastPt = pts[2];
1034 numIterPts += 2;
1035 break;
1036 case SkPath::kCubic_Verb:
1037 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1038 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1039 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1040 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1041 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1042 lastPt = pts[3];
1043 numIterPts += 3;
1044 break;
1045 case SkPath::kClose_Verb:
1046 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1047 lastPt = lastMoveTo;
1048 break;
1049 default:;
1050 }
1051 }
1052 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1053 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1054 }
1055 }
1056
1057 void TestPath(skiatest::Reporter* reporter);
TestPath(skiatest::Reporter * reporter)1058 void TestPath(skiatest::Reporter* reporter) {
1059 {
1060 SkSize size;
1061 size.fWidth = 3.4f;
1062 size.width();
1063 size = SkSize::Make(3,4);
1064 SkISize isize = SkISize::Make(3,4);
1065 }
1066
1067 SkTSize<SkScalar>::Make(3,4);
1068
1069 SkPath p, p2;
1070 SkRect bounds, bounds2;
1071
1072 REPORTER_ASSERT(reporter, p.isEmpty());
1073 REPORTER_ASSERT(reporter, 0 == p.countPoints());
1074 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1075 REPORTER_ASSERT(reporter, p.isConvex());
1076 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1077 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1078 REPORTER_ASSERT(reporter, p == p2);
1079 REPORTER_ASSERT(reporter, !(p != p2));
1080
1081 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
1082
1083 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
1084
1085 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1086 check_convex_bounds(reporter, p, bounds);
1087 // we have quads or cubics
1088 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
1089 REPORTER_ASSERT(reporter, !p.isEmpty());
1090
1091 p.reset();
1092 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1093 REPORTER_ASSERT(reporter, p.isEmpty());
1094
1095 p.addOval(bounds);
1096 check_convex_bounds(reporter, p, bounds);
1097 REPORTER_ASSERT(reporter, !p.isEmpty());
1098
1099 p.reset();
1100 p.addRect(bounds);
1101 check_convex_bounds(reporter, p, bounds);
1102 // we have only lines
1103 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
1104 REPORTER_ASSERT(reporter, !p.isEmpty());
1105
1106 REPORTER_ASSERT(reporter, p != p2);
1107 REPORTER_ASSERT(reporter, !(p == p2));
1108
1109 // does getPoints return the right result
1110 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1111 SkPoint pts[4];
1112 int count = p.getPoints(pts, 4);
1113 REPORTER_ASSERT(reporter, count == 4);
1114 bounds2.set(pts, 4);
1115 REPORTER_ASSERT(reporter, bounds == bounds2);
1116
1117 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1118 p.offset(SK_Scalar1*3, SK_Scalar1*4);
1119 REPORTER_ASSERT(reporter, bounds == p.getBounds());
1120
1121 REPORTER_ASSERT(reporter, p.isRect(NULL));
1122 bounds2.setEmpty();
1123 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1124 REPORTER_ASSERT(reporter, bounds == bounds2);
1125
1126 // now force p to not be a rect
1127 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1128 p.addRect(bounds);
1129 REPORTER_ASSERT(reporter, !p.isRect(NULL));
1130 test_isRect(reporter);
1131
1132 test_zero_length_paths(reporter);
1133 test_direction(reporter);
1134 test_convexity(reporter);
1135 test_convexity2(reporter);
1136 test_close(reporter);
1137 test_segment_masks(reporter);
1138 test_flattening(reporter);
1139 test_transform(reporter);
1140 test_bounds(reporter);
1141 test_iter(reporter);
1142 test_raw_iter(reporter);
1143 }
1144
1145 #include "TestClassDef.h"
1146 DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
1147