blob: b54545487c299d02b88d9bc46c3a9474cc06fb33 [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
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001056static void check_for_circle(skiatest::Reporter* reporter,
1057 const SkPath& path, bool expected) {
1058 SkRect rect;
1059 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1060 if (expected) {
1061 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1062 }
1063}
1064
1065static void test_circle_skew(skiatest::Reporter* reporter,
1066 const SkPath& path) {
1067 SkPath tmp;
1068
1069 SkMatrix m;
1070 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1071 path.transform(m, &tmp);
1072 check_for_circle(reporter, tmp, false);
1073}
1074
1075static void test_circle_translate(skiatest::Reporter* reporter,
1076 const SkPath& path) {
1077 SkPath tmp;
1078
1079 // translate at small offset
1080 SkMatrix m;
1081 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1082 path.transform(m, &tmp);
1083 check_for_circle(reporter, tmp, true);
1084
1085 tmp.reset();
1086 m.reset();
1087
1088 // translate at a relatively big offset
1089 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1090 path.transform(m, &tmp);
1091 check_for_circle(reporter, tmp, true);
1092}
1093
1094static void test_circle_rotate(skiatest::Reporter* reporter,
1095 const SkPath& path) {
1096 for (int angle = 0; angle < 360; ++angle) {
1097 SkPath tmp;
1098 SkMatrix m;
1099 m.setRotate(SkIntToScalar(angle));
1100 path.transform(m, &tmp);
1101
1102 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1103 // degrees is not an oval anymore, this can be improved. we made this
1104 // for the simplicity of our implementation.
1105 if (angle % 90 == 0) {
1106 check_for_circle(reporter, tmp, true);
1107 } else {
1108 check_for_circle(reporter, tmp, false);
1109 }
1110 }
1111}
1112
1113static void test_circle_with_direction(skiatest::Reporter* reporter,
1114 SkPath::Direction dir) {
1115 SkPath path;
1116
1117 // circle at origin
1118 path.addCircle(0, 0, SkIntToScalar(20), dir);
1119 check_for_circle(reporter, path, true);
1120 test_circle_rotate(reporter, path);
1121 test_circle_translate(reporter, path);
1122 test_circle_skew(reporter, path);
1123
1124 // circle at an offset at (10, 10)
1125 path.reset();
1126 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1127 SkIntToScalar(20), dir);
1128 check_for_circle(reporter, path, true);
1129 test_circle_rotate(reporter, path);
1130 test_circle_translate(reporter, path);
1131 test_circle_skew(reporter, path);
1132}
1133
1134static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1135 SkPath path;
1136 SkPath circle;
1137 SkPath rect;
1138 SkPath empty;
1139
1140 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1141 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1142 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1143
1144 SkMatrix translate;
1145 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1146
1147 // For simplicity, all the path concatenation related operations
1148 // would mark it non-circle, though in theory it's still a circle.
1149
1150 // empty + circle (translate)
1151 path = empty;
1152 path.addPath(circle, translate);
1153 check_for_circle(reporter, path, false);
1154
1155 // circle + empty (translate)
1156 path = circle;
1157 path.addPath(empty, translate);
1158 check_for_circle(reporter, path, false);
1159
1160 // test reverseAddPath
1161 path = circle;
1162 path.reverseAddPath(rect);
1163 check_for_circle(reporter, path, false);
1164}
1165
1166static void test_circle(skiatest::Reporter* reporter) {
1167 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1168 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1169
1170 // multiple addCircle()
1171 SkPath path;
1172 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1173 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1174 check_for_circle(reporter, path, false);
1175
1176 // some extra lineTo() would make isOval() fail
1177 path.reset();
1178 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1179 path.lineTo(0, 0);
1180 check_for_circle(reporter, path, false);
1181
1182 // not back to the original point
1183 path.reset();
1184 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1185 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1186 check_for_circle(reporter, path, false);
1187
1188 test_circle_with_add_paths(reporter);
1189}
1190
1191static void test_oval(skiatest::Reporter* reporter) {
1192 SkRect rect;
1193 SkMatrix m;
1194 SkPath path;
1195
1196 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1197 path.addOval(rect);
1198
1199 REPORTER_ASSERT(reporter, path.isOval(NULL));
1200
1201 m.setRotate(SkIntToScalar(90));
1202 SkPath tmp;
1203 path.transform(m, &tmp);
1204 // an oval rotated 90 degrees is still an oval.
1205 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1206
1207 m.reset();
1208 m.setRotate(SkIntToScalar(30));
1209 tmp.reset();
1210 path.transform(m, &tmp);
1211 // an oval rotated 30 degrees is not an oval anymore.
1212 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1213
1214 // since empty path being transformed.
1215 path.reset();
1216 tmp.reset();
1217 m.reset();
1218 path.transform(m, &tmp);
1219 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1220
1221 // empty path is not an oval
1222 tmp.reset();
1223 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1224
1225 // only has moveTo()s
1226 tmp.reset();
1227 tmp.moveTo(0, 0);
1228 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1229 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1230
1231 // mimic WebKit's calling convention,
1232 // call moveTo() first and then call addOval()
1233 path.reset();
1234 path.moveTo(0, 0);
1235 path.addOval(rect);
1236 REPORTER_ASSERT(reporter, path.isOval(NULL));
1237
1238 // copy path
1239 path.reset();
1240 tmp.reset();
1241 tmp.addOval(rect);
1242 path = tmp;
1243 REPORTER_ASSERT(reporter, path.isOval(NULL));
1244}
1245
reed@google.com04863fa2011-05-15 04:08:24 +00001246void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001247 {
1248 SkSize size;
1249 size.fWidth = 3.4f;
1250 size.width();
1251 size = SkSize::Make(3,4);
1252 SkISize isize = SkISize::Make(3,4);
1253 }
1254
1255 SkTSize<SkScalar>::Make(3,4);
1256
reed@android.com3abec1d2009-03-02 05:36:20 +00001257 SkPath p, p2;
1258 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001259
reed@android.com3abec1d2009-03-02 05:36:20 +00001260 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001261 REPORTER_ASSERT(reporter, 0 == p.countPoints());
reed@google.com10296cc2011-09-21 12:29:05 +00001262 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001263 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001264 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1265 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1266 REPORTER_ASSERT(reporter, p == p2);
1267 REPORTER_ASSERT(reporter, !(p != p2));
1268
reed@android.comd252db02009-04-01 18:31:44 +00001269 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001270
reed@android.com3abec1d2009-03-02 05:36:20 +00001271 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001272
reed@android.com6b82d1a2009-06-03 02:35:01 +00001273 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1274 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001275 // we have quads or cubics
1276 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001277 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001278
reed@android.com6b82d1a2009-06-03 02:35:01 +00001279 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001280 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001281 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001282
reed@android.com6b82d1a2009-06-03 02:35:01 +00001283 p.addOval(bounds);
1284 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001285 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001286
reed@android.com6b82d1a2009-06-03 02:35:01 +00001287 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001288 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001289 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001290 // we have only lines
1291 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001292 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001293
1294 REPORTER_ASSERT(reporter, p != p2);
1295 REPORTER_ASSERT(reporter, !(p == p2));
1296
1297 // does getPoints return the right result
1298 REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1299 SkPoint pts[4];
1300 int count = p.getPoints(pts, 4);
1301 REPORTER_ASSERT(reporter, count == 4);
1302 bounds2.set(pts, 4);
1303 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001304
reed@android.com3abec1d2009-03-02 05:36:20 +00001305 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1306 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001307 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001308
reed@android.com3abec1d2009-03-02 05:36:20 +00001309 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001310 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001311 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1312 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001313
reed@android.com3abec1d2009-03-02 05:36:20 +00001314 // now force p to not be a rect
1315 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1316 p.addRect(bounds);
1317 REPORTER_ASSERT(reporter, !p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001318 test_isRect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001319
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001320 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001321 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001322 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001323 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001324 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001325 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001326 test_flattening(reporter);
1327 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001328 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001329 test_iter(reporter);
1330 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001331 test_circle(reporter);
1332 test_oval(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001333}
1334
1335#include "TestClassDef.h"
1336DEFINE_TESTCLASS("Path", PathTestClass, TestPath)