blob: cfad5d664e048c5c1a616fb756df4cd683efd739 [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.com8cae8352012-09-14 15:18:41 +00009#include "SkCanvas.h"
reed@google.com55b5f4b2011-09-07 12:23:41 +000010#include "SkPaint.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000011#include "SkPath.h"
reed@google.com04863fa2011-05-15 04:08:24 +000012#include "SkParse.h"
reed@google.com3e71a882012-01-10 18:44:37 +000013#include "SkParsePath.h"
reed@google.com8b06f1a2012-05-29 12:03:46 +000014#include "SkPathEffect.h"
schenney@chromium.org6630d8d2012-01-04 21:05:51 +000015#include "SkRandom.h"
reed@google.com53effc52011-09-21 19:05:12 +000016#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000017#include "SkSize.h"
reed@google.com53effc52011-09-21 19:05:12 +000018#include "SkWriter32.h"
reed@google.com8cae8352012-09-14 15:18:41 +000019#include "SkSurface.h"
20
21static SkSurface* new_surface(int w, int h) {
22 SkImage::Info info = {
23 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
24 };
25 return SkSurface::NewRaster(info, NULL);
26}
27
28// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
29// which triggered an assert, from a tricky cubic. This test replicates that
30// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
31// assert in the SK_DEBUG build.
32static void test_tricky_cubic(skiatest::Reporter* reporter) {
33 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000034 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
35 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
36 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
37 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +000038 };
39
40 SkPath path;
41 path.moveTo(pts[0]);
42 path.cubicTo(pts[1], pts[2], pts[3]);
43
44 SkPaint paint;
45 paint.setAntiAlias(true);
46
47 SkSurface* surface = new_surface(19, 130);
48 surface->getCanvas()->drawPath(path, paint);
49 surface->unref();
50}
reed@android.com3abec1d2009-03-02 05:36:20 +000051
tomhudson@google.comed02c4d2012-08-10 14:10:45 +000052// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
53//
54static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
55 SkPath path;
56 path.quadTo(157, 366, 286, 208);
57 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +000058
tomhudson@google.comed02c4d2012-08-10 14:10:45 +000059 SkMatrix matrix;
60 matrix.setScale(1000*1000, 1000*1000);
61
62 // Be sure that path::transform correctly updates isFinite and the bounds
63 // if the transformation overflows. The previous bug was that isFinite was
64 // set to true in this case, but the bounds were not set to empty (which
65 // they should be).
66 while (path.isFinite()) {
67 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
68 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
69 path.transform(matrix);
70 }
71 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
72
73 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
74 path.transform(matrix);
75 // we need to still be non-finite
76 REPORTER_ASSERT(reporter, !path.isFinite());
77 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
78}
79
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +000080static void add_corner_arc(SkPath* path, const SkRect& rect,
81 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +000082 int startAngle)
83{
84
85 SkScalar rx = SkMinScalar(rect.width(), xIn);
86 SkScalar ry = SkMinScalar(rect.height(), yIn);
87
88 SkRect arcRect;
89 arcRect.set(-rx, -ry, rx, ry);
90 switch (startAngle) {
91 case 0:
92 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
93 break;
94 case 90:
95 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
96 break;
97 case 180:
98 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
99 break;
100 case 270:
101 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
102 break;
103 default:
104 break;
105 }
106
107 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
108}
109
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000110static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000111 SkScalar xCorner, SkScalar yCorner) {
112 // we are lazy here and use the same x & y for each corner
113 add_corner_arc(path, r, xCorner, yCorner, 270);
114 add_corner_arc(path, r, xCorner, yCorner, 0);
115 add_corner_arc(path, r, xCorner, yCorner, 90);
116 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000117 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000118}
119
120// Chrome creates its own round rects with each corner possibly being different.
121// Performance will suffer if they are not convex.
122// Note: PathBench::ArbRoundRectBench performs almost exactly
123// the same test (but with drawing)
124static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
125 SkRandom rand;
126 SkRect r;
127
128 for (int i = 0; i < 5000; ++i) {
129
robertphillips@google.com158618e2012-10-23 16:56:56 +0000130 SkScalar size = rand.nextUScalar1() * 30;
131 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000132 continue;
133 }
134 r.fLeft = rand.nextUScalar1() * 300;
135 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000136 r.fRight = r.fLeft + 2 * size;
137 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000138
139 SkPath temp;
140
141 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
142
robertphillips@google.comc7a37c72012-10-19 01:26:18 +0000143#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000144 REPORTER_ASSERT(reporter, temp.isConvex());
robertphillips@google.comc7a37c72012-10-19 01:26:18 +0000145#endif
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000146 }
147}
148
robertphillips@google.com158618e2012-10-23 16:56:56 +0000149// Chrome will sometimes create a 0 radius round rect. The degenerate
150// quads prevent the path from being converted to a rect
151// Note: PathBench::ArbRoundRectBench performs almost exactly
152// the same test (but with drawing)
153static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
154 SkRandom rand;
155 SkRect r;
156
157 for (int i = 0; i < 5000; ++i) {
158
159 SkScalar size = rand.nextUScalar1() * 30;
160 if (size < SK_Scalar1) {
161 continue;
162 }
163 r.fLeft = rand.nextUScalar1() * 300;
164 r.fTop = rand.nextUScalar1() * 300;
165 r.fRight = r.fLeft + 2 * size;
166 r.fBottom = r.fTop + 2 * size;
167
168 SkPath temp;
169
170 make_arb_round_rect(&temp, r, 0, 0);
171
172#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
173 SkRect result;
174 REPORTER_ASSERT(reporter, temp.isRect(&result));
175 REPORTER_ASSERT(reporter, r == result);
176#endif
177 }
178}
179
reed@google.com0bb18bb2012-07-26 15:20:36 +0000180static void test_rect_isfinite(skiatest::Reporter* reporter) {
181 const SkScalar inf = SK_ScalarInfinity;
182 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000183
reed@google.com0bb18bb2012-07-26 15:20:36 +0000184 SkRect r;
185 r.setEmpty();
186 REPORTER_ASSERT(reporter, r.isFinite());
187 r.set(0, 0, inf, -inf);
188 REPORTER_ASSERT(reporter, !r.isFinite());
189 r.set(0, 0, nan, 0);
190 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000191
reed@google.com0bb18bb2012-07-26 15:20:36 +0000192 SkPoint pts[] = {
193 { 0, 0 },
194 { SK_Scalar1, 0 },
195 { 0, SK_Scalar1 },
196 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000197
reed@google.com0bb18bb2012-07-26 15:20:36 +0000198 bool isFine = r.setBoundsCheck(pts, 3);
199 REPORTER_ASSERT(reporter, isFine);
200 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000201
reed@google.com0bb18bb2012-07-26 15:20:36 +0000202 pts[1].set(inf, 0);
203 isFine = r.setBoundsCheck(pts, 3);
204 REPORTER_ASSERT(reporter, !isFine);
205 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000206
reed@google.com0bb18bb2012-07-26 15:20:36 +0000207 pts[1].set(nan, 0);
208 isFine = r.setBoundsCheck(pts, 3);
209 REPORTER_ASSERT(reporter, !isFine);
210 REPORTER_ASSERT(reporter, r.isEmpty());
211}
212
213static void test_path_isfinite(skiatest::Reporter* reporter) {
214 const SkScalar inf = SK_ScalarInfinity;
215 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000216
reed@google.com0bb18bb2012-07-26 15:20:36 +0000217 SkPath path;
218 REPORTER_ASSERT(reporter, path.isFinite());
219
220 path.reset();
221 REPORTER_ASSERT(reporter, path.isFinite());
222
223 path.reset();
224 path.moveTo(SK_Scalar1, 0);
225 REPORTER_ASSERT(reporter, path.isFinite());
226
227 path.reset();
228 path.moveTo(inf, -inf);
229 REPORTER_ASSERT(reporter, !path.isFinite());
230
231 path.reset();
232 path.moveTo(nan, 0);
233 REPORTER_ASSERT(reporter, !path.isFinite());
234}
235
236static void test_isfinite(skiatest::Reporter* reporter) {
237 test_rect_isfinite(reporter);
238 test_path_isfinite(reporter);
239}
240
reed@google.com744faba2012-05-29 19:54:52 +0000241// assert that we always
242// start with a moveTo
243// only have 1 moveTo
244// only have Lines after that
245// end with a single close
246// only have (at most) 1 close
247//
248static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
249 const SkPoint srcPts[], int count, bool expectClose) {
250 SkPath::RawIter iter(path);
251 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000252
253 bool firstTime = true;
254 bool foundClose = false;
255 for (;;) {
256 switch (iter.next(pts)) {
257 case SkPath::kMove_Verb:
258 REPORTER_ASSERT(reporter, firstTime);
259 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
260 srcPts++;
261 firstTime = false;
262 break;
263 case SkPath::kLine_Verb:
264 REPORTER_ASSERT(reporter, !firstTime);
265 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
266 srcPts++;
267 break;
268 case SkPath::kQuad_Verb:
269 REPORTER_ASSERT(reporter, !"unexpected quad verb");
270 break;
271 case SkPath::kCubic_Verb:
272 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
273 break;
274 case SkPath::kClose_Verb:
275 REPORTER_ASSERT(reporter, !firstTime);
276 REPORTER_ASSERT(reporter, !foundClose);
277 REPORTER_ASSERT(reporter, expectClose);
278 foundClose = true;
279 break;
280 case SkPath::kDone_Verb:
281 goto DONE;
282 }
283 }
284DONE:
285 REPORTER_ASSERT(reporter, foundClose == expectClose);
286}
287
288static void test_addPoly(skiatest::Reporter* reporter) {
289 SkPoint pts[32];
290 SkRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000291
reed@google.com744faba2012-05-29 19:54:52 +0000292 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
293 pts[i].fX = rand.nextSScalar1();
294 pts[i].fY = rand.nextSScalar1();
295 }
296
297 for (int doClose = 0; doClose <= 1; ++doClose) {
298 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
299 SkPath path;
300 path.addPoly(pts, count, SkToBool(doClose));
301 test_poly(reporter, path, pts, count, SkToBool(doClose));
302 }
303 }
304}
305
reed@google.com8b06f1a2012-05-29 12:03:46 +0000306static void test_strokerec(skiatest::Reporter* reporter) {
307 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
308 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000309
reed@google.com8b06f1a2012-05-29 12:03:46 +0000310 rec.setHairlineStyle();
311 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000312
reed@google.com8b06f1a2012-05-29 12:03:46 +0000313 rec.setStrokeStyle(SK_Scalar1, false);
314 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000315
reed@google.com8b06f1a2012-05-29 12:03:46 +0000316 rec.setStrokeStyle(SK_Scalar1, true);
317 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000318
reed@google.com8b06f1a2012-05-29 12:03:46 +0000319 rec.setStrokeStyle(0, false);
320 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000321
reed@google.com8b06f1a2012-05-29 12:03:46 +0000322 rec.setStrokeStyle(0, true);
323 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
324}
325
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000326/**
327 * cheapIsDirection can take a shortcut when a path is marked convex.
328 * This function ensures that we always test cheapIsDirection when the path
329 * is flagged with unknown convexity status.
330 */
331static void check_direction(SkPath* path,
332 SkPath::Direction expectedDir,
333 skiatest::Reporter* reporter) {
334 if (SkPath::kConvex_Convexity == path->getConvexity()) {
335 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
336 path->setConvexity(SkPath::kUnknown_Convexity);
337 }
338 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
339}
340
reed@google.com3e71a882012-01-10 18:44:37 +0000341static void test_direction(skiatest::Reporter* reporter) {
342 size_t i;
343 SkPath path;
344 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
345 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
346 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
347
348 static const char* gDegen[] = {
349 "M 10 10",
350 "M 10 10 M 20 20",
351 "M 10 10 L 20 20",
352 "M 10 10 L 10 10 L 10 10",
353 "M 10 10 Q 10 10 10 10",
354 "M 10 10 C 10 10 10 10 10 10",
355 };
356 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
357 path.reset();
358 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
359 REPORTER_ASSERT(reporter, valid);
360 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
361 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000362
reed@google.com3e71a882012-01-10 18:44:37 +0000363 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000364 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000365 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000366 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000367 // rect with top two corners replaced by cubics with identical middle
368 // control points
369 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000370 };
371 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
372 path.reset();
373 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
374 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000375 check_direction(&path, SkPath::kCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000376 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000377
reed@google.com3e71a882012-01-10 18:44:37 +0000378 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000379 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000380 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000381 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000382 // rect with top two corners replaced by cubics with identical middle
383 // control points
384 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10"
reed@google.com3e71a882012-01-10 18:44:37 +0000385 };
386 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
387 path.reset();
388 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
389 REPORTER_ASSERT(reporter, valid);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000390 check_direction(&path, SkPath::kCCW_Direction, reporter);
reed@google.com3e71a882012-01-10 18:44:37 +0000391 }
reed@google.comac8543f2012-01-30 20:51:25 +0000392
393 // Test two donuts, each wound a different direction. Only the outer contour
394 // determines the cheap direction
395 path.reset();
396 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
397 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000398 check_direction(&path, SkPath::kCW_Direction, reporter);
399
reed@google.comac8543f2012-01-30 20:51:25 +0000400 path.reset();
401 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
402 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000403 check_direction(&path, SkPath::kCCW_Direction, reporter);
404
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000405#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000406 // triangle with one point really far from the origin.
407 path.reset();
408 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000409 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
410 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
411 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
412 check_direction(&path, SkPath::kCCW_Direction, reporter);
413#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000414}
415
reed@google.comffdb0182011-11-14 19:29:14 +0000416static void add_rect(SkPath* path, const SkRect& r) {
417 path->moveTo(r.fLeft, r.fTop);
418 path->lineTo(r.fRight, r.fTop);
419 path->lineTo(r.fRight, r.fBottom);
420 path->lineTo(r.fLeft, r.fBottom);
421 path->close();
422}
423
424static void test_bounds(skiatest::Reporter* reporter) {
425 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000426 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
427 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
428 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
429 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000430 };
431
432 SkPath path0, path1;
433 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
434 path0.addRect(rects[i]);
435 add_rect(&path1, rects[i]);
436 }
437
438 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
439}
440
reed@google.com55b5f4b2011-09-07 12:23:41 +0000441static void stroke_cubic(const SkPoint pts[4]) {
442 SkPath path;
443 path.moveTo(pts[0]);
444 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000445
reed@google.com55b5f4b2011-09-07 12:23:41 +0000446 SkPaint paint;
447 paint.setStyle(SkPaint::kStroke_Style);
448 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000449
reed@google.com55b5f4b2011-09-07 12:23:41 +0000450 SkPath fill;
451 paint.getFillPath(path, &fill);
452}
453
454// just ensure this can run w/o any SkASSERTS firing in the debug build
455// we used to assert due to differences in how we determine a degenerate vector
456// but that was fixed with the introduction of SkPoint::CanNormalize
457static void stroke_tiny_cubic() {
458 SkPoint p0[] = {
459 { 372.0f, 92.0f },
460 { 372.0f, 92.0f },
461 { 372.0f, 92.0f },
462 { 372.0f, 92.0f },
463 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000464
reed@google.com55b5f4b2011-09-07 12:23:41 +0000465 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000466
reed@google.com55b5f4b2011-09-07 12:23:41 +0000467 SkPoint p1[] = {
468 { 372.0f, 92.0f },
469 { 372.0007f, 92.000755f },
470 { 371.99927f, 92.003922f },
471 { 371.99826f, 92.003899f },
472 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000473
reed@google.com55b5f4b2011-09-07 12:23:41 +0000474 stroke_cubic(p1);
475}
476
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000477static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
478 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000479 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000480 SkPoint mv;
481 SkPoint pts[4];
482 SkPath::Verb v;
483 int nMT = 0;
484 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000485 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000486 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
487 switch (v) {
488 case SkPath::kMove_Verb:
489 mv = pts[0];
490 ++nMT;
491 break;
492 case SkPath::kClose_Verb:
493 REPORTER_ASSERT(reporter, mv == pts[0]);
494 ++nCL;
495 break;
496 default:
497 break;
498 }
499 }
500 // if we force a close on the interator we should have a close
501 // for every moveTo
502 REPORTER_ASSERT(reporter, !i || nMT == nCL);
503 }
504}
505
506static void test_close(skiatest::Reporter* reporter) {
507 SkPath closePt;
508 closePt.moveTo(0, 0);
509 closePt.close();
510 check_close(reporter, closePt);
511
512 SkPath openPt;
513 openPt.moveTo(0, 0);
514 check_close(reporter, openPt);
515
516 SkPath empty;
517 check_close(reporter, empty);
518 empty.close();
519 check_close(reporter, empty);
520
521 SkPath rect;
522 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
523 check_close(reporter, rect);
524 rect.close();
525 check_close(reporter, rect);
526
527 SkPath quad;
528 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
529 check_close(reporter, quad);
530 quad.close();
531 check_close(reporter, quad);
532
533 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000534 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000535 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
536 check_close(reporter, cubic);
537 cubic.close();
538 check_close(reporter, cubic);
539
540 SkPath line;
541 line.moveTo(SK_Scalar1, SK_Scalar1);
542 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
543 check_close(reporter, line);
544 line.close();
545 check_close(reporter, line);
546
547 SkPath rect2;
548 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
549 rect2.close();
550 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
551 check_close(reporter, rect2);
552 rect2.close();
553 check_close(reporter, rect2);
554
555 SkPath oval3;
556 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
557 oval3.close();
558 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
559 check_close(reporter, oval3);
560 oval3.close();
561 check_close(reporter, oval3);
562
563 SkPath moves;
564 moves.moveTo(SK_Scalar1, SK_Scalar1);
565 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
566 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
567 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
568 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000569
570 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000571}
572
reed@google.com7c424812011-05-15 04:38:34 +0000573static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
574 SkPath::Convexity expected) {
575 SkPath::Convexity c = SkPath::ComputeConvexity(path);
576 REPORTER_ASSERT(reporter, c == expected);
577}
578
579static void test_convexity2(skiatest::Reporter* reporter) {
580 SkPath pt;
581 pt.moveTo(0, 0);
582 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000583 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000584
reed@google.com7c424812011-05-15 04:38:34 +0000585 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000586 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
587 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000588 line.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000589 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000590
reed@google.com7c424812011-05-15 04:38:34 +0000591 SkPath triLeft;
592 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000593 triLeft.lineTo(SK_Scalar1, 0);
594 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000595 triLeft.close();
596 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000597
reed@google.com7c424812011-05-15 04:38:34 +0000598 SkPath triRight;
599 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000600 triRight.lineTo(-SK_Scalar1, 0);
601 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000602 triRight.close();
603 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000604
reed@google.com7c424812011-05-15 04:38:34 +0000605 SkPath square;
606 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000607 square.lineTo(SK_Scalar1, 0);
608 square.lineTo(SK_Scalar1, SK_Scalar1);
609 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000610 square.close();
611 check_convexity(reporter, square, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000612
reed@google.com7c424812011-05-15 04:38:34 +0000613 SkPath redundantSquare;
614 redundantSquare.moveTo(0, 0);
615 redundantSquare.lineTo(0, 0);
616 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000617 redundantSquare.lineTo(SK_Scalar1, 0);
618 redundantSquare.lineTo(SK_Scalar1, 0);
619 redundantSquare.lineTo(SK_Scalar1, 0);
620 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
621 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
622 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
623 redundantSquare.lineTo(0, SK_Scalar1);
624 redundantSquare.lineTo(0, SK_Scalar1);
625 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000626 redundantSquare.close();
627 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000628
reed@google.com7c424812011-05-15 04:38:34 +0000629 SkPath bowTie;
630 bowTie.moveTo(0, 0);
631 bowTie.lineTo(0, 0);
632 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000633 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
634 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
635 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
636 bowTie.lineTo(SK_Scalar1, 0);
637 bowTie.lineTo(SK_Scalar1, 0);
638 bowTie.lineTo(SK_Scalar1, 0);
639 bowTie.lineTo(0, SK_Scalar1);
640 bowTie.lineTo(0, SK_Scalar1);
641 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000642 bowTie.close();
643 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000644
reed@google.com7c424812011-05-15 04:38:34 +0000645 SkPath spiral;
646 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000647 spiral.lineTo(100*SK_Scalar1, 0);
648 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
649 spiral.lineTo(0, 100*SK_Scalar1);
650 spiral.lineTo(0, 50*SK_Scalar1);
651 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
652 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000653 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000654 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000655
reed@google.com7c424812011-05-15 04:38:34 +0000656 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000657 dent.moveTo(0, 0);
658 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
659 dent.lineTo(0, 100*SK_Scalar1);
660 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
661 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000662 dent.close();
663 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
664}
665
reed@android.com6b82d1a2009-06-03 02:35:01 +0000666static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
667 const SkRect& bounds) {
668 REPORTER_ASSERT(reporter, p.isConvex());
669 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000670
reed@android.com6b82d1a2009-06-03 02:35:01 +0000671 SkPath p2(p);
672 REPORTER_ASSERT(reporter, p2.isConvex());
673 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
674
675 SkPath other;
676 other.swap(p2);
677 REPORTER_ASSERT(reporter, other.isConvex());
678 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
679}
680
reed@google.com04863fa2011-05-15 04:08:24 +0000681static void setFromString(SkPath* path, const char str[]) {
682 bool first = true;
683 while (str) {
684 SkScalar x, y;
685 str = SkParse::FindScalar(str, &x);
686 if (NULL == str) {
687 break;
688 }
689 str = SkParse::FindScalar(str, &y);
690 SkASSERT(str);
691 if (first) {
692 path->moveTo(x, y);
693 first = false;
694 } else {
695 path->lineTo(x, y);
696 }
697 }
698}
699
700static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000701 static const SkPath::Convexity C = SkPath::kConcave_Convexity;
702 static const SkPath::Convexity V = SkPath::kConvex_Convexity;
703
704 SkPath path;
705
reed@google.comb54455e2011-05-16 14:16:04 +0000706 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000707 path.addCircle(0, 0, SkIntToScalar(10));
reed@google.com04863fa2011-05-15 04:08:24 +0000708 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.come3543972012-01-10 18:59:22 +0000709 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
reed@google.com04863fa2011-05-15 04:08:24 +0000710 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
711 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000712 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000713 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000714 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.com04863fa2011-05-15 04:08:24 +0000715 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000716 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
reed@google.com04863fa2011-05-15 04:08:24 +0000717 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
reed@google.com3e71a882012-01-10 18:44:37 +0000718 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000719
reed@google.com04863fa2011-05-15 04:08:24 +0000720 static const struct {
721 const char* fPathStr;
722 SkPath::Convexity fExpectedConvexity;
723 } gRec[] = {
reed@google.comb54455e2011-05-16 14:16:04 +0000724 { "", SkPath::kConvex_Convexity },
725 { "0 0", SkPath::kConvex_Convexity },
726 { "0 0 10 10", SkPath::kConvex_Convexity },
reed@google.com85b6e392011-05-15 20:25:17 +0000727 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
reed@google.com04863fa2011-05-15 04:08:24 +0000728 { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
729 { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
730 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
731 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
732 };
733
734 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
735 SkPath path;
736 setFromString(&path, gRec[i].fPathStr);
737 SkPath::Convexity c = SkPath::ComputeConvexity(path);
738 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
739 }
740}
741
reed@google.com7e6c4d12012-05-10 14:05:43 +0000742static void test_isLine(skiatest::Reporter* reporter) {
743 SkPath path;
744 SkPoint pts[2];
745 const SkScalar value = SkIntToScalar(5);
746
747 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000748
reed@google.com7e6c4d12012-05-10 14:05:43 +0000749 // set some non-zero values
750 pts[0].set(value, value);
751 pts[1].set(value, value);
752 REPORTER_ASSERT(reporter, !path.isLine(pts));
753 // check that pts was untouched
754 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
755 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
756
757 const SkScalar moveX = SkIntToScalar(1);
758 const SkScalar moveY = SkIntToScalar(2);
759 SkASSERT(value != moveX && value != moveY);
760
761 path.moveTo(moveX, moveY);
762 REPORTER_ASSERT(reporter, !path.isLine(NULL));
763 REPORTER_ASSERT(reporter, !path.isLine(pts));
764 // check that pts was untouched
765 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
766 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
767
768 const SkScalar lineX = SkIntToScalar(2);
769 const SkScalar lineY = SkIntToScalar(2);
770 SkASSERT(value != lineX && value != lineY);
771
772 path.lineTo(lineX, lineY);
773 REPORTER_ASSERT(reporter, path.isLine(NULL));
774
775 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
776 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
777 REPORTER_ASSERT(reporter, path.isLine(pts));
778 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
779 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
780
781 path.lineTo(0, 0); // too many points/verbs
782 REPORTER_ASSERT(reporter, !path.isLine(NULL));
783 REPORTER_ASSERT(reporter, !path.isLine(pts));
784 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
785 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
786}
787
caryclark@google.comf1316942011-07-26 19:54:45 +0000788// Simple isRect test is inline TestPath, below.
789// test_isRect provides more extensive testing.
790static void test_isRect(skiatest::Reporter* reporter) {
791 // passing tests (all moveTo / lineTo...
792 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
793 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
794 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
795 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
796 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
797 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
798 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
799 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
800 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
801 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
802 {1, 0}, {.5f, 0}};
803 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
804 {0, 1}, {0, .5f}};
805 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
806 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
807 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
rmistry@google.comd6176b02012-08-23 18:14:13 +0000808
caryclark@google.comf1316942011-07-26 19:54:45 +0000809 // failing tests
810 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
811 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
812 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
813 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
814 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
815 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
816 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
817 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
rmistry@google.comd6176b02012-08-23 18:14:13 +0000818
caryclark@google.comf1316942011-07-26 19:54:45 +0000819 // failing, no close
820 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
821 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
822
823 size_t testLen[] = {
824 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
825 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
826 sizeof(rd), sizeof(re),
827 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
828 sizeof(f7), sizeof(f8),
rmistry@google.comd6176b02012-08-23 18:14:13 +0000829 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +0000830 };
831 SkPoint* tests[] = {
832 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
833 f1, f2, f3, f4, f5, f6, f7, f8,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000834 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +0000835 };
836 SkPoint* lastPass = re;
837 SkPoint* lastClose = f8;
838 bool fail = false;
839 bool close = true;
840 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
841 size_t index;
842 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
843 SkPath path;
844 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
845 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
846 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
847 }
848 if (close) {
849 path.close();
850 }
851 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
852 if (tests[testIndex] == lastPass) {
853 fail = true;
854 }
855 if (tests[testIndex] == lastClose) {
856 close = false;
857 }
858 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000859
caryclark@google.comf1316942011-07-26 19:54:45 +0000860 // fail, close then line
861 SkPath path1;
862 path1.moveTo(r1[0].fX, r1[0].fY);
863 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
864 path1.lineTo(r1[index].fX, r1[index].fY);
865 }
866 path1.close();
867 path1.lineTo(1, 0);
868 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000869
caryclark@google.comf1316942011-07-26 19:54:45 +0000870 // fail, move in the middle
871 path1.reset();
872 path1.moveTo(r1[0].fX, r1[0].fY);
873 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
874 if (index == 2) {
875 path1.moveTo(1, .5f);
876 }
877 path1.lineTo(r1[index].fX, r1[index].fY);
878 }
879 path1.close();
880 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
881
882 // fail, move on the edge
883 path1.reset();
884 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
885 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
886 path1.lineTo(r1[index].fX, r1[index].fY);
887 }
888 path1.close();
889 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000890
caryclark@google.comf1316942011-07-26 19:54:45 +0000891 // fail, quad
892 path1.reset();
893 path1.moveTo(r1[0].fX, r1[0].fY);
894 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
895 if (index == 2) {
896 path1.quadTo(1, .5f, 1, .5f);
897 }
898 path1.lineTo(r1[index].fX, r1[index].fY);
899 }
900 path1.close();
901 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000902
caryclark@google.comf1316942011-07-26 19:54:45 +0000903 // fail, cubic
904 path1.reset();
905 path1.moveTo(r1[0].fX, r1[0].fY);
906 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
907 if (index == 2) {
908 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
909 }
910 path1.lineTo(r1[index].fX, r1[index].fY);
911 }
912 path1.close();
913 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
914}
915
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000916static void write_and_read_back(skiatest::Reporter* reporter,
917 const SkPath& p) {
918 SkWriter32 writer(100);
919 writer.writePath(p);
920 size_t size = writer.size();
921 SkAutoMalloc storage(size);
922 writer.flatten(storage.get());
923 SkReader32 reader(storage.get(), size);
924
925 SkPath readBack;
926 REPORTER_ASSERT(reporter, readBack != p);
927 reader.readPath(&readBack);
928 REPORTER_ASSERT(reporter, readBack == p);
929
rmistry@google.comd6176b02012-08-23 18:14:13 +0000930 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000931 p.getConvexityOrUnknown());
932
933 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
934
935 const SkRect& origBounds = p.getBounds();
936 const SkRect& readBackBounds = readBack.getBounds();
937
938 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
939}
940
reed@google.com53effc52011-09-21 19:05:12 +0000941static void test_flattening(skiatest::Reporter* reporter) {
942 SkPath p;
943
944 static const SkPoint pts[] = {
945 { 0, 0 },
946 { SkIntToScalar(10), SkIntToScalar(10) },
947 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
948 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
949 };
950 p.moveTo(pts[0]);
951 p.lineTo(pts[1]);
952 p.quadTo(pts[2], pts[3]);
953 p.cubicTo(pts[4], pts[5], pts[6]);
954
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000955 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000956
957 // create a buffer that should be much larger than the path so we don't
958 // kill our stack if writer goes too far.
959 char buffer[1024];
960 uint32_t size1 = p.writeToMemory(NULL);
961 uint32_t size2 = p.writeToMemory(buffer);
962 REPORTER_ASSERT(reporter, size1 == size2);
963
964 SkPath p2;
965 uint32_t size3 = p2.readFromMemory(buffer);
966 REPORTER_ASSERT(reporter, size1 == size3);
967 REPORTER_ASSERT(reporter, p == p2);
968
969 char buffer2[1024];
970 size3 = p2.writeToMemory(buffer2);
971 REPORTER_ASSERT(reporter, size1 == size3);
972 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +0000973
974 // test persistence of the oval flag & convexity
975 {
976 SkPath oval;
977 SkRect rect = SkRect::MakeWH(10, 10);
978 oval.addOval(rect);
979
980 write_and_read_back(reporter, oval);
981 }
reed@google.com53effc52011-09-21 19:05:12 +0000982}
983
984static void test_transform(skiatest::Reporter* reporter) {
985 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000986
reed@google.com53effc52011-09-21 19:05:12 +0000987 static const SkPoint pts[] = {
988 { 0, 0 },
989 { SkIntToScalar(10), SkIntToScalar(10) },
990 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
991 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
992 };
993 p.moveTo(pts[0]);
994 p.lineTo(pts[1]);
995 p.quadTo(pts[2], pts[3]);
996 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000997
reed@google.com53effc52011-09-21 19:05:12 +0000998 SkMatrix matrix;
999 matrix.reset();
1000 p.transform(matrix, &p1);
1001 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001002
reed@google.com53effc52011-09-21 19:05:12 +00001003 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1004 p.transform(matrix, &p1);
1005 SkPoint pts1[7];
1006 int count = p1.getPoints(pts1, 7);
1007 REPORTER_ASSERT(reporter, 7 == count);
1008 for (int i = 0; i < count; ++i) {
1009 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1010 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1011 }
1012}
1013
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001014static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001015 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001016 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001017
schenney@chromium.org7e963602012-06-13 17:05:43 +00001018 struct zeroPathTestData {
1019 const char* testPath;
1020 const size_t numResultPts;
1021 const SkRect resultBound;
1022 const SkPath::Verb* resultVerbs;
1023 const size_t numResultVerbs;
1024 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001025
schenney@chromium.org7e963602012-06-13 17:05:43 +00001026 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1027 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1028 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1029 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1030 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1031 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1032 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1033 static const SkPath::Verb resultVerbs8[] = {
1034 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1035 };
1036 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1037 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1038 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1039 static const SkPath::Verb resultVerbs12[] = {
1040 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1041 };
1042 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1043 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1044 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1045 static const SkPath::Verb resultVerbs16[] = {
1046 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1047 };
1048 static const struct zeroPathTestData gZeroLengthTests[] = {
1049 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001050 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
schenney@chromium.org7e963602012-06-13 17:05:43 +00001051 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001052 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1053 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1054 { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
1055 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1056 { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
1057 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1058 { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
1059 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1060 { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
1061 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1062 { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
schenney@chromium.org7e963602012-06-13 17:05:43 +00001063 SK_ARRAY_COUNT(resultVerbs14)
1064 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001065 { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
1066 { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
schenney@chromium.org7e963602012-06-13 17:05:43 +00001067 SK_ARRAY_COUNT(resultVerbs16)
1068 }
1069 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001070
schenney@chromium.org7e963602012-06-13 17:05:43 +00001071 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1072 p.reset();
1073 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1074 REPORTER_ASSERT(reporter, valid);
1075 REPORTER_ASSERT(reporter, !p.isEmpty());
1076 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1077 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1078 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1079 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1080 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1081 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001082 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001083}
1084
1085struct SegmentInfo {
1086 SkPath fPath;
1087 int fPointCount;
1088};
1089
reed@google.com10296cc2011-09-21 12:29:05 +00001090#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1091
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001092static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001093 SkPath p, p2;
1094
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001095 p.moveTo(0, 0);
1096 p.quadTo(100, 100, 200, 200);
1097 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1098 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001099 p2 = p;
1100 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001101 p.cubicTo(100, 100, 200, 200, 300, 300);
1102 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1103 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001104 p2 = p;
1105 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1106
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001107 p.reset();
1108 p.moveTo(0, 0);
1109 p.cubicTo(100, 100, 200, 200, 300, 300);
1110 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001111 p2 = p;
1112 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001113
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001114 REPORTER_ASSERT(reporter, !p.isEmpty());
1115}
1116
1117static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001118 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001119 SkPoint pts[4];
1120
1121 // Test an iterator with no path
1122 SkPath::Iter noPathIter;
1123 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001124
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001125 // Test that setting an empty path works
1126 noPathIter.setPath(p, false);
1127 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001128
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001129 // Test that close path makes no difference for an empty path
1130 noPathIter.setPath(p, true);
1131 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001132
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001133 // Test an iterator with an initial empty path
1134 SkPath::Iter iter(p, false);
1135 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1136
1137 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001138 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001139 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1140
rmistry@google.comd6176b02012-08-23 18:14:13 +00001141
schenney@chromium.org7e963602012-06-13 17:05:43 +00001142 struct iterTestData {
1143 const char* testPath;
1144 const bool forceClose;
1145 const bool consumeDegenerates;
1146 const size_t* numResultPtsPerVerb;
1147 const SkPoint* resultPts;
1148 const SkPath::Verb* resultVerbs;
1149 const size_t numResultVerbs;
1150 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001151
schenney@chromium.org7e963602012-06-13 17:05:43 +00001152 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1153 static const SkPath::Verb resultVerbs2[] = {
1154 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1155 };
1156 static const SkPath::Verb resultVerbs3[] = {
1157 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1158 };
1159 static const SkPath::Verb resultVerbs4[] = {
1160 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1161 };
1162 static const SkPath::Verb resultVerbs5[] = {
1163 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1164 };
1165 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001166 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1167 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1168 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1169 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001170 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001171 static const SkPoint resultPts2[] = {
1172 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1173 };
1174 static const SkPoint resultPts3[] = {
1175 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1176 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1177 };
1178 static const SkPoint resultPts4[] = {
1179 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1180 };
1181 static const SkPoint resultPts5[] = {
1182 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1183 };
1184 static const struct iterTestData gIterTests[] = {
1185 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001186 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1187 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.org7e963602012-06-13 17:05:43 +00001188 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1189 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1190 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1191 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001192 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1193 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1194 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1195 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1196 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1197 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
schenney@chromium.org7e963602012-06-13 17:05:43 +00001198 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001199
schenney@chromium.org7e963602012-06-13 17:05:43 +00001200 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1201 p.reset();
1202 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1203 REPORTER_ASSERT(reporter, valid);
1204 iter.setPath(p, gIterTests[i].forceClose);
1205 int j = 0, l = 0;
1206 do {
1207 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1208 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1209 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1210 }
1211 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1212 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1213 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001214
1215 // The GM degeneratesegments.cpp test is more extensive
1216}
1217
1218static void test_raw_iter(skiatest::Reporter* reporter) {
1219 SkPath p;
1220 SkPoint pts[4];
1221
1222 // Test an iterator with no path
1223 SkPath::RawIter noPathIter;
1224 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1225 // Test that setting an empty path works
1226 noPathIter.setPath(p);
1227 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001228
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001229 // Test an iterator with an initial empty path
1230 SkPath::RawIter iter(p);
1231 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1232
1233 // Test that a move-only path returns the move.
1234 p.moveTo(SK_Scalar1, 0);
1235 iter.setPath(p);
1236 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1237 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1238 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1239 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1240
1241 // No matter how many moves we add, we should get them all back
1242 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1243 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1244 iter.setPath(p);
1245 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1246 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1247 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1248 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1249 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1250 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1251 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1252 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1253 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1254 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1255
1256 // Initial close is never ever stored
1257 p.reset();
1258 p.close();
1259 iter.setPath(p);
1260 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1261
1262 // Move/close sequences
1263 p.reset();
1264 p.close(); // Not stored, no purpose
1265 p.moveTo(SK_Scalar1, 0);
1266 p.close();
1267 p.close(); // Not stored, no purpose
1268 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1269 p.close();
1270 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1271 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1272 p.close();
1273 iter.setPath(p);
1274 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1275 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1276 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1277 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1278 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1279 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1280 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1281 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1282 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1283 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1284 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1285 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1286 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1287 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1288 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1289 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1290 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1291 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1292 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1293 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1294 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1295 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1296
1297 // Generate random paths and verify
1298 SkPoint randomPts[25];
1299 for (int i = 0; i < 5; ++i) {
1300 for (int j = 0; j < 5; ++j) {
1301 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1302 }
1303 }
1304
1305 // Max of 10 segments, max 3 points per segment
1306 SkRandom rand(9876543);
1307 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001308 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001309 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001310
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001311 for (int i = 0; i < 500; ++i) {
1312 p.reset();
1313 bool lastWasClose = true;
1314 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001315 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001316 int numPoints = 0;
1317 int numVerbs = (rand.nextU() >> 16) % 10;
1318 int numIterVerbs = 0;
1319 for (int j = 0; j < numVerbs; ++j) {
1320 do {
1321 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1322 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001323 switch (nextVerb) {
1324 case SkPath::kMove_Verb:
1325 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1326 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001327 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001328 numPoints += 1;
1329 lastWasClose = false;
1330 haveMoveTo = true;
1331 break;
1332 case SkPath::kLine_Verb:
1333 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001334 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001335 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1336 haveMoveTo = true;
1337 }
1338 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1339 p.lineTo(expectedPts[numPoints]);
1340 numPoints += 1;
1341 lastWasClose = false;
1342 break;
1343 case SkPath::kQuad_Verb:
1344 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001345 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001346 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1347 haveMoveTo = true;
1348 }
1349 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1350 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1351 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1352 numPoints += 2;
1353 lastWasClose = false;
1354 break;
1355 case SkPath::kCubic_Verb:
1356 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001357 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001358 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1359 haveMoveTo = true;
1360 }
1361 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1362 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1363 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1364 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1365 expectedPts[numPoints + 2]);
1366 numPoints += 3;
1367 lastWasClose = false;
1368 break;
1369 case SkPath::kClose_Verb:
1370 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001371 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001372 lastWasClose = true;
1373 break;
1374 default:;
1375 }
1376 expectedVerbs[numIterVerbs++] = nextVerb;
1377 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001378
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001379 iter.setPath(p);
1380 numVerbs = numIterVerbs;
1381 numIterVerbs = 0;
1382 int numIterPts = 0;
1383 SkPoint lastMoveTo;
1384 SkPoint lastPt;
1385 lastMoveTo.set(0, 0);
1386 lastPt.set(0, 0);
1387 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1388 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1389 numIterVerbs++;
1390 switch (nextVerb) {
1391 case SkPath::kMove_Verb:
1392 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1393 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1394 lastPt = lastMoveTo = pts[0];
1395 numIterPts += 1;
1396 break;
1397 case SkPath::kLine_Verb:
1398 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1399 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1400 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1401 lastPt = pts[1];
1402 numIterPts += 1;
1403 break;
1404 case SkPath::kQuad_Verb:
1405 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1406 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1407 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1408 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1409 lastPt = pts[2];
1410 numIterPts += 2;
1411 break;
1412 case SkPath::kCubic_Verb:
1413 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1414 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1415 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1416 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1417 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1418 lastPt = pts[3];
1419 numIterPts += 3;
1420 break;
1421 case SkPath::kClose_Verb:
1422 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1423 lastPt = lastMoveTo;
1424 break;
1425 default:;
1426 }
1427 }
1428 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1429 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1430 }
1431}
1432
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001433static void check_for_circle(skiatest::Reporter* reporter,
1434 const SkPath& path, bool expected) {
1435 SkRect rect;
1436 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1437 if (expected) {
1438 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1439 }
1440}
1441
1442static void test_circle_skew(skiatest::Reporter* reporter,
1443 const SkPath& path) {
1444 SkPath tmp;
1445
1446 SkMatrix m;
1447 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1448 path.transform(m, &tmp);
1449 check_for_circle(reporter, tmp, false);
1450}
1451
1452static void test_circle_translate(skiatest::Reporter* reporter,
1453 const SkPath& path) {
1454 SkPath tmp;
1455
1456 // translate at small offset
1457 SkMatrix m;
1458 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1459 path.transform(m, &tmp);
1460 check_for_circle(reporter, tmp, true);
1461
1462 tmp.reset();
1463 m.reset();
1464
1465 // translate at a relatively big offset
1466 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1467 path.transform(m, &tmp);
1468 check_for_circle(reporter, tmp, true);
1469}
1470
1471static void test_circle_rotate(skiatest::Reporter* reporter,
1472 const SkPath& path) {
1473 for (int angle = 0; angle < 360; ++angle) {
1474 SkPath tmp;
1475 SkMatrix m;
1476 m.setRotate(SkIntToScalar(angle));
1477 path.transform(m, &tmp);
1478
1479 // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1480 // degrees is not an oval anymore, this can be improved. we made this
1481 // for the simplicity of our implementation.
1482 if (angle % 90 == 0) {
1483 check_for_circle(reporter, tmp, true);
1484 } else {
1485 check_for_circle(reporter, tmp, false);
1486 }
1487 }
1488}
1489
1490static void test_circle_with_direction(skiatest::Reporter* reporter,
1491 SkPath::Direction dir) {
1492 SkPath path;
1493
1494 // circle at origin
1495 path.addCircle(0, 0, SkIntToScalar(20), dir);
1496 check_for_circle(reporter, path, true);
1497 test_circle_rotate(reporter, path);
1498 test_circle_translate(reporter, path);
1499 test_circle_skew(reporter, path);
1500
1501 // circle at an offset at (10, 10)
1502 path.reset();
1503 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1504 SkIntToScalar(20), dir);
1505 check_for_circle(reporter, path, true);
1506 test_circle_rotate(reporter, path);
1507 test_circle_translate(reporter, path);
1508 test_circle_skew(reporter, path);
1509}
1510
1511static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1512 SkPath path;
1513 SkPath circle;
1514 SkPath rect;
1515 SkPath empty;
1516
1517 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1518 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1519 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1520
1521 SkMatrix translate;
1522 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1523
1524 // For simplicity, all the path concatenation related operations
1525 // would mark it non-circle, though in theory it's still a circle.
1526
1527 // empty + circle (translate)
1528 path = empty;
1529 path.addPath(circle, translate);
1530 check_for_circle(reporter, path, false);
1531
1532 // circle + empty (translate)
1533 path = circle;
1534 path.addPath(empty, translate);
1535 check_for_circle(reporter, path, false);
1536
1537 // test reverseAddPath
1538 path = circle;
1539 path.reverseAddPath(rect);
1540 check_for_circle(reporter, path, false);
1541}
1542
1543static void test_circle(skiatest::Reporter* reporter) {
1544 test_circle_with_direction(reporter, SkPath::kCW_Direction);
1545 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1546
1547 // multiple addCircle()
1548 SkPath path;
1549 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1550 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1551 check_for_circle(reporter, path, false);
1552
1553 // some extra lineTo() would make isOval() fail
1554 path.reset();
1555 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1556 path.lineTo(0, 0);
1557 check_for_circle(reporter, path, false);
1558
1559 // not back to the original point
1560 path.reset();
1561 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1562 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1563 check_for_circle(reporter, path, false);
1564
1565 test_circle_with_add_paths(reporter);
1566}
1567
1568static void test_oval(skiatest::Reporter* reporter) {
1569 SkRect rect;
1570 SkMatrix m;
1571 SkPath path;
1572
1573 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1574 path.addOval(rect);
1575
1576 REPORTER_ASSERT(reporter, path.isOval(NULL));
1577
1578 m.setRotate(SkIntToScalar(90));
1579 SkPath tmp;
1580 path.transform(m, &tmp);
1581 // an oval rotated 90 degrees is still an oval.
1582 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1583
1584 m.reset();
1585 m.setRotate(SkIntToScalar(30));
1586 tmp.reset();
1587 path.transform(m, &tmp);
1588 // an oval rotated 30 degrees is not an oval anymore.
1589 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1590
1591 // since empty path being transformed.
1592 path.reset();
1593 tmp.reset();
1594 m.reset();
1595 path.transform(m, &tmp);
1596 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1597
1598 // empty path is not an oval
1599 tmp.reset();
1600 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1601
1602 // only has moveTo()s
1603 tmp.reset();
1604 tmp.moveTo(0, 0);
1605 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1606 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1607
1608 // mimic WebKit's calling convention,
1609 // call moveTo() first and then call addOval()
1610 path.reset();
1611 path.moveTo(0, 0);
1612 path.addOval(rect);
1613 REPORTER_ASSERT(reporter, path.isOval(NULL));
1614
1615 // copy path
1616 path.reset();
1617 tmp.reset();
1618 tmp.addOval(rect);
1619 path = tmp;
1620 REPORTER_ASSERT(reporter, path.isOval(NULL));
1621}
1622
caryclark@google.com42639cd2012-06-06 12:03:39 +00001623static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00001624 SkTSize<SkScalar>::Make(3,4);
1625
reed@android.com3abec1d2009-03-02 05:36:20 +00001626 SkPath p, p2;
1627 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00001628
reed@android.com3abec1d2009-03-02 05:36:20 +00001629 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001630 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001631 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00001632 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00001633 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00001634 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1635 REPORTER_ASSERT(reporter, !p.isInverseFillType());
1636 REPORTER_ASSERT(reporter, p == p2);
1637 REPORTER_ASSERT(reporter, !(p != p2));
1638
reed@android.comd252db02009-04-01 18:31:44 +00001639 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00001640
reed@android.com3abec1d2009-03-02 05:36:20 +00001641 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001642
reed@android.com6b82d1a2009-06-03 02:35:01 +00001643 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1644 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001645 // we have quads or cubics
1646 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001647 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001648
reed@android.com6b82d1a2009-06-03 02:35:01 +00001649 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00001650 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001651 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00001652
reed@android.com6b82d1a2009-06-03 02:35:01 +00001653 p.addOval(bounds);
1654 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001655 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00001656
reed@android.com6b82d1a2009-06-03 02:35:01 +00001657 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00001658 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00001659 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00001660 // we have only lines
1661 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001662 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00001663
1664 REPORTER_ASSERT(reporter, p != p2);
1665 REPORTER_ASSERT(reporter, !(p == p2));
1666
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001667 // do getPoints and getVerbs return the right result
1668 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1669 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00001670 SkPoint pts[4];
1671 int count = p.getPoints(pts, 4);
1672 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001673 uint8_t verbs[6];
1674 verbs[5] = 0xff;
1675 p.getVerbs(verbs, 5);
1676 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1677 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1678 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1679 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1680 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1681 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00001682 bounds2.set(pts, 4);
1683 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001684
reed@android.com3abec1d2009-03-02 05:36:20 +00001685 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1686 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00001687 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00001688
reed@android.com3abec1d2009-03-02 05:36:20 +00001689 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001690 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00001691 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1692 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00001693
reed@android.com3abec1d2009-03-02 05:36:20 +00001694 // now force p to not be a rect
1695 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1696 p.addRect(bounds);
1697 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00001698
reed@google.com7e6c4d12012-05-10 14:05:43 +00001699 test_isLine(reporter);
1700 test_isRect(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001701 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00001702 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00001703 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00001704 test_convexity2(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00001705 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001706 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00001707 test_flattening(reporter);
1708 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00001709 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001710 test_iter(reporter);
1711 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001712 test_circle(reporter);
1713 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00001714 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00001715 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00001716 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00001717 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00001718 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00001719 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00001720 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00001721}
1722
1723#include "TestClassDef.h"
1724DEFINE_TESTCLASS("Path", PathTestClass, TestPath)