blob: a83a0a4fc688af90fcd2c6d9aeb77396f9a968b7 [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
caryclark@google.com56f233a2012-11-19 13:06:06 +000021#if defined(WIN32)
22 #define SUPPRESS_VISIBILITY_WARNING
23#else
24 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
25#endif
26
reed@google.com8cae8352012-09-14 15:18:41 +000027static SkSurface* new_surface(int w, int h) {
28 SkImage::Info info = {
29 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
30 };
mike@reedtribe.orgb9476252012-11-15 02:37:45 +000031 return SkSurface::NewRaster(info);
reed@google.com8cae8352012-09-14 15:18:41 +000032}
33
reed@google.coma8790de2012-10-24 21:04:04 +000034// Make sure we stay non-finite once we get there (unless we reset or rewind).
35static void test_addrect_isfinite(skiatest::Reporter* reporter) {
36 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +000037
reed@google.coma8790de2012-10-24 21:04:04 +000038 path.addRect(SkRect::MakeWH(50, 100));
39 REPORTER_ASSERT(reporter, path.isFinite());
40
41 path.moveTo(0, 0);
42 path.lineTo(SK_ScalarInfinity, 42);
43 REPORTER_ASSERT(reporter, !path.isFinite());
44
45 path.addRect(SkRect::MakeWH(50, 100));
46 REPORTER_ASSERT(reporter, !path.isFinite());
47
48 path.reset();
49 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +000050
reed@google.coma8790de2012-10-24 21:04:04 +000051 path.addRect(SkRect::MakeWH(50, 100));
52 REPORTER_ASSERT(reporter, path.isFinite());
53}
54
reed@google.com8cae8352012-09-14 15:18:41 +000055// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
56// which triggered an assert, from a tricky cubic. This test replicates that
57// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
58// assert in the SK_DEBUG build.
59static void test_tricky_cubic(skiatest::Reporter* reporter) {
60 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000061 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
62 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
63 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
64 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +000065 };
66
67 SkPath path;
68 path.moveTo(pts[0]);
69 path.cubicTo(pts[1], pts[2], pts[3]);
70
71 SkPaint paint;
72 paint.setAntiAlias(true);
73
74 SkSurface* surface = new_surface(19, 130);
75 surface->getCanvas()->drawPath(path, paint);
76 surface->unref();
77}
reed@android.com3abec1d2009-03-02 05:36:20 +000078
tomhudson@google.comed02c4d2012-08-10 14:10:45 +000079// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
80//
81static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
82 SkPath path;
83 path.quadTo(157, 366, 286, 208);
84 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +000085
tomhudson@google.comed02c4d2012-08-10 14:10:45 +000086 SkMatrix matrix;
87 matrix.setScale(1000*1000, 1000*1000);
88
89 // Be sure that path::transform correctly updates isFinite and the bounds
90 // if the transformation overflows. The previous bug was that isFinite was
91 // set to true in this case, but the bounds were not set to empty (which
92 // they should be).
93 while (path.isFinite()) {
94 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
95 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
96 path.transform(matrix);
97 }
98 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
99
100 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
101 path.transform(matrix);
102 // we need to still be non-finite
103 REPORTER_ASSERT(reporter, !path.isFinite());
104 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
105}
106
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000107static void add_corner_arc(SkPath* path, const SkRect& rect,
108 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000109 int startAngle)
110{
111
112 SkScalar rx = SkMinScalar(rect.width(), xIn);
113 SkScalar ry = SkMinScalar(rect.height(), yIn);
114
115 SkRect arcRect;
116 arcRect.set(-rx, -ry, rx, ry);
117 switch (startAngle) {
118 case 0:
119 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
120 break;
121 case 90:
122 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
123 break;
124 case 180:
125 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
126 break;
127 case 270:
128 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
129 break;
130 default:
131 break;
132 }
133
134 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
135}
136
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000137static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000138 SkScalar xCorner, SkScalar yCorner) {
139 // we are lazy here and use the same x & y for each corner
140 add_corner_arc(path, r, xCorner, yCorner, 270);
141 add_corner_arc(path, r, xCorner, yCorner, 0);
142 add_corner_arc(path, r, xCorner, yCorner, 90);
143 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000144 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000145}
146
147// Chrome creates its own round rects with each corner possibly being different.
148// Performance will suffer if they are not convex.
149// Note: PathBench::ArbRoundRectBench performs almost exactly
150// the same test (but with drawing)
151static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
152 SkRandom rand;
153 SkRect r;
154
155 for (int i = 0; i < 5000; ++i) {
156
robertphillips@google.com158618e2012-10-23 16:56:56 +0000157 SkScalar size = rand.nextUScalar1() * 30;
158 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000159 continue;
160 }
161 r.fLeft = rand.nextUScalar1() * 300;
162 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000163 r.fRight = r.fLeft + 2 * size;
164 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000165
166 SkPath temp;
167
168 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
169
robertphillips@google.comc7a37c72012-10-19 01:26:18 +0000170#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000171 REPORTER_ASSERT(reporter, temp.isConvex());
robertphillips@google.comc7a37c72012-10-19 01:26:18 +0000172#endif
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000173 }
174}
175
robertphillips@google.com158618e2012-10-23 16:56:56 +0000176// Chrome will sometimes create a 0 radius round rect. The degenerate
177// quads prevent the path from being converted to a rect
178// Note: PathBench::ArbRoundRectBench performs almost exactly
179// the same test (but with drawing)
180static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
181 SkRandom rand;
182 SkRect r;
183
184 for (int i = 0; i < 5000; ++i) {
185
186 SkScalar size = rand.nextUScalar1() * 30;
187 if (size < SK_Scalar1) {
188 continue;
189 }
190 r.fLeft = rand.nextUScalar1() * 300;
191 r.fTop = rand.nextUScalar1() * 300;
192 r.fRight = r.fLeft + 2 * size;
193 r.fBottom = r.fTop + 2 * size;
194
195 SkPath temp;
196
197 make_arb_round_rect(&temp, r, 0, 0);
198
199#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
200 SkRect result;
201 REPORTER_ASSERT(reporter, temp.isRect(&result));
202 REPORTER_ASSERT(reporter, r == result);
203#endif
204 }
205}
206
reed@google.com0bb18bb2012-07-26 15:20:36 +0000207static void test_rect_isfinite(skiatest::Reporter* reporter) {
208 const SkScalar inf = SK_ScalarInfinity;
209 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000210
reed@google.com0bb18bb2012-07-26 15:20:36 +0000211 SkRect r;
212 r.setEmpty();
213 REPORTER_ASSERT(reporter, r.isFinite());
214 r.set(0, 0, inf, -inf);
215 REPORTER_ASSERT(reporter, !r.isFinite());
216 r.set(0, 0, nan, 0);
217 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000218
reed@google.com0bb18bb2012-07-26 15:20:36 +0000219 SkPoint pts[] = {
220 { 0, 0 },
221 { SK_Scalar1, 0 },
222 { 0, SK_Scalar1 },
223 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000224
reed@google.com0bb18bb2012-07-26 15:20:36 +0000225 bool isFine = r.setBoundsCheck(pts, 3);
226 REPORTER_ASSERT(reporter, isFine);
227 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000228
reed@google.com0bb18bb2012-07-26 15:20:36 +0000229 pts[1].set(inf, 0);
230 isFine = r.setBoundsCheck(pts, 3);
231 REPORTER_ASSERT(reporter, !isFine);
232 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000233
reed@google.com0bb18bb2012-07-26 15:20:36 +0000234 pts[1].set(nan, 0);
235 isFine = r.setBoundsCheck(pts, 3);
236 REPORTER_ASSERT(reporter, !isFine);
237 REPORTER_ASSERT(reporter, r.isEmpty());
238}
239
240static void test_path_isfinite(skiatest::Reporter* reporter) {
241 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000242 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000243 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000244
reed@google.com0bb18bb2012-07-26 15:20:36 +0000245 SkPath path;
246 REPORTER_ASSERT(reporter, path.isFinite());
247
248 path.reset();
249 REPORTER_ASSERT(reporter, path.isFinite());
250
251 path.reset();
252 path.moveTo(SK_Scalar1, 0);
253 REPORTER_ASSERT(reporter, path.isFinite());
254
255 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000256 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000257 REPORTER_ASSERT(reporter, !path.isFinite());
258
259 path.reset();
260 path.moveTo(nan, 0);
261 REPORTER_ASSERT(reporter, !path.isFinite());
262}
263
264static void test_isfinite(skiatest::Reporter* reporter) {
265 test_rect_isfinite(reporter);
266 test_path_isfinite(reporter);
267}
268
reed@google.com744faba2012-05-29 19:54:52 +0000269// assert that we always
270// start with a moveTo
271// only have 1 moveTo
272// only have Lines after that
273// end with a single close
274// only have (at most) 1 close
275//
276static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
277 const SkPoint srcPts[], int count, bool expectClose) {
278 SkPath::RawIter iter(path);
279 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000280
281 bool firstTime = true;
282 bool foundClose = false;
283 for (;;) {
284 switch (iter.next(pts)) {
285 case SkPath::kMove_Verb:
286 REPORTER_ASSERT(reporter, firstTime);
287 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
288 srcPts++;
289 firstTime = false;
290 break;
291 case SkPath::kLine_Verb:
292 REPORTER_ASSERT(reporter, !firstTime);
293 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
294 srcPts++;
295 break;
296 case SkPath::kQuad_Verb:
297 REPORTER_ASSERT(reporter, !"unexpected quad verb");
298 break;
299 case SkPath::kCubic_Verb:
300 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
301 break;
302 case SkPath::kClose_Verb:
303 REPORTER_ASSERT(reporter, !firstTime);
304 REPORTER_ASSERT(reporter, !foundClose);
305 REPORTER_ASSERT(reporter, expectClose);
306 foundClose = true;
307 break;
308 case SkPath::kDone_Verb:
309 goto DONE;
310 }
311 }
312DONE:
313 REPORTER_ASSERT(reporter, foundClose == expectClose);
314}
315
316static void test_addPoly(skiatest::Reporter* reporter) {
317 SkPoint pts[32];
318 SkRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000319
reed@google.com744faba2012-05-29 19:54:52 +0000320 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
321 pts[i].fX = rand.nextSScalar1();
322 pts[i].fY = rand.nextSScalar1();
323 }
324
325 for (int doClose = 0; doClose <= 1; ++doClose) {
326 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
327 SkPath path;
328 path.addPoly(pts, count, SkToBool(doClose));
329 test_poly(reporter, path, pts, count, SkToBool(doClose));
330 }
331 }
332}
333
reed@google.com8b06f1a2012-05-29 12:03:46 +0000334static void test_strokerec(skiatest::Reporter* reporter) {
335 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
336 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000337
reed@google.com8b06f1a2012-05-29 12:03:46 +0000338 rec.setHairlineStyle();
339 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000340
reed@google.com8b06f1a2012-05-29 12:03:46 +0000341 rec.setStrokeStyle(SK_Scalar1, false);
342 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000343
reed@google.com8b06f1a2012-05-29 12:03:46 +0000344 rec.setStrokeStyle(SK_Scalar1, true);
345 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000346
reed@google.com8b06f1a2012-05-29 12:03:46 +0000347 rec.setStrokeStyle(0, false);
348 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000349
reed@google.com8b06f1a2012-05-29 12:03:46 +0000350 rec.setStrokeStyle(0, true);
351 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
352}
353
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000354// Set this for paths that don't have a consistent direction such as a bowtie.
355// (cheapComputeDirection is not expected to catch these.)
356static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
357
358static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
359 SkPath::Direction expected) {
360 if (expected == kDontCheckDir) {
361 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000362 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000363 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
364
365 SkPath::Direction dir;
366 if (copy.cheapComputeDirection(&dir)) {
367 REPORTER_ASSERT(reporter, dir == expected);
368 } else {
369 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
370 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000371}
372
reed@google.com3e71a882012-01-10 18:44:37 +0000373static void test_direction(skiatest::Reporter* reporter) {
374 size_t i;
375 SkPath path;
376 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
377 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
378 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000379 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000380
381 static const char* gDegen[] = {
382 "M 10 10",
383 "M 10 10 M 20 20",
384 "M 10 10 L 20 20",
385 "M 10 10 L 10 10 L 10 10",
386 "M 10 10 Q 10 10 10 10",
387 "M 10 10 C 10 10 10 10 10 10",
388 };
389 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
390 path.reset();
391 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
392 REPORTER_ASSERT(reporter, valid);
393 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
394 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000395
reed@google.com3e71a882012-01-10 18:44:37 +0000396 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000397 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000398 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000399 "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 +0000400 // rect with top two corners replaced by cubics with identical middle
401 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000402 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
403 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000404 };
405 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
406 path.reset();
407 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
408 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000409 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000410 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000411
reed@google.com3e71a882012-01-10 18:44:37 +0000412 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000413 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000414 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000415 "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 +0000416 // rect with top two corners replaced by cubics with identical middle
417 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000418 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
419 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000420 };
421 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
422 path.reset();
423 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
424 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000425 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000426 }
reed@google.comac8543f2012-01-30 20:51:25 +0000427
428 // Test two donuts, each wound a different direction. Only the outer contour
429 // determines the cheap direction
430 path.reset();
431 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
432 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000433 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000434
reed@google.comac8543f2012-01-30 20:51:25 +0000435 path.reset();
436 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
437 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000438 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000439
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000440#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000441 // triangle with one point really far from the origin.
442 path.reset();
443 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000444 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
445 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
446 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000447 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000448#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000449}
450
reed@google.comffdb0182011-11-14 19:29:14 +0000451static void add_rect(SkPath* path, const SkRect& r) {
452 path->moveTo(r.fLeft, r.fTop);
453 path->lineTo(r.fRight, r.fTop);
454 path->lineTo(r.fRight, r.fBottom);
455 path->lineTo(r.fLeft, r.fBottom);
456 path->close();
457}
458
459static void test_bounds(skiatest::Reporter* reporter) {
460 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000461 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
462 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
463 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
464 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000465 };
466
467 SkPath path0, path1;
468 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
469 path0.addRect(rects[i]);
470 add_rect(&path1, rects[i]);
471 }
472
473 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
474}
475
reed@google.com55b5f4b2011-09-07 12:23:41 +0000476static void stroke_cubic(const SkPoint pts[4]) {
477 SkPath path;
478 path.moveTo(pts[0]);
479 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000480
reed@google.com55b5f4b2011-09-07 12:23:41 +0000481 SkPaint paint;
482 paint.setStyle(SkPaint::kStroke_Style);
483 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000484
reed@google.com55b5f4b2011-09-07 12:23:41 +0000485 SkPath fill;
486 paint.getFillPath(path, &fill);
487}
488
489// just ensure this can run w/o any SkASSERTS firing in the debug build
490// we used to assert due to differences in how we determine a degenerate vector
491// but that was fixed with the introduction of SkPoint::CanNormalize
492static void stroke_tiny_cubic() {
493 SkPoint p0[] = {
494 { 372.0f, 92.0f },
495 { 372.0f, 92.0f },
496 { 372.0f, 92.0f },
497 { 372.0f, 92.0f },
498 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000499
reed@google.com55b5f4b2011-09-07 12:23:41 +0000500 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000501
reed@google.com55b5f4b2011-09-07 12:23:41 +0000502 SkPoint p1[] = {
503 { 372.0f, 92.0f },
504 { 372.0007f, 92.000755f },
505 { 371.99927f, 92.003922f },
506 { 371.99826f, 92.003899f },
507 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000508
reed@google.com55b5f4b2011-09-07 12:23:41 +0000509 stroke_cubic(p1);
510}
511
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000512static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
513 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000514 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000515 SkPoint mv;
516 SkPoint pts[4];
517 SkPath::Verb v;
518 int nMT = 0;
519 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000520 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000521 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
522 switch (v) {
523 case SkPath::kMove_Verb:
524 mv = pts[0];
525 ++nMT;
526 break;
527 case SkPath::kClose_Verb:
528 REPORTER_ASSERT(reporter, mv == pts[0]);
529 ++nCL;
530 break;
531 default:
532 break;
533 }
534 }
535 // if we force a close on the interator we should have a close
536 // for every moveTo
537 REPORTER_ASSERT(reporter, !i || nMT == nCL);
538 }
539}
540
541static void test_close(skiatest::Reporter* reporter) {
542 SkPath closePt;
543 closePt.moveTo(0, 0);
544 closePt.close();
545 check_close(reporter, closePt);
546
547 SkPath openPt;
548 openPt.moveTo(0, 0);
549 check_close(reporter, openPt);
550
551 SkPath empty;
552 check_close(reporter, empty);
553 empty.close();
554 check_close(reporter, empty);
555
556 SkPath rect;
557 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
558 check_close(reporter, rect);
559 rect.close();
560 check_close(reporter, rect);
561
562 SkPath quad;
563 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
564 check_close(reporter, quad);
565 quad.close();
566 check_close(reporter, quad);
567
568 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000569 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000570 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
571 check_close(reporter, cubic);
572 cubic.close();
573 check_close(reporter, cubic);
574
575 SkPath line;
576 line.moveTo(SK_Scalar1, SK_Scalar1);
577 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
578 check_close(reporter, line);
579 line.close();
580 check_close(reporter, line);
581
582 SkPath rect2;
583 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
584 rect2.close();
585 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
586 check_close(reporter, rect2);
587 rect2.close();
588 check_close(reporter, rect2);
589
590 SkPath oval3;
591 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
592 oval3.close();
593 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
594 check_close(reporter, oval3);
595 oval3.close();
596 check_close(reporter, oval3);
597
598 SkPath moves;
599 moves.moveTo(SK_Scalar1, SK_Scalar1);
600 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
601 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
602 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
603 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000604
605 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000606}
607
reed@google.com7c424812011-05-15 04:38:34 +0000608static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
609 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000610 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
611 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000612 REPORTER_ASSERT(reporter, c == expected);
613}
614
615static void test_convexity2(skiatest::Reporter* reporter) {
616 SkPath pt;
617 pt.moveTo(0, 0);
618 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000619 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000620 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000621
reed@google.com7c424812011-05-15 04:38:34 +0000622 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000623 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
624 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000625 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000626 check_convexity(reporter, line, SkPath::kConvex_Convexity);
627 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000628
reed@google.com7c424812011-05-15 04:38:34 +0000629 SkPath triLeft;
630 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000631 triLeft.lineTo(SK_Scalar1, 0);
632 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000633 triLeft.close();
634 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000635 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000636
reed@google.com7c424812011-05-15 04:38:34 +0000637 SkPath triRight;
638 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000639 triRight.lineTo(-SK_Scalar1, 0);
640 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000641 triRight.close();
642 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000643 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000644
reed@google.com7c424812011-05-15 04:38:34 +0000645 SkPath square;
646 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000647 square.lineTo(SK_Scalar1, 0);
648 square.lineTo(SK_Scalar1, SK_Scalar1);
649 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000650 square.close();
651 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000652 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000653
reed@google.com7c424812011-05-15 04:38:34 +0000654 SkPath redundantSquare;
655 redundantSquare.moveTo(0, 0);
656 redundantSquare.lineTo(0, 0);
657 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000658 redundantSquare.lineTo(SK_Scalar1, 0);
659 redundantSquare.lineTo(SK_Scalar1, 0);
660 redundantSquare.lineTo(SK_Scalar1, 0);
661 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
662 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
663 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
664 redundantSquare.lineTo(0, SK_Scalar1);
665 redundantSquare.lineTo(0, SK_Scalar1);
666 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000667 redundantSquare.close();
668 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000669 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000670
reed@google.com7c424812011-05-15 04:38:34 +0000671 SkPath bowTie;
672 bowTie.moveTo(0, 0);
673 bowTie.lineTo(0, 0);
674 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000675 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
676 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
677 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
678 bowTie.lineTo(SK_Scalar1, 0);
679 bowTie.lineTo(SK_Scalar1, 0);
680 bowTie.lineTo(SK_Scalar1, 0);
681 bowTie.lineTo(0, SK_Scalar1);
682 bowTie.lineTo(0, SK_Scalar1);
683 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000684 bowTie.close();
685 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000686 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000687
reed@google.com7c424812011-05-15 04:38:34 +0000688 SkPath spiral;
689 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000690 spiral.lineTo(100*SK_Scalar1, 0);
691 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
692 spiral.lineTo(0, 100*SK_Scalar1);
693 spiral.lineTo(0, 50*SK_Scalar1);
694 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
695 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000696 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000697 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000698 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000699
reed@google.com7c424812011-05-15 04:38:34 +0000700 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000701 dent.moveTo(0, 0);
702 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
703 dent.lineTo(0, 100*SK_Scalar1);
704 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
705 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000706 dent.close();
707 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000708 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000709}
710
reed@android.com6b82d1a2009-06-03 02:35:01 +0000711static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
712 const SkRect& bounds) {
713 REPORTER_ASSERT(reporter, p.isConvex());
714 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000715
reed@android.com6b82d1a2009-06-03 02:35:01 +0000716 SkPath p2(p);
717 REPORTER_ASSERT(reporter, p2.isConvex());
718 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
719
720 SkPath other;
721 other.swap(p2);
722 REPORTER_ASSERT(reporter, other.isConvex());
723 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
724}
725
reed@google.com04863fa2011-05-15 04:08:24 +0000726static void setFromString(SkPath* path, const char str[]) {
727 bool first = true;
728 while (str) {
729 SkScalar x, y;
730 str = SkParse::FindScalar(str, &x);
731 if (NULL == str) {
732 break;
733 }
734 str = SkParse::FindScalar(str, &y);
735 SkASSERT(str);
736 if (first) {
737 path->moveTo(x, y);
738 first = false;
739 } else {
740 path->lineTo(x, y);
741 }
742 }
743}
744
745static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000746 SkPath path;
747
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000748 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000749 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000750 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000751 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000752 check_convexity(reporter, path, SkPath::kConcave_Convexity);
753
reed@google.com04863fa2011-05-15 04:08:24 +0000754 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000755 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000756 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000757 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000758
reed@google.com04863fa2011-05-15 04:08:24 +0000759 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000760 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000761 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000762 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000763
reed@google.com04863fa2011-05-15 04:08:24 +0000764 static const struct {
765 const char* fPathStr;
766 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000767 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000768 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000769 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
770 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
771 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
772 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
773 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
774 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
775 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
776 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000777 };
778
779 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
780 SkPath path;
781 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000782 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
783 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000784 }
785}
786
reed@google.com7e6c4d12012-05-10 14:05:43 +0000787static void test_isLine(skiatest::Reporter* reporter) {
788 SkPath path;
789 SkPoint pts[2];
790 const SkScalar value = SkIntToScalar(5);
791
792 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000793
reed@google.com7e6c4d12012-05-10 14:05:43 +0000794 // set some non-zero values
795 pts[0].set(value, value);
796 pts[1].set(value, value);
797 REPORTER_ASSERT(reporter, !path.isLine(pts));
798 // check that pts was untouched
799 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
800 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
801
802 const SkScalar moveX = SkIntToScalar(1);
803 const SkScalar moveY = SkIntToScalar(2);
804 SkASSERT(value != moveX && value != moveY);
805
806 path.moveTo(moveX, moveY);
807 REPORTER_ASSERT(reporter, !path.isLine(NULL));
808 REPORTER_ASSERT(reporter, !path.isLine(pts));
809 // check that pts was untouched
810 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
811 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
812
813 const SkScalar lineX = SkIntToScalar(2);
814 const SkScalar lineY = SkIntToScalar(2);
815 SkASSERT(value != lineX && value != lineY);
816
817 path.lineTo(lineX, lineY);
818 REPORTER_ASSERT(reporter, path.isLine(NULL));
819
820 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
821 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
822 REPORTER_ASSERT(reporter, path.isLine(pts));
823 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
824 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
825
826 path.lineTo(0, 0); // too many points/verbs
827 REPORTER_ASSERT(reporter, !path.isLine(NULL));
828 REPORTER_ASSERT(reporter, !path.isLine(pts));
829 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
830 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
831}
832
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000833static void test_conservativelyContains(skiatest::Reporter* reporter) {
834 SkPath path;
835
836 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
837 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
838
839 // A circle that bounds kBaseRect (with a significant amount of slop)
840 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
841 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
842 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
843
844 // round-rect radii
845 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +0000846
caryclark@google.com56f233a2012-11-19 13:06:06 +0000847 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000848 SkRect fQueryRect;
849 bool fInRect;
850 bool fInCircle;
851 bool fInRR;
852 } kQueries[] = {
853 {kBaseRect, true, true, false},
854
855 // rect well inside of kBaseRect
856 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
857 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
858 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
859 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
860 true, true, true},
861
862 // rects with edges off by one from kBaseRect's edges
863 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
864 kBaseRect.width(), kBaseRect.height() + 1),
865 false, true, false},
866 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
867 kBaseRect.width() + 1, kBaseRect.height()),
868 false, true, false},
869 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
870 kBaseRect.width() + 1, kBaseRect.height() + 1),
871 false, true, false},
872 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
873 kBaseRect.width(), kBaseRect.height()),
874 false, true, false},
875 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
876 kBaseRect.width(), kBaseRect.height()),
877 false, true, false},
878 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
879 kBaseRect.width() + 2, kBaseRect.height()),
880 false, true, false},
881 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
882 kBaseRect.width() + 2, kBaseRect.height()),
883 false, true, false},
884
885 // zero-w/h rects at each corner of kBaseRect
886 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
887 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
888 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
889 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
890
891 // far away rect
892 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
893 SkIntToScalar(10), SkIntToScalar(10)),
894 false, false, false},
895
896 // very large rect containing kBaseRect
897 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
898 kBaseRect.fTop - 5 * kBaseRect.height(),
899 11 * kBaseRect.width(), 11 * kBaseRect.height()),
900 false, false, false},
901
902 // skinny rect that spans same y-range as kBaseRect
903 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
904 SkIntToScalar(1), kBaseRect.height()),
905 true, true, true},
906
907 // short rect that spans same x-range as kBaseRect
908 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
909 true, true, true},
910
911 // skinny rect that spans slightly larger y-range than kBaseRect
912 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
913 SkIntToScalar(1), kBaseRect.height() + 1),
914 false, true, false},
915
916 // short rect that spans slightly larger x-range than kBaseRect
917 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
918 kBaseRect.width() + 1, SkScalar(1)),
919 false, true, false},
920 };
921
922 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +0000923 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000924 SkRect qRect = kQueries[q].fQueryRect;
925 if (inv & 0x1) {
926 SkTSwap(qRect.fLeft, qRect.fRight);
927 }
928 if (inv & 0x2) {
929 SkTSwap(qRect.fTop, qRect.fBottom);
930 }
931 for (int d = 0; d < 2; ++d) {
932 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
933 path.reset();
934 path.addRect(kBaseRect, dir);
935 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
936 path.conservativelyContainsRect(qRect));
937
938 path.reset();
939 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
940 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
941 path.conservativelyContainsRect(qRect));
942
943 path.reset();
944 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
945 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
946 path.conservativelyContainsRect(qRect));
947 }
948 // Slightly non-convex shape, shouldn't contain any rects.
949 path.reset();
950 path.moveTo(0, 0);
951 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
952 path.lineTo(SkIntToScalar(100), 0);
953 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
954 path.lineTo(0, SkIntToScalar(100));
955 path.close();
956 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
957 }
958 }
959
960 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
961 path.reset();
962 path.moveTo(0, 0);
963 path.lineTo(SkIntToScalar(100), 0);
964 path.lineTo(0, SkIntToScalar(100));
965
966 // inside, on along top edge
967 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
968 SkIntToScalar(10),
969 SkIntToScalar(10))));
970 // above
971 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
972 SkRect::MakeXYWH(SkIntToScalar(50),
973 SkIntToScalar(-10),
974 SkIntToScalar(10),
975 SkIntToScalar(10))));
976 // to the left
977 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
978 SkIntToScalar(5),
979 SkIntToScalar(5),
980 SkIntToScalar(5))));
981
982 // outside the diagonal edge
983 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
984 SkIntToScalar(200),
985 SkIntToScalar(20),
986 SkIntToScalar(5))));
987}
988
caryclark@google.comf1316942011-07-26 19:54:45 +0000989// Simple isRect test is inline TestPath, below.
990// test_isRect provides more extensive testing.
991static void test_isRect(skiatest::Reporter* reporter) {
992 // passing tests (all moveTo / lineTo...
993 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
994 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
995 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
996 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
997 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
998 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
999 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1000 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1001 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1002 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1003 {1, 0}, {.5f, 0}};
1004 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1005 {0, 1}, {0, .5f}};
1006 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1007 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1008 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001009 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001010
caryclark@google.comf1316942011-07-26 19:54:45 +00001011 // failing tests
1012 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1013 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1014 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1015 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1016 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1017 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1018 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1019 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001020 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1021 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1022 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001023
caryclark@google.comf1316942011-07-26 19:54:45 +00001024 // failing, no close
1025 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1026 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1027
1028 size_t testLen[] = {
1029 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1030 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001031 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001032 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001033 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001034 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001035 };
1036 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001037 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1038 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001039 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001040 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001041 SkPoint* lastPass = rf;
1042 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001043 bool fail = false;
1044 bool close = true;
1045 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1046 size_t index;
1047 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1048 SkPath path;
1049 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1050 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1051 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1052 }
1053 if (close) {
1054 path.close();
1055 }
1056 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001057 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1058
caryclark@google.com56f233a2012-11-19 13:06:06 +00001059 if (!fail) {
1060 SkRect computed, expected;
1061 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1062 REPORTER_ASSERT(reporter, path.isRect(&computed));
1063 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001064
caryclark@google.comf68154a2012-11-21 15:18:06 +00001065 bool isClosed;
1066 SkPath::Direction direction, cheapDirection;
1067 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1068 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1069 REPORTER_ASSERT(reporter, isClosed == close);
1070 REPORTER_ASSERT(reporter, direction == cheapDirection);
1071 } else {
1072 SkRect computed;
1073 computed.set(123, 456, 789, 1011);
1074 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1075 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1076 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1077
1078 bool isClosed = (bool) -1;
1079 SkPath::Direction direction = (SkPath::Direction) -1;
1080 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1081 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1082 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001083 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001084
caryclark@google.comf1316942011-07-26 19:54:45 +00001085 if (tests[testIndex] == lastPass) {
1086 fail = true;
1087 }
1088 if (tests[testIndex] == lastClose) {
1089 close = false;
1090 }
1091 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001092
caryclark@google.comf1316942011-07-26 19:54:45 +00001093 // fail, close then line
1094 SkPath path1;
1095 path1.moveTo(r1[0].fX, r1[0].fY);
1096 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1097 path1.lineTo(r1[index].fX, r1[index].fY);
1098 }
1099 path1.close();
1100 path1.lineTo(1, 0);
1101 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001102
caryclark@google.comf1316942011-07-26 19:54:45 +00001103 // fail, move in the middle
1104 path1.reset();
1105 path1.moveTo(r1[0].fX, r1[0].fY);
1106 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1107 if (index == 2) {
1108 path1.moveTo(1, .5f);
1109 }
1110 path1.lineTo(r1[index].fX, r1[index].fY);
1111 }
1112 path1.close();
1113 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1114
1115 // fail, move on the edge
1116 path1.reset();
1117 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1118 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1119 path1.lineTo(r1[index].fX, r1[index].fY);
1120 }
1121 path1.close();
1122 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001123
caryclark@google.comf1316942011-07-26 19:54:45 +00001124 // fail, quad
1125 path1.reset();
1126 path1.moveTo(r1[0].fX, r1[0].fY);
1127 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1128 if (index == 2) {
1129 path1.quadTo(1, .5f, 1, .5f);
1130 }
1131 path1.lineTo(r1[index].fX, r1[index].fY);
1132 }
1133 path1.close();
1134 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001135
caryclark@google.comf1316942011-07-26 19:54:45 +00001136 // fail, cubic
1137 path1.reset();
1138 path1.moveTo(r1[0].fX, r1[0].fY);
1139 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1140 if (index == 2) {
1141 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1142 }
1143 path1.lineTo(r1[index].fX, r1[index].fY);
1144 }
1145 path1.close();
1146 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1147}
1148
caryclark@google.com56f233a2012-11-19 13:06:06 +00001149static void test_isNestedRects(skiatest::Reporter* reporter) {
1150 // passing tests (all moveTo / lineTo...
1151 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1152 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1153 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1154 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1155 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1156 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1157 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1158 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1159 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1160 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1161 {1, 0}, {.5f, 0}};
1162 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1163 {0, 1}, {0, .5f}};
1164 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1165 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1166 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1167
1168 // failing tests
1169 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1170 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1171 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1172 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1173 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1174 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1175 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1176 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1177
1178 // failing, no close
1179 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1180 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1181
1182 size_t testLen[] = {
1183 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1184 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1185 sizeof(rd), sizeof(re),
1186 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1187 sizeof(f7), sizeof(f8),
1188 sizeof(c1), sizeof(c2)
1189 };
1190 SkPoint* tests[] = {
1191 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1192 f1, f2, f3, f4, f5, f6, f7, f8,
1193 c1, c2
1194 };
1195 const SkPoint* lastPass = re;
1196 const SkPoint* lastClose = f8;
1197 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1198 size_t index;
1199 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1200 bool fail = false;
1201 bool close = true;
1202 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1203 SkPath path;
1204 if (rectFirst) {
1205 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1206 }
1207 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1208 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1209 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1210 }
1211 if (close) {
1212 path.close();
1213 }
1214 if (!rectFirst) {
1215 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1216 }
1217 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1218 if (!fail) {
1219 SkRect expected[2], computed[2];
1220 SkRect testBounds;
1221 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1222 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1223 expected[1] = testBounds;
1224 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1225 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1226 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1227 }
1228 if (tests[testIndex] == lastPass) {
1229 fail = true;
1230 }
1231 if (tests[testIndex] == lastClose) {
1232 close = false;
1233 }
1234 }
1235
1236 // fail, close then line
1237 SkPath path1;
1238 if (rectFirst) {
1239 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1240 }
1241 path1.moveTo(r1[0].fX, r1[0].fY);
1242 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1243 path1.lineTo(r1[index].fX, r1[index].fY);
1244 }
1245 path1.close();
1246 path1.lineTo(1, 0);
1247 if (!rectFirst) {
1248 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1249 }
1250 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1251
1252 // fail, move in the middle
1253 path1.reset();
1254 if (rectFirst) {
1255 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1256 }
1257 path1.moveTo(r1[0].fX, r1[0].fY);
1258 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1259 if (index == 2) {
1260 path1.moveTo(1, .5f);
1261 }
1262 path1.lineTo(r1[index].fX, r1[index].fY);
1263 }
1264 path1.close();
1265 if (!rectFirst) {
1266 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1267 }
1268 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1269
1270 // fail, move on the edge
1271 path1.reset();
1272 if (rectFirst) {
1273 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1274 }
1275 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1276 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1277 path1.lineTo(r1[index].fX, r1[index].fY);
1278 }
1279 path1.close();
1280 if (!rectFirst) {
1281 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1282 }
1283 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1284
1285 // fail, quad
1286 path1.reset();
1287 if (rectFirst) {
1288 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1289 }
1290 path1.moveTo(r1[0].fX, r1[0].fY);
1291 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1292 if (index == 2) {
1293 path1.quadTo(1, .5f, 1, .5f);
1294 }
1295 path1.lineTo(r1[index].fX, r1[index].fY);
1296 }
1297 path1.close();
1298 if (!rectFirst) {
1299 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1300 }
1301 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1302
1303 // fail, cubic
1304 path1.reset();
1305 if (rectFirst) {
1306 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1307 }
1308 path1.moveTo(r1[0].fX, r1[0].fY);
1309 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1310 if (index == 2) {
1311 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1312 }
1313 path1.lineTo(r1[index].fX, r1[index].fY);
1314 }
1315 path1.close();
1316 if (!rectFirst) {
1317 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1318 }
1319 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001320
caryclark@google.com56f233a2012-11-19 13:06:06 +00001321 // fail, not nested
1322 path1.reset();
1323 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1324 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1325 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1326 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001327
1328 // pass, stroke rect
1329 SkPath src, dst;
1330 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1331 SkPaint strokePaint;
1332 strokePaint.setStyle(SkPaint::kStroke_Style);
1333 strokePaint.setStrokeWidth(2);
1334 strokePaint.getFillPath(src, &dst);
1335 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001336}
1337
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001338static void write_and_read_back(skiatest::Reporter* reporter,
1339 const SkPath& p) {
1340 SkWriter32 writer(100);
1341 writer.writePath(p);
1342 size_t size = writer.size();
1343 SkAutoMalloc storage(size);
1344 writer.flatten(storage.get());
1345 SkReader32 reader(storage.get(), size);
1346
1347 SkPath readBack;
1348 REPORTER_ASSERT(reporter, readBack != p);
1349 reader.readPath(&readBack);
1350 REPORTER_ASSERT(reporter, readBack == p);
1351
rmistry@google.comd6176b02012-08-23 18:14:13 +00001352 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001353 p.getConvexityOrUnknown());
1354
1355 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1356
1357 const SkRect& origBounds = p.getBounds();
1358 const SkRect& readBackBounds = readBack.getBounds();
1359
1360 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1361}
1362
reed@google.com53effc52011-09-21 19:05:12 +00001363static void test_flattening(skiatest::Reporter* reporter) {
1364 SkPath p;
1365
1366 static const SkPoint pts[] = {
1367 { 0, 0 },
1368 { SkIntToScalar(10), SkIntToScalar(10) },
1369 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1370 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1371 };
1372 p.moveTo(pts[0]);
1373 p.lineTo(pts[1]);
1374 p.quadTo(pts[2], pts[3]);
1375 p.cubicTo(pts[4], pts[5], pts[6]);
1376
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001377 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001378
1379 // create a buffer that should be much larger than the path so we don't
1380 // kill our stack if writer goes too far.
1381 char buffer[1024];
1382 uint32_t size1 = p.writeToMemory(NULL);
1383 uint32_t size2 = p.writeToMemory(buffer);
1384 REPORTER_ASSERT(reporter, size1 == size2);
1385
1386 SkPath p2;
1387 uint32_t size3 = p2.readFromMemory(buffer);
1388 REPORTER_ASSERT(reporter, size1 == size3);
1389 REPORTER_ASSERT(reporter, p == p2);
1390
1391 char buffer2[1024];
1392 size3 = p2.writeToMemory(buffer2);
1393 REPORTER_ASSERT(reporter, size1 == size3);
1394 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001395
1396 // test persistence of the oval flag & convexity
1397 {
1398 SkPath oval;
1399 SkRect rect = SkRect::MakeWH(10, 10);
1400 oval.addOval(rect);
1401
1402 write_and_read_back(reporter, oval);
1403 }
reed@google.com53effc52011-09-21 19:05:12 +00001404}
1405
1406static void test_transform(skiatest::Reporter* reporter) {
1407 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001408
reed@google.com53effc52011-09-21 19:05:12 +00001409 static const SkPoint pts[] = {
1410 { 0, 0 },
1411 { SkIntToScalar(10), SkIntToScalar(10) },
1412 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1413 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1414 };
1415 p.moveTo(pts[0]);
1416 p.lineTo(pts[1]);
1417 p.quadTo(pts[2], pts[3]);
1418 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001419
reed@google.com53effc52011-09-21 19:05:12 +00001420 SkMatrix matrix;
1421 matrix.reset();
1422 p.transform(matrix, &p1);
1423 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001424
reed@google.com53effc52011-09-21 19:05:12 +00001425 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1426 p.transform(matrix, &p1);
1427 SkPoint pts1[7];
1428 int count = p1.getPoints(pts1, 7);
1429 REPORTER_ASSERT(reporter, 7 == count);
1430 for (int i = 0; i < count; ++i) {
1431 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1432 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1433 }
1434}
1435
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001436static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001437 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001438 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001439
caryclark@google.com56f233a2012-11-19 13:06:06 +00001440 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001441 const char* testPath;
1442 const size_t numResultPts;
1443 const SkRect resultBound;
1444 const SkPath::Verb* resultVerbs;
1445 const size_t numResultVerbs;
1446 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001447
schenney@chromium.org7e963602012-06-13 17:05:43 +00001448 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1449 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1450 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1451 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1452 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1453 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1454 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1455 static const SkPath::Verb resultVerbs8[] = {
1456 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1457 };
1458 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1459 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1460 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1461 static const SkPath::Verb resultVerbs12[] = {
1462 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1463 };
1464 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1465 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1466 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1467 static const SkPath::Verb resultVerbs16[] = {
1468 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1469 };
1470 static const struct zeroPathTestData gZeroLengthTests[] = {
1471 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001472 { "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 +00001473 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001474 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1475 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1476 { "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) },
1477 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1478 { "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) },
1479 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1480 { "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) },
1481 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1482 { "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) },
1483 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1484 { "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 +00001485 SK_ARRAY_COUNT(resultVerbs14)
1486 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001487 { "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) },
1488 { "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 +00001489 SK_ARRAY_COUNT(resultVerbs16)
1490 }
1491 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001492
schenney@chromium.org7e963602012-06-13 17:05:43 +00001493 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1494 p.reset();
1495 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1496 REPORTER_ASSERT(reporter, valid);
1497 REPORTER_ASSERT(reporter, !p.isEmpty());
1498 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1499 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1500 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1501 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1502 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1503 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001504 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001505}
1506
1507struct SegmentInfo {
1508 SkPath fPath;
1509 int fPointCount;
1510};
1511
reed@google.com10296cc2011-09-21 12:29:05 +00001512#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1513
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001514static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001515 SkPath p, p2;
1516
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001517 p.moveTo(0, 0);
1518 p.quadTo(100, 100, 200, 200);
1519 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1520 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001521 p2 = p;
1522 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001523 p.cubicTo(100, 100, 200, 200, 300, 300);
1524 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1525 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001526 p2 = p;
1527 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1528
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001529 p.reset();
1530 p.moveTo(0, 0);
1531 p.cubicTo(100, 100, 200, 200, 300, 300);
1532 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001533 p2 = p;
1534 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001535
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001536 REPORTER_ASSERT(reporter, !p.isEmpty());
1537}
1538
1539static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001540 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001541 SkPoint pts[4];
1542
1543 // Test an iterator with no path
1544 SkPath::Iter noPathIter;
1545 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001546
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001547 // Test that setting an empty path works
1548 noPathIter.setPath(p, false);
1549 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001550
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001551 // Test that close path makes no difference for an empty path
1552 noPathIter.setPath(p, true);
1553 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001554
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001555 // Test an iterator with an initial empty path
1556 SkPath::Iter iter(p, false);
1557 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1558
1559 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001560 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001561 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1562
rmistry@google.comd6176b02012-08-23 18:14:13 +00001563
schenney@chromium.org7e963602012-06-13 17:05:43 +00001564 struct iterTestData {
1565 const char* testPath;
1566 const bool forceClose;
1567 const bool consumeDegenerates;
1568 const size_t* numResultPtsPerVerb;
1569 const SkPoint* resultPts;
1570 const SkPath::Verb* resultVerbs;
1571 const size_t numResultVerbs;
1572 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001573
schenney@chromium.org7e963602012-06-13 17:05:43 +00001574 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1575 static const SkPath::Verb resultVerbs2[] = {
1576 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1577 };
1578 static const SkPath::Verb resultVerbs3[] = {
1579 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1580 };
1581 static const SkPath::Verb resultVerbs4[] = {
1582 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1583 };
1584 static const SkPath::Verb resultVerbs5[] = {
1585 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1586 };
1587 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001588 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1589 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1590 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1591 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001592 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001593 static const SkPoint resultPts2[] = {
1594 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1595 };
1596 static const SkPoint resultPts3[] = {
1597 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1598 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1599 };
1600 static const SkPoint resultPts4[] = {
1601 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1602 };
1603 static const SkPoint resultPts5[] = {
1604 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1605 };
1606 static const struct iterTestData gIterTests[] = {
1607 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001608 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1609 { "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 +00001610 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1611 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1612 { "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) },
1613 { "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 +00001614 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1615 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1616 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1617 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1618 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1619 { "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 +00001620 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001621
schenney@chromium.org7e963602012-06-13 17:05:43 +00001622 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1623 p.reset();
1624 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1625 REPORTER_ASSERT(reporter, valid);
1626 iter.setPath(p, gIterTests[i].forceClose);
1627 int j = 0, l = 0;
1628 do {
1629 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1630 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1631 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1632 }
1633 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1634 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1635 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001636
1637 // The GM degeneratesegments.cpp test is more extensive
1638}
1639
1640static void test_raw_iter(skiatest::Reporter* reporter) {
1641 SkPath p;
1642 SkPoint pts[4];
1643
1644 // Test an iterator with no path
1645 SkPath::RawIter noPathIter;
1646 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1647 // Test that setting an empty path works
1648 noPathIter.setPath(p);
1649 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001650
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001651 // Test an iterator with an initial empty path
1652 SkPath::RawIter iter(p);
1653 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1654
1655 // Test that a move-only path returns the move.
1656 p.moveTo(SK_Scalar1, 0);
1657 iter.setPath(p);
1658 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1659 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1660 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1661 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1662
1663 // No matter how many moves we add, we should get them all back
1664 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1665 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1666 iter.setPath(p);
1667 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1668 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1669 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1670 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1671 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1672 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1673 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1674 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1675 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1676 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1677
1678 // Initial close is never ever stored
1679 p.reset();
1680 p.close();
1681 iter.setPath(p);
1682 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1683
1684 // Move/close sequences
1685 p.reset();
1686 p.close(); // Not stored, no purpose
1687 p.moveTo(SK_Scalar1, 0);
1688 p.close();
1689 p.close(); // Not stored, no purpose
1690 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1691 p.close();
1692 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1693 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1694 p.close();
1695 iter.setPath(p);
1696 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1697 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1698 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1699 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1700 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1701 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1702 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1703 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1704 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1705 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1706 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1707 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1708 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1709 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1710 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1711 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1712 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1713 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1714 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1715 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1716 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1717 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1718
1719 // Generate random paths and verify
1720 SkPoint randomPts[25];
1721 for (int i = 0; i < 5; ++i) {
1722 for (int j = 0; j < 5; ++j) {
1723 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1724 }
1725 }
1726
1727 // Max of 10 segments, max 3 points per segment
1728 SkRandom rand(9876543);
1729 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001730 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001731 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001732
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001733 for (int i = 0; i < 500; ++i) {
1734 p.reset();
1735 bool lastWasClose = true;
1736 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001737 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001738 int numPoints = 0;
1739 int numVerbs = (rand.nextU() >> 16) % 10;
1740 int numIterVerbs = 0;
1741 for (int j = 0; j < numVerbs; ++j) {
1742 do {
1743 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1744 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001745 switch (nextVerb) {
1746 case SkPath::kMove_Verb:
1747 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1748 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001749 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001750 numPoints += 1;
1751 lastWasClose = false;
1752 haveMoveTo = true;
1753 break;
1754 case SkPath::kLine_Verb:
1755 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001756 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001757 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1758 haveMoveTo = true;
1759 }
1760 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1761 p.lineTo(expectedPts[numPoints]);
1762 numPoints += 1;
1763 lastWasClose = false;
1764 break;
1765 case SkPath::kQuad_Verb:
1766 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001767 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001768 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1769 haveMoveTo = true;
1770 }
1771 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1772 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1773 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1774 numPoints += 2;
1775 lastWasClose = false;
1776 break;
1777 case SkPath::kCubic_Verb:
1778 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001779 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001780 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1781 haveMoveTo = true;
1782 }
1783 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1784 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1785 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1786 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1787 expectedPts[numPoints + 2]);
1788 numPoints += 3;
1789 lastWasClose = false;
1790 break;
1791 case SkPath::kClose_Verb:
1792 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001793 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001794 lastWasClose = true;
1795 break;
1796 default:;
1797 }
1798 expectedVerbs[numIterVerbs++] = nextVerb;
1799 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001800
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001801 iter.setPath(p);
1802 numVerbs = numIterVerbs;
1803 numIterVerbs = 0;
1804 int numIterPts = 0;
1805 SkPoint lastMoveTo;
1806 SkPoint lastPt;
1807 lastMoveTo.set(0, 0);
1808 lastPt.set(0, 0);
1809 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1810 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1811 numIterVerbs++;
1812 switch (nextVerb) {
1813 case SkPath::kMove_Verb:
1814 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1815 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1816 lastPt = lastMoveTo = pts[0];
1817 numIterPts += 1;
1818 break;
1819 case SkPath::kLine_Verb:
1820 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1821 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1822 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1823 lastPt = pts[1];
1824 numIterPts += 1;
1825 break;
1826 case SkPath::kQuad_Verb:
1827 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1828 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1829 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1830 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1831 lastPt = pts[2];
1832 numIterPts += 2;
1833 break;
1834 case SkPath::kCubic_Verb:
1835 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1836 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1837 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1838 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1839 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1840 lastPt = pts[3];
1841 numIterPts += 3;
1842 break;
1843 case SkPath::kClose_Verb:
1844 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1845 lastPt = lastMoveTo;
1846 break;
1847 default:;
1848 }
1849 }
1850 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1851 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1852 }
1853}
1854
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001855static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001856 const SkPath& path,
1857 bool expectedCircle,
1858 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001859 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001860 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1861 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001862
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001863 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001864 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1865 }
1866}
1867
1868static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001869 const SkPath& path,
1870 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001871 SkPath tmp;
1872
1873 SkMatrix m;
1874 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1875 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001876 // this matrix reverses the direction.
1877 if (SkPath::kCCW_Direction == dir) {
1878 dir = SkPath::kCW_Direction;
1879 } else {
1880 SkASSERT(SkPath::kCW_Direction == dir);
1881 dir = SkPath::kCCW_Direction;
1882 }
1883 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001884}
1885
1886static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001887 const SkPath& path,
1888 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001889 SkPath tmp;
1890
1891 // translate at small offset
1892 SkMatrix m;
1893 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1894 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001895 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001896
1897 tmp.reset();
1898 m.reset();
1899
1900 // translate at a relatively big offset
1901 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1902 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001903 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001904}
1905
1906static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001907 const SkPath& path,
1908 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001909 for (int angle = 0; angle < 360; ++angle) {
1910 SkPath tmp;
1911 SkMatrix m;
1912 m.setRotate(SkIntToScalar(angle));
1913 path.transform(m, &tmp);
1914
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001915 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001916 // degrees is not an oval anymore, this can be improved. we made this
1917 // for the simplicity of our implementation.
1918 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001919 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001920 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001921 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001922 }
1923 }
1924}
1925
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001926static void test_circle_mirror_x(skiatest::Reporter* reporter,
1927 const SkPath& path,
1928 SkPath::Direction dir) {
1929 SkPath tmp;
1930 SkMatrix m;
1931 m.reset();
1932 m.setScaleX(-SK_Scalar1);
1933 path.transform(m, &tmp);
1934
1935 if (SkPath::kCW_Direction == dir) {
1936 dir = SkPath::kCCW_Direction;
1937 } else {
1938 SkASSERT(SkPath::kCCW_Direction == dir);
1939 dir = SkPath::kCW_Direction;
1940 }
1941
1942 check_for_circle(reporter, tmp, true, dir);
1943}
1944
1945static void test_circle_mirror_y(skiatest::Reporter* reporter,
1946 const SkPath& path,
1947 SkPath::Direction dir) {
1948 SkPath tmp;
1949 SkMatrix m;
1950 m.reset();
1951 m.setScaleY(-SK_Scalar1);
1952 path.transform(m, &tmp);
1953
1954 if (SkPath::kCW_Direction == dir) {
1955 dir = SkPath::kCCW_Direction;
1956 } else {
1957 SkASSERT(SkPath::kCCW_Direction == dir);
1958 dir = SkPath::kCW_Direction;
1959 }
1960
1961 check_for_circle(reporter, tmp, true, dir);
1962}
1963
1964static void test_circle_mirror_xy(skiatest::Reporter* reporter,
1965 const SkPath& path,
1966 SkPath::Direction dir) {
1967 SkPath tmp;
1968 SkMatrix m;
1969 m.reset();
1970 m.setScaleX(-SK_Scalar1);
1971 m.setScaleY(-SK_Scalar1);
1972 path.transform(m, &tmp);
1973
1974 check_for_circle(reporter, tmp, true, dir);
1975}
1976
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001977static void test_circle_with_direction(skiatest::Reporter* reporter,
1978 SkPath::Direction dir) {
1979 SkPath path;
1980
1981 // circle at origin
1982 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001983 check_for_circle(reporter, path, true, dir);
1984 test_circle_rotate(reporter, path, dir);
1985 test_circle_translate(reporter, path, dir);
1986 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001987
1988 // circle at an offset at (10, 10)
1989 path.reset();
1990 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1991 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001992 check_for_circle(reporter, path, true, dir);
1993 test_circle_rotate(reporter, path, dir);
1994 test_circle_translate(reporter, path, dir);
1995 test_circle_skew(reporter, path, dir);
1996 test_circle_mirror_x(reporter, path, dir);
1997 test_circle_mirror_y(reporter, path, dir);
1998 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001999}
2000
2001static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2002 SkPath path;
2003 SkPath circle;
2004 SkPath rect;
2005 SkPath empty;
2006
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002007 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2008 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2009
2010 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002011 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2012 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2013
2014 SkMatrix translate;
2015 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2016
2017 // For simplicity, all the path concatenation related operations
2018 // would mark it non-circle, though in theory it's still a circle.
2019
2020 // empty + circle (translate)
2021 path = empty;
2022 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002023 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002024
2025 // circle + empty (translate)
2026 path = circle;
2027 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002028 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002029
2030 // test reverseAddPath
2031 path = circle;
2032 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002033 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002034}
2035
2036static void test_circle(skiatest::Reporter* reporter) {
2037 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2038 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2039
2040 // multiple addCircle()
2041 SkPath path;
2042 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2043 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002044 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002045
2046 // some extra lineTo() would make isOval() fail
2047 path.reset();
2048 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2049 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002050 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002051
2052 // not back to the original point
2053 path.reset();
2054 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2055 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002056 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002057
2058 test_circle_with_add_paths(reporter);
2059}
2060
2061static void test_oval(skiatest::Reporter* reporter) {
2062 SkRect rect;
2063 SkMatrix m;
2064 SkPath path;
2065
2066 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2067 path.addOval(rect);
2068
2069 REPORTER_ASSERT(reporter, path.isOval(NULL));
2070
2071 m.setRotate(SkIntToScalar(90));
2072 SkPath tmp;
2073 path.transform(m, &tmp);
2074 // an oval rotated 90 degrees is still an oval.
2075 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2076
2077 m.reset();
2078 m.setRotate(SkIntToScalar(30));
2079 tmp.reset();
2080 path.transform(m, &tmp);
2081 // an oval rotated 30 degrees is not an oval anymore.
2082 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2083
2084 // since empty path being transformed.
2085 path.reset();
2086 tmp.reset();
2087 m.reset();
2088 path.transform(m, &tmp);
2089 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2090
2091 // empty path is not an oval
2092 tmp.reset();
2093 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2094
2095 // only has moveTo()s
2096 tmp.reset();
2097 tmp.moveTo(0, 0);
2098 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2099 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2100
2101 // mimic WebKit's calling convention,
2102 // call moveTo() first and then call addOval()
2103 path.reset();
2104 path.moveTo(0, 0);
2105 path.addOval(rect);
2106 REPORTER_ASSERT(reporter, path.isOval(NULL));
2107
2108 // copy path
2109 path.reset();
2110 tmp.reset();
2111 tmp.addOval(rect);
2112 path = tmp;
2113 REPORTER_ASSERT(reporter, path.isOval(NULL));
2114}
2115
caryclark@google.com42639cd2012-06-06 12:03:39 +00002116static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002117 SkTSize<SkScalar>::Make(3,4);
2118
reed@android.com3abec1d2009-03-02 05:36:20 +00002119 SkPath p, p2;
2120 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002121
reed@android.com3abec1d2009-03-02 05:36:20 +00002122 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002123 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002124 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002125 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002126 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002127 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2128 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2129 REPORTER_ASSERT(reporter, p == p2);
2130 REPORTER_ASSERT(reporter, !(p != p2));
2131
reed@android.comd252db02009-04-01 18:31:44 +00002132 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002133
reed@android.com3abec1d2009-03-02 05:36:20 +00002134 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002135
reed@android.com6b82d1a2009-06-03 02:35:01 +00002136 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2137 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002138 // we have quads or cubics
2139 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002140 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002141
reed@android.com6b82d1a2009-06-03 02:35:01 +00002142 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002143 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002144 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002145
reed@android.com6b82d1a2009-06-03 02:35:01 +00002146 p.addOval(bounds);
2147 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002148 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002149
reed@android.com6b82d1a2009-06-03 02:35:01 +00002150 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002151 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002152 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002153 // we have only lines
2154 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002155 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002156
2157 REPORTER_ASSERT(reporter, p != p2);
2158 REPORTER_ASSERT(reporter, !(p == p2));
2159
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002160 // do getPoints and getVerbs return the right result
2161 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2162 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002163 SkPoint pts[4];
2164 int count = p.getPoints(pts, 4);
2165 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002166 uint8_t verbs[6];
2167 verbs[5] = 0xff;
2168 p.getVerbs(verbs, 5);
2169 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2170 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2171 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2172 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2173 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2174 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002175 bounds2.set(pts, 4);
2176 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002177
reed@android.com3abec1d2009-03-02 05:36:20 +00002178 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2179 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002180 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002181
reed@android.com3abec1d2009-03-02 05:36:20 +00002182 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002183 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002184 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2185 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002186
reed@android.com3abec1d2009-03-02 05:36:20 +00002187 // now force p to not be a rect
2188 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2189 p.addRect(bounds);
2190 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002191
reed@google.com7e6c4d12012-05-10 14:05:43 +00002192 test_isLine(reporter);
2193 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002194 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002195 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002196 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002197 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002198 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002199 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002200 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002201 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002202 test_flattening(reporter);
2203 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002204 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002205 test_iter(reporter);
2206 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002207 test_circle(reporter);
2208 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002209 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002210 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002211 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002212 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002213 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002214 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002215 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002216 test_addrect_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002217}
2218
2219#include "TestClassDef.h"
2220DEFINE_TESTCLASS("Path", PathTestClass, TestPath)