blob: 2b4342ace63a7f5eb9e06573dab30f66c3698438 [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.com53effc52011-09-21 19:05:12 +000012#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000013#include "SkSize.h"
reed@google.com53effc52011-09-21 19:05:12 +000014#include "SkWriter32.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000015
reed@google.comffdb0182011-11-14 19:29:14 +000016static void add_rect(SkPath* path, const SkRect& r) {
17 path->moveTo(r.fLeft, r.fTop);
18 path->lineTo(r.fRight, r.fTop);
19 path->lineTo(r.fRight, r.fBottom);
20 path->lineTo(r.fLeft, r.fBottom);
21 path->close();
22}
23
24static void test_bounds(skiatest::Reporter* reporter) {
25 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +000026 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
27 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
28 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
29 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +000030 };
31
32 SkPath path0, path1;
33 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
34 path0.addRect(rects[i]);
35 add_rect(&path1, rects[i]);
36 }
37
38 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
39}
40
reed@google.com55b5f4b2011-09-07 12:23:41 +000041static void stroke_cubic(const SkPoint pts[4]) {
42 SkPath path;
43 path.moveTo(pts[0]);
44 path.cubicTo(pts[1], pts[2], pts[3]);
45
46 SkPaint paint;
47 paint.setStyle(SkPaint::kStroke_Style);
48 paint.setStrokeWidth(SK_Scalar1 * 2);
49
50 SkPath fill;
51 paint.getFillPath(path, &fill);
52}
53
54// just ensure this can run w/o any SkASSERTS firing in the debug build
55// we used to assert due to differences in how we determine a degenerate vector
56// but that was fixed with the introduction of SkPoint::CanNormalize
57static void stroke_tiny_cubic() {
58 SkPoint p0[] = {
59 { 372.0f, 92.0f },
60 { 372.0f, 92.0f },
61 { 372.0f, 92.0f },
62 { 372.0f, 92.0f },
63 };
64
65 stroke_cubic(p0);
66
67 SkPoint p1[] = {
68 { 372.0f, 92.0f },
69 { 372.0007f, 92.000755f },
70 { 371.99927f, 92.003922f },
71 { 371.99826f, 92.003899f },
72 };
73
74 stroke_cubic(p1);
75}
76
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +000077static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
78 for (int i = 0; i < 2; ++i) {
79 SkPath::Iter iter(path, (bool)i);
80 SkPoint mv;
81 SkPoint pts[4];
82 SkPath::Verb v;
83 int nMT = 0;
84 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +000085 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +000086 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
87 switch (v) {
88 case SkPath::kMove_Verb:
89 mv = pts[0];
90 ++nMT;
91 break;
92 case SkPath::kClose_Verb:
93 REPORTER_ASSERT(reporter, mv == pts[0]);
94 ++nCL;
95 break;
96 default:
97 break;
98 }
99 }
100 // if we force a close on the interator we should have a close
101 // for every moveTo
102 REPORTER_ASSERT(reporter, !i || nMT == nCL);
103 }
104}
105
106static void test_close(skiatest::Reporter* reporter) {
107 SkPath closePt;
108 closePt.moveTo(0, 0);
109 closePt.close();
110 check_close(reporter, closePt);
111
112 SkPath openPt;
113 openPt.moveTo(0, 0);
114 check_close(reporter, openPt);
115
116 SkPath empty;
117 check_close(reporter, empty);
118 empty.close();
119 check_close(reporter, empty);
120
121 SkPath rect;
122 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
123 check_close(reporter, rect);
124 rect.close();
125 check_close(reporter, rect);
126
127 SkPath quad;
128 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
129 check_close(reporter, quad);
130 quad.close();
131 check_close(reporter, quad);
132
133 SkPath cubic;
134 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
135 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
136 check_close(reporter, cubic);
137 cubic.close();
138 check_close(reporter, cubic);
139
140 SkPath line;
141 line.moveTo(SK_Scalar1, SK_Scalar1);
142 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
143 check_close(reporter, line);
144 line.close();
145 check_close(reporter, line);
146
147 SkPath rect2;
148 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
149 rect2.close();
150 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
151 check_close(reporter, rect2);
152 rect2.close();
153 check_close(reporter, rect2);
154
155 SkPath oval3;
156 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
157 oval3.close();
158 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
159 check_close(reporter, oval3);
160 oval3.close();
161 check_close(reporter, oval3);
162
163 SkPath moves;
164 moves.moveTo(SK_Scalar1, SK_Scalar1);
165 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
166 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
167 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
168 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000169
170 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000171}
172
reed@google.com7c424812011-05-15 04:38:34 +0000173static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
174 SkPath::Convexity expected) {
175 SkPath::Convexity c = SkPath::ComputeConvexity(path);
176 REPORTER_ASSERT(reporter, c == expected);
177}
178
179static void test_convexity2(skiatest::Reporter* reporter) {
180 SkPath pt;
181 pt.moveTo(0, 0);
182 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000183 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000184
185 SkPath line;
186 line.moveTo(12, 20);
187 line.lineTo(-12, -20);
188 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000189 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000190
191 SkPath triLeft;
192 triLeft.moveTo(0, 0);
193 triLeft.lineTo(1, 0);
194 triLeft.lineTo(1, 1);
195 triLeft.close();
196 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
197
198 SkPath triRight;
199 triRight.moveTo(0, 0);
200 triRight.lineTo(-1, 0);
201 triRight.lineTo(1, 1);
202 triRight.close();
203 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
204
205 SkPath square;
206 square.moveTo(0, 0);
207 square.lineTo(1, 0);
208 square.lineTo(1, 1);
209 square.lineTo(0, 1);
210 square.close();
211 check_convexity(reporter, square, SkPath::kConvex_Convexity);
212
213 SkPath redundantSquare;
214 redundantSquare.moveTo(0, 0);
215 redundantSquare.lineTo(0, 0);
216 redundantSquare.lineTo(0, 0);
217 redundantSquare.lineTo(1, 0);
218 redundantSquare.lineTo(1, 0);
219 redundantSquare.lineTo(1, 0);
220 redundantSquare.lineTo(1, 1);
221 redundantSquare.lineTo(1, 1);
222 redundantSquare.lineTo(1, 1);
223 redundantSquare.lineTo(0, 1);
224 redundantSquare.lineTo(0, 1);
225 redundantSquare.lineTo(0, 1);
226 redundantSquare.close();
227 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
228
229 SkPath bowTie;
230 bowTie.moveTo(0, 0);
231 bowTie.lineTo(0, 0);
232 bowTie.lineTo(0, 0);
233 bowTie.lineTo(1, 1);
234 bowTie.lineTo(1, 1);
235 bowTie.lineTo(1, 1);
236 bowTie.lineTo(1, 0);
237 bowTie.lineTo(1, 0);
238 bowTie.lineTo(1, 0);
239 bowTie.lineTo(0, 1);
240 bowTie.lineTo(0, 1);
241 bowTie.lineTo(0, 1);
242 bowTie.close();
243 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
244
245 SkPath spiral;
246 spiral.moveTo(0, 0);
epoger@google.com2047f002011-05-17 17:36:59 +0000247 spiral.lineTo(100, 0);
248 spiral.lineTo(100, 100);
249 spiral.lineTo(0, 100);
250 spiral.lineTo(0, 50);
251 spiral.lineTo(50, 50);
252 spiral.lineTo(50, 75);
reed@google.com7c424812011-05-15 04:38:34 +0000253 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000254 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000255
256 SkPath dent;
epoger@google.com1f753992011-05-18 20:23:30 +0000257 dent.moveTo(SkIntToScalar(0), SkIntToScalar(0));
258 dent.lineTo(SkIntToScalar(100), SkIntToScalar(100));
259 dent.lineTo(SkIntToScalar(0), SkIntToScalar(100));
260 dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200));
261 dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100));
reed@google.com7c424812011-05-15 04:38:34 +0000262 dent.close();
263 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
264}
265
reed@android.com6b82d1a2009-06-03 02:35:01 +0000266static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
267 const SkRect& bounds) {
268 REPORTER_ASSERT(reporter, p.isConvex());
269 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000270
reed@android.com6b82d1a2009-06-03 02:35:01 +0000271 SkPath p2(p);
272 REPORTER_ASSERT(reporter, p2.isConvex());
273 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
274
275 SkPath other;
276 other.swap(p2);
277 REPORTER_ASSERT(reporter, other.isConvex());
278 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
279}
280
reed@google.com04863fa2011-05-15 04:08:24 +0000281static void setFromString(SkPath* path, const char str[]) {
282 bool first = true;
283 while (str) {
284 SkScalar x, y;
285 str = SkParse::FindScalar(str, &x);
286 if (NULL == str) {
287 break;
288 }
289 str = SkParse::FindScalar(str, &y);
290 SkASSERT(str);
291 if (first) {
292 path->moveTo(x, y);
293 first = false;
294 } else {
295 path->lineTo(x, y);
296 }
297 }
298}
299
300static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000301 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
302 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
303
304 SkPath path;
305
reed@google.comb54455e2011-05-16 14:16:04 +0000306 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com04863fa2011-05-15 04:08:24 +0000307 path.addCircle(0, 0, 10);
308 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
309 path.addCircle(0, 0, 10); // 2nd circle
310 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
311 path.reset();
312 path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
313 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
314 path.reset();
315 path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
316 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
317
318 static const struct {
319 const char* fPathStr;
320 SkPath::Convexity fExpectedConvexity;
321 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000322 { "", SkPath::kConvex_Convexity },
323 { "0 0", SkPath::kConvex_Convexity },
324 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000325 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000326 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
327 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
328 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
329 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
330 };
331
332 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
333 SkPath path;
334 setFromString(&path, gRec[i].fPathStr);
335 SkPath::Convexity c = SkPath::ComputeConvexity(path);
336 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
337 }
338}
339
caryclark@google.comf1316942011-07-26 19:54:45 +0000340// Simple isRect test is inline TestPath, below.
341// test_isRect provides more extensive testing.
342static void test_isRect(skiatest::Reporter* reporter) {
343 // passing tests (all moveTo / lineTo...
344 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
345 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
346 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
347 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
348 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
349 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
350 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
351 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
352 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
353 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
354 {1, 0}, {.5f, 0}};
355 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
356 {0, 1}, {0, .5f}};
357 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
358 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
359 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
360
361 // failing tests
362 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
363 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
364 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
365 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
366 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
367 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
368 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
369 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
370
371 // failing, no close
372 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
373 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
374
375 size_t testLen[] = {
376 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
377 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
378 sizeof(rd), sizeof(re),
379 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
380 sizeof(f7), sizeof(f8),
381 sizeof(c1), sizeof(c2)
382 };
383 SkPoint* tests[] = {
384 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
385 f1, f2, f3, f4, f5, f6, f7, f8,
386 c1, c2
387 };
388 SkPoint* lastPass = re;
389 SkPoint* lastClose = f8;
390 bool fail = false;
391 bool close = true;
392 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
393 size_t index;
394 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
395 SkPath path;
396 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
397 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
398 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
399 }
400 if (close) {
401 path.close();
402 }
403 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
404 if (tests[testIndex] == lastPass) {
405 fail = true;
406 }
407 if (tests[testIndex] == lastClose) {
408 close = false;
409 }
410 }
411
412 // fail, close then line
413 SkPath path1;
414 path1.moveTo(r1[0].fX, r1[0].fY);
415 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
416 path1.lineTo(r1[index].fX, r1[index].fY);
417 }
418 path1.close();
419 path1.lineTo(1, 0);
420 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
421
422 // fail, move in the middle
423 path1.reset();
424 path1.moveTo(r1[0].fX, r1[0].fY);
425 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
426 if (index == 2) {
427 path1.moveTo(1, .5f);
428 }
429 path1.lineTo(r1[index].fX, r1[index].fY);
430 }
431 path1.close();
432 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
433
434 // fail, move on the edge
435 path1.reset();
436 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
437 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
438 path1.lineTo(r1[index].fX, r1[index].fY);
439 }
440 path1.close();
441 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
442
443 // fail, quad
444 path1.reset();
445 path1.moveTo(r1[0].fX, r1[0].fY);
446 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
447 if (index == 2) {
448 path1.quadTo(1, .5f, 1, .5f);
449 }
450 path1.lineTo(r1[index].fX, r1[index].fY);
451 }
452 path1.close();
453 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
454
455 // fail, cubic
456 path1.reset();
457 path1.moveTo(r1[0].fX, r1[0].fY);
458 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
459 if (index == 2) {
460 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
461 }
462 path1.lineTo(r1[index].fX, r1[index].fY);
463 }
464 path1.close();
465 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
466}
467
reed@google.com53effc52011-09-21 19:05:12 +0000468static void test_flattening(skiatest::Reporter* reporter) {
469 SkPath p;
470
471 static const SkPoint pts[] = {
472 { 0, 0 },
473 { SkIntToScalar(10), SkIntToScalar(10) },
474 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
475 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
476 };
477 p.moveTo(pts[0]);
478 p.lineTo(pts[1]);
479 p.quadTo(pts[2], pts[3]);
480 p.cubicTo(pts[4], pts[5], pts[6]);
481
482 SkWriter32 writer(100);
483 p.flatten(writer);
484 size_t size = writer.size();
485 SkAutoMalloc storage(size);
486 writer.flatten(storage.get());
487 SkReader32 reader(storage.get(), size);
488
489 SkPath p1;
490 REPORTER_ASSERT(reporter, p1 != p);
491 p1.unflatten(reader);
492 REPORTER_ASSERT(reporter, p1 == p);
493}
494
495static void test_transform(skiatest::Reporter* reporter) {
496 SkPath p, p1;
497
498 static const SkPoint pts[] = {
499 { 0, 0 },
500 { SkIntToScalar(10), SkIntToScalar(10) },
501 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
502 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
503 };
504 p.moveTo(pts[0]);
505 p.lineTo(pts[1]);
506 p.quadTo(pts[2], pts[3]);
507 p.cubicTo(pts[4], pts[5], pts[6]);
508
509 SkMatrix matrix;
510 matrix.reset();
511 p.transform(matrix, &p1);
512 REPORTER_ASSERT(reporter, p == p1);
513
514 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
515 p.transform(matrix, &p1);
516 SkPoint pts1[7];
517 int count = p1.getPoints(pts1, 7);
518 REPORTER_ASSERT(reporter, 7 == count);
519 for (int i = 0; i < count; ++i) {
520 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
521 REPORTER_ASSERT(reporter, newPt == pts1[i]);
522 }
523}
524
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000525static void test_zero_length_paths(skiatest::Reporter* reporter) {
526 SkPath p;
527 SkRect bounds;
528
529 // Lone moveTo case
530 p.moveTo(SK_Scalar1, SK_Scalar1);
531 bounds.set(0, 0, 0, 0);
532 REPORTER_ASSERT(reporter, !p.isEmpty());
533 REPORTER_ASSERT(reporter, 1 == p.countPoints());
534 REPORTER_ASSERT(reporter, bounds == p.getBounds());
535
536 // MoveTo-MoveTo case
537 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org32879492011-12-20 15:33:11 +0000538 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000539 REPORTER_ASSERT(reporter, !p.isEmpty());
540 REPORTER_ASSERT(reporter, 2 == p.countPoints());
541 REPORTER_ASSERT(reporter, bounds == p.getBounds());
542
543 // moveTo-close case
544 p.reset();
545 p.moveTo(SK_Scalar1, SK_Scalar1);
546 p.close();
547 bounds.set(0, 0, 0, 0);
548 REPORTER_ASSERT(reporter, !p.isEmpty());
549 REPORTER_ASSERT(reporter, 1 == p.countPoints());
550 REPORTER_ASSERT(reporter, bounds == p.getBounds());
551
552 // moveTo-close-moveTo-close case
553 p.moveTo(SK_Scalar1*2, SK_Scalar1);
554 p.close();
schenney@chromium.org32879492011-12-20 15:33:11 +0000555 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000556 REPORTER_ASSERT(reporter, !p.isEmpty());
557 REPORTER_ASSERT(reporter, 2 == p.countPoints());
558 REPORTER_ASSERT(reporter, bounds == p.getBounds());
559
560 // moveTo-line case
561 p.reset();
562 p.moveTo(SK_Scalar1, SK_Scalar1);
563 p.lineTo(SK_Scalar1, SK_Scalar1);
564 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
565 REPORTER_ASSERT(reporter, !p.isEmpty());
566 REPORTER_ASSERT(reporter, 2 == p.countPoints());
567 REPORTER_ASSERT(reporter, bounds == p.getBounds());
568
569 // moveTo-lineTo-moveTo-lineTo case
570 p.moveTo(SK_Scalar1*2, SK_Scalar1);
571 p.lineTo(SK_Scalar1*2, SK_Scalar1);
572 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
573 REPORTER_ASSERT(reporter, !p.isEmpty());
574 REPORTER_ASSERT(reporter, 4 == p.countPoints());
575 REPORTER_ASSERT(reporter, bounds == p.getBounds());
576
577 // moveTo-line-close case
578 p.reset();
579 p.moveTo(SK_Scalar1, SK_Scalar1);
580 p.lineTo(SK_Scalar1, SK_Scalar1);
581 p.close();
582 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
583 REPORTER_ASSERT(reporter, !p.isEmpty());
584 REPORTER_ASSERT(reporter, 2 == p.countPoints());
585 REPORTER_ASSERT(reporter, bounds == p.getBounds());
586
587 // moveTo-line-close-moveTo-line-close case
588 p.moveTo(SK_Scalar1*2, SK_Scalar1);
589 p.lineTo(SK_Scalar1*2, SK_Scalar1);
590 p.close();
591 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
592 REPORTER_ASSERT(reporter, !p.isEmpty());
593 REPORTER_ASSERT(reporter, 4 == p.countPoints());
594 REPORTER_ASSERT(reporter, bounds == p.getBounds());
595
596 // moveTo-quadTo case
597 p.reset();
598 p.moveTo(SK_Scalar1, SK_Scalar1);
599 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
600 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
601 REPORTER_ASSERT(reporter, !p.isEmpty());
602 REPORTER_ASSERT(reporter, 3 == p.countPoints());
603 REPORTER_ASSERT(reporter, bounds == p.getBounds());
604
605 // moveTo-quadTo-close case
606 p.close();
607 REPORTER_ASSERT(reporter, !p.isEmpty());
608 REPORTER_ASSERT(reporter, 3 == p.countPoints());
609 REPORTER_ASSERT(reporter, bounds == p.getBounds());
610
611 // moveTo-quadTo-moveTo-quadTo case
612 p.reset();
613 p.moveTo(SK_Scalar1, SK_Scalar1);
614 p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
615 p.moveTo(SK_Scalar1*2, SK_Scalar1);
616 p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
617 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
618 REPORTER_ASSERT(reporter, !p.isEmpty());
619 REPORTER_ASSERT(reporter, 6 == p.countPoints());
620 REPORTER_ASSERT(reporter, bounds == p.getBounds());
621
622 // moveTo-cubicTo case
623 p.reset();
624 p.moveTo(SK_Scalar1, SK_Scalar1);
625 p.cubicTo(SK_Scalar1, SK_Scalar1,
626 SK_Scalar1, SK_Scalar1,
627 SK_Scalar1, SK_Scalar1);
628 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, 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-quadTo-close case
634 p.close();
635 REPORTER_ASSERT(reporter, !p.isEmpty());
636 REPORTER_ASSERT(reporter, 4 == p.countPoints());
637 REPORTER_ASSERT(reporter, bounds == p.getBounds());
638
639 // moveTo-quadTo-moveTo-quadTo case
640 p.reset();
641 p.moveTo(SK_Scalar1, SK_Scalar1);
642 p.cubicTo(SK_Scalar1, SK_Scalar1,
643 SK_Scalar1, SK_Scalar1,
644 SK_Scalar1, SK_Scalar1);
645 p.moveTo(SK_Scalar1*2, SK_Scalar1);
646 p.cubicTo(SK_Scalar1*2, SK_Scalar1,
647 SK_Scalar1*2, SK_Scalar1,
648 SK_Scalar1*2, SK_Scalar1);
649 bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
650 REPORTER_ASSERT(reporter, !p.isEmpty());
651 REPORTER_ASSERT(reporter, 8 == p.countPoints());
652 REPORTER_ASSERT(reporter, bounds == p.getBounds());
653}
654
655struct SegmentInfo {
656 SkPath fPath;
657 int fPointCount;
658};
659
reed@google.com10296cc2011-09-21 12:29:05 +0000660#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
661
reed@google.com04863fa2011-05-15 04:08:24 +0000662void TestPath(skiatest::Reporter* reporter);
663void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +0000664 {
665 SkSize size;
666 size.fWidth = 3.4f;
667 size.width();
668 size = SkSize::Make(3,4);
669 SkISize isize = SkISize::Make(3,4);
670 }
671
672 SkTSize<SkScalar>::Make(3,4);
673
reed@android.com3abec1d2009-03-02 05:36:20 +0000674 SkPath p, p2;
675 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +0000676
reed@android.com3abec1d2009-03-02 05:36:20 +0000677 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000678 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +0000679 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +0000680 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +0000681 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
682 REPORTER_ASSERT(reporter, !p.isInverseFillType());
683 REPORTER_ASSERT(reporter, p == p2);
684 REPORTER_ASSERT(reporter, !(p != p2));
685
reed@android.comd252db02009-04-01 18:31:44 +0000686 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +0000687
reed@android.com3abec1d2009-03-02 05:36:20 +0000688 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +0000689
reed@android.com6b82d1a2009-06-03 02:35:01 +0000690 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
691 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +0000692 // we have quads or cubics
693 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000694 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +0000695
reed@android.com6b82d1a2009-06-03 02:35:01 +0000696 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +0000697 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000698 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +0000699
reed@android.com6b82d1a2009-06-03 02:35:01 +0000700 p.addOval(bounds);
701 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000702 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +0000703
reed@android.com6b82d1a2009-06-03 02:35:01 +0000704 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +0000705 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +0000706 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +0000707 // we have only lines
708 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000709 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +0000710
711 REPORTER_ASSERT(reporter, p != p2);
712 REPORTER_ASSERT(reporter, !(p == p2));
713
714 // does getPoints return the right result
715 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
716 SkPoint pts[4];
717 int count = p.getPoints(pts, 4);
718 REPORTER_ASSERT(reporter, count == 4);
719 bounds2.set(pts, 4);
720 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +0000721
reed@android.com3abec1d2009-03-02 05:36:20 +0000722 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
723 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +0000724 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +0000725
reed@android.com3abec1d2009-03-02 05:36:20 +0000726 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +0000727 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +0000728 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
729 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +0000730
reed@android.com3abec1d2009-03-02 05:36:20 +0000731 // now force p to not be a rect
732 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
733 p.addRect(bounds);
734 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +0000735 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +0000736
737 SkPoint pt;
738
739 p.moveTo(SK_Scalar1, 0);
740 p.getLastPt(&pt);
741 REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000742 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +0000743
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000744 test_zero_length_paths(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +0000745 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +0000746 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000747 test_close(reporter);
reed@google.com10296cc2011-09-21 12:29:05 +0000748
749 p.reset();
750 p.moveTo(0, 0);
751 p.quadTo(100, 100, 200, 200);
752 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000753 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +0000754 p.cubicTo(100, 100, 200, 200, 300, 300);
755 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000756 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +0000757 p.reset();
758 p.moveTo(0, 0);
759 p.cubicTo(100, 100, 200, 200, 300, 300);
760 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000761 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com53effc52011-09-21 19:05:12 +0000762
763 test_flattening(reporter);
764 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +0000765 test_bounds(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +0000766}
767
768#include "TestClassDef.h"
769DEFINE_TESTCLASS("Path", PathTestClass, TestPath)