blob: c4ad0a6eb3a33dbb3961436a3c4b8a950b111333 [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
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000018/**
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 */
23static 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
reed@google.com3e71a882012-01-10 18:44:37 +000033static 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[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000056 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +000057 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +000058 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
reed@google.com3e71a882012-01-10 18:44:37 +000059 };
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);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000064 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +000065 }
66
67 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +000068 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +000069 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +000070 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
reed@google.com3e71a882012-01-10 18:44:37 +000071 };
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);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000076 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +000077 }
reed@google.comac8543f2012-01-30 20:51:25 +000078
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);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000084 check_direction(&path, SkPath::kCW_Direction, reporter);
85
reed@google.comac8543f2012-01-30 20:51:25 +000086 path.reset();
87 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
88 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000089 check_direction(&path, SkPath::kCCW_Direction, reporter);
90
bsalomon@google.com6843ac42012-02-17 13:49:03 +000091#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +000092 // triangle with one point really far from the origin.
93 path.reset();
94 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +000095 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
reed@google.com3e71a882012-01-10 18:44:37 +0000100}
101
reed@google.comffdb0182011-11-14 19:29:14 +0000102static 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
110static void test_bounds(skiatest::Reporter* reporter) {
111 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000112 { 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) },
reed@google.comffdb0182011-11-14 19:29:14 +0000116 };
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
reed@google.com55b5f4b2011-09-07 12:23:41 +0000127static 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
143static 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
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000163static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
164 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000165 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000166 SkPoint mv;
167 SkPoint pts[4];
168 SkPath::Verb v;
169 int nMT = 0;
170 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000171 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000172 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
192static 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);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000255
256 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000257}
258
reed@google.com7c424812011-05-15 04:38:34 +0000259static 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
265static void test_convexity2(skiatest::Reporter* reporter) {
266 SkPath pt;
267 pt.moveTo(0, 0);
268 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000269 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000270
271 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000272 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
273 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000274 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000275 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000276
277 SkPath triLeft;
278 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000279 triLeft.lineTo(SK_Scalar1, 0);
280 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000281 triLeft.close();
282 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
283
284 SkPath triRight;
285 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000286 triRight.lineTo(-SK_Scalar1, 0);
287 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000288 triRight.close();
289 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
290
291 SkPath square;
292 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000293 square.lineTo(SK_Scalar1, 0);
294 square.lineTo(SK_Scalar1, SK_Scalar1);
295 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000296 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);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000303 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);
reed@google.com7c424812011-05-15 04:38:34 +0000312 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);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000319 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);
reed@google.com7c424812011-05-15 04:38:34 +0000328 bowTie.close();
329 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
330
331 SkPath spiral;
332 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000333 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);
reed@google.com7c424812011-05-15 04:38:34 +0000339 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000340 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
reed@google.com7c424812011-05-15 04:38:34 +0000341
342 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000343 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);
reed@google.com7c424812011-05-15 04:38:34 +0000348 dent.close();
349 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
350}
351
reed@android.com6b82d1a2009-06-03 02:35:01 +0000352static 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);
reed@google.com62047cf2011-02-07 19:39:09 +0000356
reed@android.com6b82d1a2009-06-03 02:35:01 +0000357 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
reed@google.com04863fa2011-05-15 04:08:24 +0000367static 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
386static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000387 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
388 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
389
390 SkPath path;
391
reed@google.comb54455e2011-05-16 14:16:04 +0000392 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000393 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000394 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000395 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000396 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
397 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000398 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000399 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000400 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000401 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000402 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000403 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000404 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000405
406 static const struct {
407 const char* fPathStr;
408 SkPath::Convexity fExpectedConvexity;
409 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000410 { "", SkPath::kConvex_Convexity },
411 { "0 0", SkPath::kConvex_Convexity },
412 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000413 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000414 { "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
caryclark@google.comf1316942011-07-26 19:54:45 +0000428// Simple isRect test is inline TestPath, below.
429// test_isRect provides more extensive testing.
430static 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
reed@google.com53effc52011-09-21 19:05:12 +0000556static 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
583static 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
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000613static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000614 SkPath p;
615 SkPoint pt;
616 SkRect bounds;
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000617
618 // Lone moveTo case
619 p.moveTo(SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000620 REPORTER_ASSERT(reporter, !p.isEmpty());
621 REPORTER_ASSERT(reporter, 1 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000622 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);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000626 REPORTER_ASSERT(reporter, bounds == p.getBounds());
627
628 // MoveTo-MoveTo case
629 p.moveTo(SK_Scalar1*2, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000630 REPORTER_ASSERT(reporter, !p.isEmpty());
631 REPORTER_ASSERT(reporter, 2 == p.countPoints());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000632 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);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000636 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();
schenney@chromium.org32879492011-12-20 15:33:11 +0000650 bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000651 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
750struct SegmentInfo {
751 SkPath fPath;
752 int fPointCount;
753};
754
reed@google.com10296cc2011-09-21 12:29:05 +0000755#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
756
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000757static 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
773static 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
841static 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
reed@google.comd335d1d2012-01-12 18:17:11 +0000931 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000932 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +0000933
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000934 for (int i = 0; i < 500; ++i) {
935 p.reset();
936 bool lastWasClose = true;
937 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +0000938 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000939 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);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000946 switch (nextVerb) {
947 case SkPath::kMove_Verb:
948 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
949 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +0000950 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000951 numPoints += 1;
952 lastWasClose = false;
953 haveMoveTo = true;
954 break;
955 case SkPath::kLine_Verb:
956 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000957 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000958 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
959 haveMoveTo = true;
960 }
961 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
962 p.lineTo(expectedPts[numPoints]);
963 numPoints += 1;
964 lastWasClose = false;
965 break;
966 case SkPath::kQuad_Verb:
967 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000968 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000969 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
970 haveMoveTo = true;
971 }
972 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
973 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
974 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
975 numPoints += 2;
976 lastWasClose = false;
977 break;
978 case SkPath::kCubic_Verb:
979 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +0000980 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000981 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
982 haveMoveTo = true;
983 }
984 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
985 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
986 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
987 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
988 expectedPts[numPoints + 2]);
989 numPoints += 3;
990 lastWasClose = false;
991 break;
992 case SkPath::kClose_Verb:
993 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +0000994 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +0000995 lastWasClose = true;
996 break;
997 default:;
998 }
999 expectedVerbs[numIterVerbs++] = nextVerb;
1000 }
1001
1002 iter.setPath(p);
1003 numVerbs = numIterVerbs;
1004 numIterVerbs = 0;
1005 int numIterPts = 0;
1006 SkPoint lastMoveTo;
1007 SkPoint lastPt;
1008 lastMoveTo.set(0, 0);
1009 lastPt.set(0, 0);
1010 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1011 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1012 numIterVerbs++;
1013 switch (nextVerb) {
1014 case SkPath::kMove_Verb:
1015 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1016 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1017 lastPt = lastMoveTo = pts[0];
1018 numIterPts += 1;
1019 break;
1020 case SkPath::kLine_Verb:
1021 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1022 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1023 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1024 lastPt = pts[1];
1025 numIterPts += 1;
1026 break;
1027 case SkPath::kQuad_Verb:
1028 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1029 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1030 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1031 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1032 lastPt = pts[2];
1033 numIterPts += 2;
1034 break;
1035 case SkPath::kCubic_Verb:
1036 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1037 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1038 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1039 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1040 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1041 lastPt = pts[3];
1042 numIterPts += 3;
1043 break;
1044 case SkPath::kClose_Verb:
1045 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1046 lastPt = lastMoveTo;
1047 break;
1048 default:;
1049 }
1050 }
1051 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1052 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1053 }
1054}
1055
reed@google.com04863fa2011-05-15 04:08:24 +00001056void TestPath(skiatest::Reporter* reporter);
1057void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001058 {
1059 SkSize size;
1060 size.fWidth = 3.4f;
1061 size.width();
1062 size = SkSize::Make(3,4);
1063 SkISize isize = SkISize::Make(3,4);
1064 }
1065
1066 SkTSize<SkScalar>::Make(3,4);
1067
reed@android.com3abec1d2009-03-02 05:36:20 +00001068 SkPath p, p2;
1069 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001070
reed@android.com3abec1d2009-03-02 05:36:20 +00001071 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001072 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +00001073 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001074 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001075 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1076 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1077 REPORTER_ASSERT(reporter, p == p2);
1078 REPORTER_ASSERT(reporter, !(p != p2));
1079
reed@android.comd252db02009-04-01 18:31:44 +00001080 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001081
reed@android.com3abec1d2009-03-02 05:36:20 +00001082 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001083
reed@android.com6b82d1a2009-06-03 02:35:01 +00001084 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1085 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001086 // we have quads or cubics
1087 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001088 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001089
reed@android.com6b82d1a2009-06-03 02:35:01 +00001090 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001091 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001092 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001093
reed@android.com6b82d1a2009-06-03 02:35:01 +00001094 p.addOval(bounds);
1095 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001096 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001097
reed@android.com6b82d1a2009-06-03 02:35:01 +00001098 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001099 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001100 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001101 // we have only lines
1102 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001103 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001104
1105 REPORTER_ASSERT(reporter, p != p2);
1106 REPORTER_ASSERT(reporter, !(p == p2));
1107
1108 // does getPoints return the right result
1109 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1110 SkPoint pts[4];
1111 int count = p.getPoints(pts, 4);
1112 REPORTER_ASSERT(reporter, count == 4);
1113 bounds2.set(pts, 4);
1114 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001115
reed@android.com3abec1d2009-03-02 05:36:20 +00001116 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1117 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001118 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001119
reed@android.com3abec1d2009-03-02 05:36:20 +00001120 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001121 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001122 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1123 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001124
reed@android.com3abec1d2009-03-02 05:36:20 +00001125 // now force p to not be a rect
1126 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1127 p.addRect(bounds);
1128 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001129 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001130
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001131 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001132 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001133 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001134 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001135 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001136 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001137 test_flattening(reporter);
1138 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001139 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001140 test_iter(reporter);
1141 test_raw_iter(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001142}
1143
1144#include "TestClassDef.h"
1145DEFINE_TESTCLASS("Path", PathTestClass, TestPath)