blob: 6f9a9e64658939d74c0549aac29c74a590ea778b [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;
242 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000243
reed@google.com0bb18bb2012-07-26 15:20:36 +0000244 SkPath path;
245 REPORTER_ASSERT(reporter, path.isFinite());
246
247 path.reset();
248 REPORTER_ASSERT(reporter, path.isFinite());
249
250 path.reset();
251 path.moveTo(SK_Scalar1, 0);
252 REPORTER_ASSERT(reporter, path.isFinite());
253
254 path.reset();
255 path.moveTo(inf, -inf);
256 REPORTER_ASSERT(reporter, !path.isFinite());
257
258 path.reset();
259 path.moveTo(nan, 0);
260 REPORTER_ASSERT(reporter, !path.isFinite());
261}
262
263static void test_isfinite(skiatest::Reporter* reporter) {
264 test_rect_isfinite(reporter);
265 test_path_isfinite(reporter);
266}
267
reed@google.com744faba2012-05-29 19:54:52 +0000268// assert that we always
269// start with a moveTo
270// only have 1 moveTo
271// only have Lines after that
272// end with a single close
273// only have (at most) 1 close
274//
275static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
276 const SkPoint srcPts[], int count, bool expectClose) {
277 SkPath::RawIter iter(path);
278 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000279
280 bool firstTime = true;
281 bool foundClose = false;
282 for (;;) {
283 switch (iter.next(pts)) {
284 case SkPath::kMove_Verb:
285 REPORTER_ASSERT(reporter, firstTime);
286 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
287 srcPts++;
288 firstTime = false;
289 break;
290 case SkPath::kLine_Verb:
291 REPORTER_ASSERT(reporter, !firstTime);
292 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
293 srcPts++;
294 break;
295 case SkPath::kQuad_Verb:
296 REPORTER_ASSERT(reporter, !"unexpected quad verb");
297 break;
298 case SkPath::kCubic_Verb:
299 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
300 break;
301 case SkPath::kClose_Verb:
302 REPORTER_ASSERT(reporter, !firstTime);
303 REPORTER_ASSERT(reporter, !foundClose);
304 REPORTER_ASSERT(reporter, expectClose);
305 foundClose = true;
306 break;
307 case SkPath::kDone_Verb:
308 goto DONE;
309 }
310 }
311DONE:
312 REPORTER_ASSERT(reporter, foundClose == expectClose);
313}
314
315static void test_addPoly(skiatest::Reporter* reporter) {
316 SkPoint pts[32];
317 SkRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000318
reed@google.com744faba2012-05-29 19:54:52 +0000319 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
320 pts[i].fX = rand.nextSScalar1();
321 pts[i].fY = rand.nextSScalar1();
322 }
323
324 for (int doClose = 0; doClose <= 1; ++doClose) {
325 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
326 SkPath path;
327 path.addPoly(pts, count, SkToBool(doClose));
328 test_poly(reporter, path, pts, count, SkToBool(doClose));
329 }
330 }
331}
332
reed@google.com8b06f1a2012-05-29 12:03:46 +0000333static void test_strokerec(skiatest::Reporter* reporter) {
334 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
335 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000336
reed@google.com8b06f1a2012-05-29 12:03:46 +0000337 rec.setHairlineStyle();
338 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000339
reed@google.com8b06f1a2012-05-29 12:03:46 +0000340 rec.setStrokeStyle(SK_Scalar1, false);
341 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000342
reed@google.com8b06f1a2012-05-29 12:03:46 +0000343 rec.setStrokeStyle(SK_Scalar1, true);
344 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000345
reed@google.com8b06f1a2012-05-29 12:03:46 +0000346 rec.setStrokeStyle(0, false);
347 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000348
reed@google.com8b06f1a2012-05-29 12:03:46 +0000349 rec.setStrokeStyle(0, true);
350 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
351}
352
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000353// Set this for paths that don't have a consistent direction such as a bowtie.
354// (cheapComputeDirection is not expected to catch these.)
355static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
356
357static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
358 SkPath::Direction expected) {
359 if (expected == kDontCheckDir) {
360 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000361 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000362 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
363
364 SkPath::Direction dir;
365 if (copy.cheapComputeDirection(&dir)) {
366 REPORTER_ASSERT(reporter, dir == expected);
367 } else {
368 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
369 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000370}
371
reed@google.com3e71a882012-01-10 18:44:37 +0000372static void test_direction(skiatest::Reporter* reporter) {
373 size_t i;
374 SkPath path;
375 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
376 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
377 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000378 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000379
380 static const char* gDegen[] = {
381 "M 10 10",
382 "M 10 10 M 20 20",
383 "M 10 10 L 20 20",
384 "M 10 10 L 10 10 L 10 10",
385 "M 10 10 Q 10 10 10 10",
386 "M 10 10 C 10 10 10 10 10 10",
387 };
388 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
389 path.reset();
390 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
391 REPORTER_ASSERT(reporter, valid);
392 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
393 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000394
reed@google.com3e71a882012-01-10 18:44:37 +0000395 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000396 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000397 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000398 "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 +0000399 // rect with top two corners replaced by cubics with identical middle
400 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000401 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
402 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000403 };
404 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
405 path.reset();
406 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
407 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000408 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000409 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000410
reed@google.com3e71a882012-01-10 18:44:37 +0000411 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000412 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000413 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000414 "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 +0000415 // rect with top two corners replaced by cubics with identical middle
416 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000417 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
418 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000419 };
420 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
421 path.reset();
422 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
423 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000424 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000425 }
reed@google.comac8543f2012-01-30 20:51:25 +0000426
427 // Test two donuts, each wound a different direction. Only the outer contour
428 // determines the cheap direction
429 path.reset();
430 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
431 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000432 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000433
reed@google.comac8543f2012-01-30 20:51:25 +0000434 path.reset();
435 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
436 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000437 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000438
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000439#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000440 // triangle with one point really far from the origin.
441 path.reset();
442 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000443 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
444 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
445 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000446 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000447#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000448}
449
reed@google.comffdb0182011-11-14 19:29:14 +0000450static void add_rect(SkPath* path, const SkRect& r) {
451 path->moveTo(r.fLeft, r.fTop);
452 path->lineTo(r.fRight, r.fTop);
453 path->lineTo(r.fRight, r.fBottom);
454 path->lineTo(r.fLeft, r.fBottom);
455 path->close();
456}
457
458static void test_bounds(skiatest::Reporter* reporter) {
459 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000460 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
461 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
462 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
463 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000464 };
465
466 SkPath path0, path1;
467 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
468 path0.addRect(rects[i]);
469 add_rect(&path1, rects[i]);
470 }
471
472 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
473}
474
reed@google.com55b5f4b2011-09-07 12:23:41 +0000475static void stroke_cubic(const SkPoint pts[4]) {
476 SkPath path;
477 path.moveTo(pts[0]);
478 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000479
reed@google.com55b5f4b2011-09-07 12:23:41 +0000480 SkPaint paint;
481 paint.setStyle(SkPaint::kStroke_Style);
482 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000483
reed@google.com55b5f4b2011-09-07 12:23:41 +0000484 SkPath fill;
485 paint.getFillPath(path, &fill);
486}
487
488// just ensure this can run w/o any SkASSERTS firing in the debug build
489// we used to assert due to differences in how we determine a degenerate vector
490// but that was fixed with the introduction of SkPoint::CanNormalize
491static void stroke_tiny_cubic() {
492 SkPoint p0[] = {
493 { 372.0f, 92.0f },
494 { 372.0f, 92.0f },
495 { 372.0f, 92.0f },
496 { 372.0f, 92.0f },
497 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000498
reed@google.com55b5f4b2011-09-07 12:23:41 +0000499 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000500
reed@google.com55b5f4b2011-09-07 12:23:41 +0000501 SkPoint p1[] = {
502 { 372.0f, 92.0f },
503 { 372.0007f, 92.000755f },
504 { 371.99927f, 92.003922f },
505 { 371.99826f, 92.003899f },
506 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000507
reed@google.com55b5f4b2011-09-07 12:23:41 +0000508 stroke_cubic(p1);
509}
510
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000511static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
512 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000513 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000514 SkPoint mv;
515 SkPoint pts[4];
516 SkPath::Verb v;
517 int nMT = 0;
518 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000519 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000520 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
521 switch (v) {
522 case SkPath::kMove_Verb:
523 mv = pts[0];
524 ++nMT;
525 break;
526 case SkPath::kClose_Verb:
527 REPORTER_ASSERT(reporter, mv == pts[0]);
528 ++nCL;
529 break;
530 default:
531 break;
532 }
533 }
534 // if we force a close on the interator we should have a close
535 // for every moveTo
536 REPORTER_ASSERT(reporter, !i || nMT == nCL);
537 }
538}
539
540static void test_close(skiatest::Reporter* reporter) {
541 SkPath closePt;
542 closePt.moveTo(0, 0);
543 closePt.close();
544 check_close(reporter, closePt);
545
546 SkPath openPt;
547 openPt.moveTo(0, 0);
548 check_close(reporter, openPt);
549
550 SkPath empty;
551 check_close(reporter, empty);
552 empty.close();
553 check_close(reporter, empty);
554
555 SkPath rect;
556 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
557 check_close(reporter, rect);
558 rect.close();
559 check_close(reporter, rect);
560
561 SkPath quad;
562 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
563 check_close(reporter, quad);
564 quad.close();
565 check_close(reporter, quad);
566
567 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000568 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000569 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
570 check_close(reporter, cubic);
571 cubic.close();
572 check_close(reporter, cubic);
573
574 SkPath line;
575 line.moveTo(SK_Scalar1, SK_Scalar1);
576 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
577 check_close(reporter, line);
578 line.close();
579 check_close(reporter, line);
580
581 SkPath rect2;
582 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
583 rect2.close();
584 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
585 check_close(reporter, rect2);
586 rect2.close();
587 check_close(reporter, rect2);
588
589 SkPath oval3;
590 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
591 oval3.close();
592 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
593 check_close(reporter, oval3);
594 oval3.close();
595 check_close(reporter, oval3);
596
597 SkPath moves;
598 moves.moveTo(SK_Scalar1, SK_Scalar1);
599 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
600 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
601 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
602 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000603
604 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000605}
606
reed@google.com7c424812011-05-15 04:38:34 +0000607static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
608 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000609 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
610 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000611 REPORTER_ASSERT(reporter, c == expected);
612}
613
614static void test_convexity2(skiatest::Reporter* reporter) {
615 SkPath pt;
616 pt.moveTo(0, 0);
617 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000618 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000619 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000620
reed@google.com7c424812011-05-15 04:38:34 +0000621 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000622 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
623 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000624 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000625 check_convexity(reporter, line, SkPath::kConvex_Convexity);
626 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000627
reed@google.com7c424812011-05-15 04:38:34 +0000628 SkPath triLeft;
629 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000630 triLeft.lineTo(SK_Scalar1, 0);
631 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000632 triLeft.close();
633 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000634 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000635
reed@google.com7c424812011-05-15 04:38:34 +0000636 SkPath triRight;
637 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000638 triRight.lineTo(-SK_Scalar1, 0);
639 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000640 triRight.close();
641 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000642 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000643
reed@google.com7c424812011-05-15 04:38:34 +0000644 SkPath square;
645 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000646 square.lineTo(SK_Scalar1, 0);
647 square.lineTo(SK_Scalar1, SK_Scalar1);
648 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000649 square.close();
650 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000651 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000652
reed@google.com7c424812011-05-15 04:38:34 +0000653 SkPath redundantSquare;
654 redundantSquare.moveTo(0, 0);
655 redundantSquare.lineTo(0, 0);
656 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000657 redundantSquare.lineTo(SK_Scalar1, 0);
658 redundantSquare.lineTo(SK_Scalar1, 0);
659 redundantSquare.lineTo(SK_Scalar1, 0);
660 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
661 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
662 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
663 redundantSquare.lineTo(0, SK_Scalar1);
664 redundantSquare.lineTo(0, SK_Scalar1);
665 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000666 redundantSquare.close();
667 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000668 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000669
reed@google.com7c424812011-05-15 04:38:34 +0000670 SkPath bowTie;
671 bowTie.moveTo(0, 0);
672 bowTie.lineTo(0, 0);
673 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000674 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
675 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
676 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
677 bowTie.lineTo(SK_Scalar1, 0);
678 bowTie.lineTo(SK_Scalar1, 0);
679 bowTie.lineTo(SK_Scalar1, 0);
680 bowTie.lineTo(0, SK_Scalar1);
681 bowTie.lineTo(0, SK_Scalar1);
682 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000683 bowTie.close();
684 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000685 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000686
reed@google.com7c424812011-05-15 04:38:34 +0000687 SkPath spiral;
688 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000689 spiral.lineTo(100*SK_Scalar1, 0);
690 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
691 spiral.lineTo(0, 100*SK_Scalar1);
692 spiral.lineTo(0, 50*SK_Scalar1);
693 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
694 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000695 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000696 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000697 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000698
reed@google.com7c424812011-05-15 04:38:34 +0000699 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000700 dent.moveTo(0, 0);
701 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
702 dent.lineTo(0, 100*SK_Scalar1);
703 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
704 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000705 dent.close();
706 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000707 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000708}
709
reed@android.com6b82d1a2009-06-03 02:35:01 +0000710static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
711 const SkRect& bounds) {
712 REPORTER_ASSERT(reporter, p.isConvex());
713 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000714
reed@android.com6b82d1a2009-06-03 02:35:01 +0000715 SkPath p2(p);
716 REPORTER_ASSERT(reporter, p2.isConvex());
717 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
718
719 SkPath other;
720 other.swap(p2);
721 REPORTER_ASSERT(reporter, other.isConvex());
722 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
723}
724
reed@google.com04863fa2011-05-15 04:08:24 +0000725static void setFromString(SkPath* path, const char str[]) {
726 bool first = true;
727 while (str) {
728 SkScalar x, y;
729 str = SkParse::FindScalar(str, &x);
730 if (NULL == str) {
731 break;
732 }
733 str = SkParse::FindScalar(str, &y);
734 SkASSERT(str);
735 if (first) {
736 path->moveTo(x, y);
737 first = false;
738 } else {
739 path->lineTo(x, y);
740 }
741 }
742}
743
744static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000745 SkPath path;
746
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000747 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000748 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000749 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000750 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000751 check_convexity(reporter, path, SkPath::kConcave_Convexity);
752
reed@google.com04863fa2011-05-15 04:08:24 +0000753 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000754 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000755 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000756 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000757
reed@google.com04863fa2011-05-15 04:08:24 +0000758 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000759 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000760 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000761 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000762
reed@google.com04863fa2011-05-15 04:08:24 +0000763 static const struct {
764 const char* fPathStr;
765 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000766 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000767 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000768 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
769 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
770 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
771 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
772 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
773 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
774 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
775 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000776 };
777
778 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
779 SkPath path;
780 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000781 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
782 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000783 }
784}
785
reed@google.com7e6c4d12012-05-10 14:05:43 +0000786static void test_isLine(skiatest::Reporter* reporter) {
787 SkPath path;
788 SkPoint pts[2];
789 const SkScalar value = SkIntToScalar(5);
790
791 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000792
reed@google.com7e6c4d12012-05-10 14:05:43 +0000793 // set some non-zero values
794 pts[0].set(value, value);
795 pts[1].set(value, value);
796 REPORTER_ASSERT(reporter, !path.isLine(pts));
797 // check that pts was untouched
798 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
799 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
800
801 const SkScalar moveX = SkIntToScalar(1);
802 const SkScalar moveY = SkIntToScalar(2);
803 SkASSERT(value != moveX && value != moveY);
804
805 path.moveTo(moveX, moveY);
806 REPORTER_ASSERT(reporter, !path.isLine(NULL));
807 REPORTER_ASSERT(reporter, !path.isLine(pts));
808 // check that pts was untouched
809 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
810 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
811
812 const SkScalar lineX = SkIntToScalar(2);
813 const SkScalar lineY = SkIntToScalar(2);
814 SkASSERT(value != lineX && value != lineY);
815
816 path.lineTo(lineX, lineY);
817 REPORTER_ASSERT(reporter, path.isLine(NULL));
818
819 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
820 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
821 REPORTER_ASSERT(reporter, path.isLine(pts));
822 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
823 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
824
825 path.lineTo(0, 0); // too many points/verbs
826 REPORTER_ASSERT(reporter, !path.isLine(NULL));
827 REPORTER_ASSERT(reporter, !path.isLine(pts));
828 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
829 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
830}
831
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000832static void test_conservativelyContains(skiatest::Reporter* reporter) {
833 SkPath path;
834
835 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
836 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
837
838 // A circle that bounds kBaseRect (with a significant amount of slop)
839 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
840 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
841 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
842
843 // round-rect radii
844 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +0000845
caryclark@google.com56f233a2012-11-19 13:06:06 +0000846 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000847 SkRect fQueryRect;
848 bool fInRect;
849 bool fInCircle;
850 bool fInRR;
851 } kQueries[] = {
852 {kBaseRect, true, true, false},
853
854 // rect well inside of kBaseRect
855 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
856 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
857 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
858 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
859 true, true, true},
860
861 // rects with edges off by one from kBaseRect's edges
862 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
863 kBaseRect.width(), kBaseRect.height() + 1),
864 false, true, false},
865 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
866 kBaseRect.width() + 1, kBaseRect.height()),
867 false, true, false},
868 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
869 kBaseRect.width() + 1, kBaseRect.height() + 1),
870 false, true, false},
871 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
872 kBaseRect.width(), kBaseRect.height()),
873 false, true, false},
874 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
875 kBaseRect.width(), kBaseRect.height()),
876 false, true, false},
877 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
878 kBaseRect.width() + 2, kBaseRect.height()),
879 false, true, false},
880 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
881 kBaseRect.width() + 2, kBaseRect.height()),
882 false, true, false},
883
884 // zero-w/h rects at each corner of kBaseRect
885 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
886 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
887 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
888 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
889
890 // far away rect
891 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
892 SkIntToScalar(10), SkIntToScalar(10)),
893 false, false, false},
894
895 // very large rect containing kBaseRect
896 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
897 kBaseRect.fTop - 5 * kBaseRect.height(),
898 11 * kBaseRect.width(), 11 * kBaseRect.height()),
899 false, false, false},
900
901 // skinny rect that spans same y-range as kBaseRect
902 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
903 SkIntToScalar(1), kBaseRect.height()),
904 true, true, true},
905
906 // short rect that spans same x-range as kBaseRect
907 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
908 true, true, true},
909
910 // skinny rect that spans slightly larger y-range than kBaseRect
911 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
912 SkIntToScalar(1), kBaseRect.height() + 1),
913 false, true, false},
914
915 // short rect that spans slightly larger x-range than kBaseRect
916 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
917 kBaseRect.width() + 1, SkScalar(1)),
918 false, true, false},
919 };
920
921 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +0000922 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000923 SkRect qRect = kQueries[q].fQueryRect;
924 if (inv & 0x1) {
925 SkTSwap(qRect.fLeft, qRect.fRight);
926 }
927 if (inv & 0x2) {
928 SkTSwap(qRect.fTop, qRect.fBottom);
929 }
930 for (int d = 0; d < 2; ++d) {
931 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
932 path.reset();
933 path.addRect(kBaseRect, dir);
934 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
935 path.conservativelyContainsRect(qRect));
936
937 path.reset();
938 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
939 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
940 path.conservativelyContainsRect(qRect));
941
942 path.reset();
943 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
944 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
945 path.conservativelyContainsRect(qRect));
946 }
947 // Slightly non-convex shape, shouldn't contain any rects.
948 path.reset();
949 path.moveTo(0, 0);
950 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
951 path.lineTo(SkIntToScalar(100), 0);
952 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
953 path.lineTo(0, SkIntToScalar(100));
954 path.close();
955 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
956 }
957 }
958
959 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
960 path.reset();
961 path.moveTo(0, 0);
962 path.lineTo(SkIntToScalar(100), 0);
963 path.lineTo(0, SkIntToScalar(100));
964
965 // inside, on along top edge
966 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
967 SkIntToScalar(10),
968 SkIntToScalar(10))));
969 // above
970 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
971 SkRect::MakeXYWH(SkIntToScalar(50),
972 SkIntToScalar(-10),
973 SkIntToScalar(10),
974 SkIntToScalar(10))));
975 // to the left
976 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
977 SkIntToScalar(5),
978 SkIntToScalar(5),
979 SkIntToScalar(5))));
980
981 // outside the diagonal edge
982 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
983 SkIntToScalar(200),
984 SkIntToScalar(20),
985 SkIntToScalar(5))));
986}
987
caryclark@google.comf1316942011-07-26 19:54:45 +0000988// Simple isRect test is inline TestPath, below.
989// test_isRect provides more extensive testing.
990static void test_isRect(skiatest::Reporter* reporter) {
991 // passing tests (all moveTo / lineTo...
992 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
993 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
994 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
995 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
996 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
997 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
998 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
999 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1000 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1001 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1002 {1, 0}, {.5f, 0}};
1003 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1004 {0, 1}, {0, .5f}};
1005 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1006 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1007 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001008 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001009
caryclark@google.comf1316942011-07-26 19:54:45 +00001010 // failing tests
1011 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1012 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1013 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1014 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1015 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1016 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1017 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1018 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001019 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1020 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1021 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001022
caryclark@google.comf1316942011-07-26 19:54:45 +00001023 // failing, no close
1024 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1025 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1026
1027 size_t testLen[] = {
1028 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1029 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001030 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001031 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001032 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001033 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001034 };
1035 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001036 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1037 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001038 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001039 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001040 SkPoint* lastPass = rf;
1041 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001042 bool fail = false;
1043 bool close = true;
1044 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1045 size_t index;
1046 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1047 SkPath path;
1048 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1049 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1050 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1051 }
1052 if (close) {
1053 path.close();
1054 }
1055 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001056 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1057
caryclark@google.com56f233a2012-11-19 13:06:06 +00001058 if (!fail) {
1059 SkRect computed, expected;
1060 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1061 REPORTER_ASSERT(reporter, path.isRect(&computed));
1062 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001063
caryclark@google.comf68154a2012-11-21 15:18:06 +00001064 bool isClosed;
1065 SkPath::Direction direction, cheapDirection;
1066 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1067 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1068 REPORTER_ASSERT(reporter, isClosed == close);
1069 REPORTER_ASSERT(reporter, direction == cheapDirection);
1070 } else {
1071 SkRect computed;
1072 computed.set(123, 456, 789, 1011);
1073 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1074 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1075 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1076
1077 bool isClosed = (bool) -1;
1078 SkPath::Direction direction = (SkPath::Direction) -1;
1079 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1080 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1081 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001082 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001083
caryclark@google.comf1316942011-07-26 19:54:45 +00001084 if (tests[testIndex] == lastPass) {
1085 fail = true;
1086 }
1087 if (tests[testIndex] == lastClose) {
1088 close = false;
1089 }
1090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001091
caryclark@google.comf1316942011-07-26 19:54:45 +00001092 // fail, close then line
1093 SkPath path1;
1094 path1.moveTo(r1[0].fX, r1[0].fY);
1095 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1096 path1.lineTo(r1[index].fX, r1[index].fY);
1097 }
1098 path1.close();
1099 path1.lineTo(1, 0);
1100 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001101
caryclark@google.comf1316942011-07-26 19:54:45 +00001102 // fail, move in the middle
1103 path1.reset();
1104 path1.moveTo(r1[0].fX, r1[0].fY);
1105 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1106 if (index == 2) {
1107 path1.moveTo(1, .5f);
1108 }
1109 path1.lineTo(r1[index].fX, r1[index].fY);
1110 }
1111 path1.close();
1112 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1113
1114 // fail, move on the edge
1115 path1.reset();
1116 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1117 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1118 path1.lineTo(r1[index].fX, r1[index].fY);
1119 }
1120 path1.close();
1121 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001122
caryclark@google.comf1316942011-07-26 19:54:45 +00001123 // fail, quad
1124 path1.reset();
1125 path1.moveTo(r1[0].fX, r1[0].fY);
1126 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1127 if (index == 2) {
1128 path1.quadTo(1, .5f, 1, .5f);
1129 }
1130 path1.lineTo(r1[index].fX, r1[index].fY);
1131 }
1132 path1.close();
1133 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001134
caryclark@google.comf1316942011-07-26 19:54:45 +00001135 // fail, cubic
1136 path1.reset();
1137 path1.moveTo(r1[0].fX, r1[0].fY);
1138 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1139 if (index == 2) {
1140 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1141 }
1142 path1.lineTo(r1[index].fX, r1[index].fY);
1143 }
1144 path1.close();
1145 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1146}
1147
caryclark@google.com56f233a2012-11-19 13:06:06 +00001148static void test_isNestedRects(skiatest::Reporter* reporter) {
1149 // passing tests (all moveTo / lineTo...
1150 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1151 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1152 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1153 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1154 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1155 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1156 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1157 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1158 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1159 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1160 {1, 0}, {.5f, 0}};
1161 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1162 {0, 1}, {0, .5f}};
1163 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1164 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1165 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1166
1167 // failing tests
1168 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1169 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1170 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1171 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1172 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1173 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1174 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1175 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1176
1177 // failing, no close
1178 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1179 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1180
1181 size_t testLen[] = {
1182 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1183 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1184 sizeof(rd), sizeof(re),
1185 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1186 sizeof(f7), sizeof(f8),
1187 sizeof(c1), sizeof(c2)
1188 };
1189 SkPoint* tests[] = {
1190 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1191 f1, f2, f3, f4, f5, f6, f7, f8,
1192 c1, c2
1193 };
1194 const SkPoint* lastPass = re;
1195 const SkPoint* lastClose = f8;
1196 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1197 size_t index;
1198 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1199 bool fail = false;
1200 bool close = true;
1201 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1202 SkPath path;
1203 if (rectFirst) {
1204 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1205 }
1206 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1207 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1208 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1209 }
1210 if (close) {
1211 path.close();
1212 }
1213 if (!rectFirst) {
1214 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1215 }
1216 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1217 if (!fail) {
1218 SkRect expected[2], computed[2];
1219 SkRect testBounds;
1220 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1221 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1222 expected[1] = testBounds;
1223 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1224 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1225 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1226 }
1227 if (tests[testIndex] == lastPass) {
1228 fail = true;
1229 }
1230 if (tests[testIndex] == lastClose) {
1231 close = false;
1232 }
1233 }
1234
1235 // fail, close then line
1236 SkPath path1;
1237 if (rectFirst) {
1238 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1239 }
1240 path1.moveTo(r1[0].fX, r1[0].fY);
1241 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1242 path1.lineTo(r1[index].fX, r1[index].fY);
1243 }
1244 path1.close();
1245 path1.lineTo(1, 0);
1246 if (!rectFirst) {
1247 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1248 }
1249 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1250
1251 // fail, move in the middle
1252 path1.reset();
1253 if (rectFirst) {
1254 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1255 }
1256 path1.moveTo(r1[0].fX, r1[0].fY);
1257 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1258 if (index == 2) {
1259 path1.moveTo(1, .5f);
1260 }
1261 path1.lineTo(r1[index].fX, r1[index].fY);
1262 }
1263 path1.close();
1264 if (!rectFirst) {
1265 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1266 }
1267 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1268
1269 // fail, move on the edge
1270 path1.reset();
1271 if (rectFirst) {
1272 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1273 }
1274 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1275 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1276 path1.lineTo(r1[index].fX, r1[index].fY);
1277 }
1278 path1.close();
1279 if (!rectFirst) {
1280 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1281 }
1282 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1283
1284 // fail, quad
1285 path1.reset();
1286 if (rectFirst) {
1287 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1288 }
1289 path1.moveTo(r1[0].fX, r1[0].fY);
1290 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1291 if (index == 2) {
1292 path1.quadTo(1, .5f, 1, .5f);
1293 }
1294 path1.lineTo(r1[index].fX, r1[index].fY);
1295 }
1296 path1.close();
1297 if (!rectFirst) {
1298 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1299 }
1300 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1301
1302 // fail, cubic
1303 path1.reset();
1304 if (rectFirst) {
1305 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1306 }
1307 path1.moveTo(r1[0].fX, r1[0].fY);
1308 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1309 if (index == 2) {
1310 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1311 }
1312 path1.lineTo(r1[index].fX, r1[index].fY);
1313 }
1314 path1.close();
1315 if (!rectFirst) {
1316 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1317 }
1318 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001319
caryclark@google.com56f233a2012-11-19 13:06:06 +00001320 // fail, not nested
1321 path1.reset();
1322 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1323 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1324 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1325 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001326
1327 // pass, stroke rect
1328 SkPath src, dst;
1329 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1330 SkPaint strokePaint;
1331 strokePaint.setStyle(SkPaint::kStroke_Style);
1332 strokePaint.setStrokeWidth(2);
1333 strokePaint.getFillPath(src, &dst);
1334 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001335}
1336
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001337static void write_and_read_back(skiatest::Reporter* reporter,
1338 const SkPath& p) {
1339 SkWriter32 writer(100);
1340 writer.writePath(p);
1341 size_t size = writer.size();
1342 SkAutoMalloc storage(size);
1343 writer.flatten(storage.get());
1344 SkReader32 reader(storage.get(), size);
1345
1346 SkPath readBack;
1347 REPORTER_ASSERT(reporter, readBack != p);
1348 reader.readPath(&readBack);
1349 REPORTER_ASSERT(reporter, readBack == p);
1350
rmistry@google.comd6176b02012-08-23 18:14:13 +00001351 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001352 p.getConvexityOrUnknown());
1353
1354 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1355
1356 const SkRect& origBounds = p.getBounds();
1357 const SkRect& readBackBounds = readBack.getBounds();
1358
1359 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1360}
1361
reed@google.com53effc52011-09-21 19:05:12 +00001362static void test_flattening(skiatest::Reporter* reporter) {
1363 SkPath p;
1364
1365 static const SkPoint pts[] = {
1366 { 0, 0 },
1367 { SkIntToScalar(10), SkIntToScalar(10) },
1368 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1369 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1370 };
1371 p.moveTo(pts[0]);
1372 p.lineTo(pts[1]);
1373 p.quadTo(pts[2], pts[3]);
1374 p.cubicTo(pts[4], pts[5], pts[6]);
1375
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001376 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001377
1378 // create a buffer that should be much larger than the path so we don't
1379 // kill our stack if writer goes too far.
1380 char buffer[1024];
1381 uint32_t size1 = p.writeToMemory(NULL);
1382 uint32_t size2 = p.writeToMemory(buffer);
1383 REPORTER_ASSERT(reporter, size1 == size2);
1384
1385 SkPath p2;
1386 uint32_t size3 = p2.readFromMemory(buffer);
1387 REPORTER_ASSERT(reporter, size1 == size3);
1388 REPORTER_ASSERT(reporter, p == p2);
1389
1390 char buffer2[1024];
1391 size3 = p2.writeToMemory(buffer2);
1392 REPORTER_ASSERT(reporter, size1 == size3);
1393 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001394
1395 // test persistence of the oval flag & convexity
1396 {
1397 SkPath oval;
1398 SkRect rect = SkRect::MakeWH(10, 10);
1399 oval.addOval(rect);
1400
1401 write_and_read_back(reporter, oval);
1402 }
reed@google.com53effc52011-09-21 19:05:12 +00001403}
1404
1405static void test_transform(skiatest::Reporter* reporter) {
1406 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001407
reed@google.com53effc52011-09-21 19:05:12 +00001408 static const SkPoint pts[] = {
1409 { 0, 0 },
1410 { SkIntToScalar(10), SkIntToScalar(10) },
1411 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1412 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1413 };
1414 p.moveTo(pts[0]);
1415 p.lineTo(pts[1]);
1416 p.quadTo(pts[2], pts[3]);
1417 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001418
reed@google.com53effc52011-09-21 19:05:12 +00001419 SkMatrix matrix;
1420 matrix.reset();
1421 p.transform(matrix, &p1);
1422 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001423
reed@google.com53effc52011-09-21 19:05:12 +00001424 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1425 p.transform(matrix, &p1);
1426 SkPoint pts1[7];
1427 int count = p1.getPoints(pts1, 7);
1428 REPORTER_ASSERT(reporter, 7 == count);
1429 for (int i = 0; i < count; ++i) {
1430 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1431 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1432 }
1433}
1434
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001435static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001436 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001437 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001438
caryclark@google.com56f233a2012-11-19 13:06:06 +00001439 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001440 const char* testPath;
1441 const size_t numResultPts;
1442 const SkRect resultBound;
1443 const SkPath::Verb* resultVerbs;
1444 const size_t numResultVerbs;
1445 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001446
schenney@chromium.org7e963602012-06-13 17:05:43 +00001447 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1448 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1449 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1450 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1451 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1452 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1453 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1454 static const SkPath::Verb resultVerbs8[] = {
1455 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1456 };
1457 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1458 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1459 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1460 static const SkPath::Verb resultVerbs12[] = {
1461 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1462 };
1463 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1464 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1465 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1466 static const SkPath::Verb resultVerbs16[] = {
1467 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1468 };
1469 static const struct zeroPathTestData gZeroLengthTests[] = {
1470 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001471 { "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 +00001472 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001473 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1474 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1475 { "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) },
1476 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1477 { "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) },
1478 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1479 { "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) },
1480 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1481 { "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) },
1482 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1483 { "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 +00001484 SK_ARRAY_COUNT(resultVerbs14)
1485 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001486 { "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) },
1487 { "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 +00001488 SK_ARRAY_COUNT(resultVerbs16)
1489 }
1490 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001491
schenney@chromium.org7e963602012-06-13 17:05:43 +00001492 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1493 p.reset();
1494 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1495 REPORTER_ASSERT(reporter, valid);
1496 REPORTER_ASSERT(reporter, !p.isEmpty());
1497 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1498 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1499 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1500 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1501 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1502 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001503 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001504}
1505
1506struct SegmentInfo {
1507 SkPath fPath;
1508 int fPointCount;
1509};
1510
reed@google.com10296cc2011-09-21 12:29:05 +00001511#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1512
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001513static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001514 SkPath p, p2;
1515
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001516 p.moveTo(0, 0);
1517 p.quadTo(100, 100, 200, 200);
1518 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1519 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001520 p2 = p;
1521 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001522 p.cubicTo(100, 100, 200, 200, 300, 300);
1523 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1524 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001525 p2 = p;
1526 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1527
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001528 p.reset();
1529 p.moveTo(0, 0);
1530 p.cubicTo(100, 100, 200, 200, 300, 300);
1531 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001532 p2 = p;
1533 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001534
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001535 REPORTER_ASSERT(reporter, !p.isEmpty());
1536}
1537
1538static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001539 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001540 SkPoint pts[4];
1541
1542 // Test an iterator with no path
1543 SkPath::Iter noPathIter;
1544 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001545
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001546 // Test that setting an empty path works
1547 noPathIter.setPath(p, false);
1548 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001549
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001550 // Test that close path makes no difference for an empty path
1551 noPathIter.setPath(p, true);
1552 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001553
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001554 // Test an iterator with an initial empty path
1555 SkPath::Iter iter(p, false);
1556 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1557
1558 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001559 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001560 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1561
rmistry@google.comd6176b02012-08-23 18:14:13 +00001562
schenney@chromium.org7e963602012-06-13 17:05:43 +00001563 struct iterTestData {
1564 const char* testPath;
1565 const bool forceClose;
1566 const bool consumeDegenerates;
1567 const size_t* numResultPtsPerVerb;
1568 const SkPoint* resultPts;
1569 const SkPath::Verb* resultVerbs;
1570 const size_t numResultVerbs;
1571 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001572
schenney@chromium.org7e963602012-06-13 17:05:43 +00001573 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1574 static const SkPath::Verb resultVerbs2[] = {
1575 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1576 };
1577 static const SkPath::Verb resultVerbs3[] = {
1578 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1579 };
1580 static const SkPath::Verb resultVerbs4[] = {
1581 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1582 };
1583 static const SkPath::Verb resultVerbs5[] = {
1584 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1585 };
1586 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001587 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1588 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1589 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1590 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001591 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001592 static const SkPoint resultPts2[] = {
1593 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1594 };
1595 static const SkPoint resultPts3[] = {
1596 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1597 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1598 };
1599 static const SkPoint resultPts4[] = {
1600 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1601 };
1602 static const SkPoint resultPts5[] = {
1603 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1604 };
1605 static const struct iterTestData gIterTests[] = {
1606 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001607 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1608 { "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 +00001609 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1610 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1611 { "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) },
1612 { "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 +00001613 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1614 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1615 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1616 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1617 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1618 { "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 +00001619 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001620
schenney@chromium.org7e963602012-06-13 17:05:43 +00001621 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1622 p.reset();
1623 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1624 REPORTER_ASSERT(reporter, valid);
1625 iter.setPath(p, gIterTests[i].forceClose);
1626 int j = 0, l = 0;
1627 do {
1628 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1629 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1630 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1631 }
1632 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1633 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1634 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001635
1636 // The GM degeneratesegments.cpp test is more extensive
1637}
1638
1639static void test_raw_iter(skiatest::Reporter* reporter) {
1640 SkPath p;
1641 SkPoint pts[4];
1642
1643 // Test an iterator with no path
1644 SkPath::RawIter noPathIter;
1645 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1646 // Test that setting an empty path works
1647 noPathIter.setPath(p);
1648 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001649
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001650 // Test an iterator with an initial empty path
1651 SkPath::RawIter iter(p);
1652 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1653
1654 // Test that a move-only path returns the move.
1655 p.moveTo(SK_Scalar1, 0);
1656 iter.setPath(p);
1657 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1658 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1659 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1660 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1661
1662 // No matter how many moves we add, we should get them all back
1663 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1664 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1665 iter.setPath(p);
1666 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1667 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1668 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1669 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1670 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1671 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1672 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1673 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1674 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1675 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1676
1677 // Initial close is never ever stored
1678 p.reset();
1679 p.close();
1680 iter.setPath(p);
1681 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1682
1683 // Move/close sequences
1684 p.reset();
1685 p.close(); // Not stored, no purpose
1686 p.moveTo(SK_Scalar1, 0);
1687 p.close();
1688 p.close(); // Not stored, no purpose
1689 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1690 p.close();
1691 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1692 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1693 p.close();
1694 iter.setPath(p);
1695 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1696 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1697 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1698 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1699 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1700 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1701 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1702 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1703 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1704 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1705 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1706 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1707 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1708 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1709 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1710 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1711 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1712 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1713 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1714 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1715 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1716 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1717
1718 // Generate random paths and verify
1719 SkPoint randomPts[25];
1720 for (int i = 0; i < 5; ++i) {
1721 for (int j = 0; j < 5; ++j) {
1722 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1723 }
1724 }
1725
1726 // Max of 10 segments, max 3 points per segment
1727 SkRandom rand(9876543);
1728 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001729 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001730 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001731
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001732 for (int i = 0; i < 500; ++i) {
1733 p.reset();
1734 bool lastWasClose = true;
1735 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001736 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001737 int numPoints = 0;
1738 int numVerbs = (rand.nextU() >> 16) % 10;
1739 int numIterVerbs = 0;
1740 for (int j = 0; j < numVerbs; ++j) {
1741 do {
1742 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1743 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001744 switch (nextVerb) {
1745 case SkPath::kMove_Verb:
1746 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1747 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001748 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001749 numPoints += 1;
1750 lastWasClose = false;
1751 haveMoveTo = true;
1752 break;
1753 case SkPath::kLine_Verb:
1754 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001755 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001756 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1757 haveMoveTo = true;
1758 }
1759 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1760 p.lineTo(expectedPts[numPoints]);
1761 numPoints += 1;
1762 lastWasClose = false;
1763 break;
1764 case SkPath::kQuad_Verb:
1765 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001766 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001767 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1768 haveMoveTo = true;
1769 }
1770 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1771 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1772 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1773 numPoints += 2;
1774 lastWasClose = false;
1775 break;
1776 case SkPath::kCubic_Verb:
1777 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001778 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001779 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1780 haveMoveTo = true;
1781 }
1782 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1783 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1784 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1785 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1786 expectedPts[numPoints + 2]);
1787 numPoints += 3;
1788 lastWasClose = false;
1789 break;
1790 case SkPath::kClose_Verb:
1791 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001792 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001793 lastWasClose = true;
1794 break;
1795 default:;
1796 }
1797 expectedVerbs[numIterVerbs++] = nextVerb;
1798 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001799
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001800 iter.setPath(p);
1801 numVerbs = numIterVerbs;
1802 numIterVerbs = 0;
1803 int numIterPts = 0;
1804 SkPoint lastMoveTo;
1805 SkPoint lastPt;
1806 lastMoveTo.set(0, 0);
1807 lastPt.set(0, 0);
1808 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1809 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1810 numIterVerbs++;
1811 switch (nextVerb) {
1812 case SkPath::kMove_Verb:
1813 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1814 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1815 lastPt = lastMoveTo = pts[0];
1816 numIterPts += 1;
1817 break;
1818 case SkPath::kLine_Verb:
1819 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1820 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1821 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1822 lastPt = pts[1];
1823 numIterPts += 1;
1824 break;
1825 case SkPath::kQuad_Verb:
1826 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1827 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1828 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1829 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1830 lastPt = pts[2];
1831 numIterPts += 2;
1832 break;
1833 case SkPath::kCubic_Verb:
1834 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1835 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1836 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1837 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1838 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1839 lastPt = pts[3];
1840 numIterPts += 3;
1841 break;
1842 case SkPath::kClose_Verb:
1843 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1844 lastPt = lastMoveTo;
1845 break;
1846 default:;
1847 }
1848 }
1849 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1850 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1851 }
1852}
1853
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001854static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001855 const SkPath& path,
1856 bool expectedCircle,
1857 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001858 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001859 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1860 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001861
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001862 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001863 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1864 }
1865}
1866
1867static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001868 const SkPath& path,
1869 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001870 SkPath tmp;
1871
1872 SkMatrix m;
1873 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1874 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001875 // this matrix reverses the direction.
1876 if (SkPath::kCCW_Direction == dir) {
1877 dir = SkPath::kCW_Direction;
1878 } else {
1879 SkASSERT(SkPath::kCW_Direction == dir);
1880 dir = SkPath::kCCW_Direction;
1881 }
1882 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001883}
1884
1885static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001886 const SkPath& path,
1887 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001888 SkPath tmp;
1889
1890 // translate at small offset
1891 SkMatrix m;
1892 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1893 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001894 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001895
1896 tmp.reset();
1897 m.reset();
1898
1899 // translate at a relatively big offset
1900 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1901 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001902 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001903}
1904
1905static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001906 const SkPath& path,
1907 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001908 for (int angle = 0; angle < 360; ++angle) {
1909 SkPath tmp;
1910 SkMatrix m;
1911 m.setRotate(SkIntToScalar(angle));
1912 path.transform(m, &tmp);
1913
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001914 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001915 // degrees is not an oval anymore, this can be improved. we made this
1916 // for the simplicity of our implementation.
1917 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001918 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001919 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001920 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001921 }
1922 }
1923}
1924
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001925static void test_circle_mirror_x(skiatest::Reporter* reporter,
1926 const SkPath& path,
1927 SkPath::Direction dir) {
1928 SkPath tmp;
1929 SkMatrix m;
1930 m.reset();
1931 m.setScaleX(-SK_Scalar1);
1932 path.transform(m, &tmp);
1933
1934 if (SkPath::kCW_Direction == dir) {
1935 dir = SkPath::kCCW_Direction;
1936 } else {
1937 SkASSERT(SkPath::kCCW_Direction == dir);
1938 dir = SkPath::kCW_Direction;
1939 }
1940
1941 check_for_circle(reporter, tmp, true, dir);
1942}
1943
1944static void test_circle_mirror_y(skiatest::Reporter* reporter,
1945 const SkPath& path,
1946 SkPath::Direction dir) {
1947 SkPath tmp;
1948 SkMatrix m;
1949 m.reset();
1950 m.setScaleY(-SK_Scalar1);
1951 path.transform(m, &tmp);
1952
1953 if (SkPath::kCW_Direction == dir) {
1954 dir = SkPath::kCCW_Direction;
1955 } else {
1956 SkASSERT(SkPath::kCCW_Direction == dir);
1957 dir = SkPath::kCW_Direction;
1958 }
1959
1960 check_for_circle(reporter, tmp, true, dir);
1961}
1962
1963static void test_circle_mirror_xy(skiatest::Reporter* reporter,
1964 const SkPath& path,
1965 SkPath::Direction dir) {
1966 SkPath tmp;
1967 SkMatrix m;
1968 m.reset();
1969 m.setScaleX(-SK_Scalar1);
1970 m.setScaleY(-SK_Scalar1);
1971 path.transform(m, &tmp);
1972
1973 check_for_circle(reporter, tmp, true, dir);
1974}
1975
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001976static void test_circle_with_direction(skiatest::Reporter* reporter,
1977 SkPath::Direction dir) {
1978 SkPath path;
1979
1980 // circle at origin
1981 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001982 check_for_circle(reporter, path, true, dir);
1983 test_circle_rotate(reporter, path, dir);
1984 test_circle_translate(reporter, path, dir);
1985 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001986
1987 // circle at an offset at (10, 10)
1988 path.reset();
1989 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1990 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001991 check_for_circle(reporter, path, true, dir);
1992 test_circle_rotate(reporter, path, dir);
1993 test_circle_translate(reporter, path, dir);
1994 test_circle_skew(reporter, path, dir);
1995 test_circle_mirror_x(reporter, path, dir);
1996 test_circle_mirror_y(reporter, path, dir);
1997 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001998}
1999
2000static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2001 SkPath path;
2002 SkPath circle;
2003 SkPath rect;
2004 SkPath empty;
2005
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002006 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2007 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2008
2009 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002010 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2011 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2012
2013 SkMatrix translate;
2014 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2015
2016 // For simplicity, all the path concatenation related operations
2017 // would mark it non-circle, though in theory it's still a circle.
2018
2019 // empty + circle (translate)
2020 path = empty;
2021 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002022 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002023
2024 // circle + empty (translate)
2025 path = circle;
2026 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002027 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002028
2029 // test reverseAddPath
2030 path = circle;
2031 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002032 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002033}
2034
2035static void test_circle(skiatest::Reporter* reporter) {
2036 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2037 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2038
2039 // multiple addCircle()
2040 SkPath path;
2041 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2042 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002043 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002044
2045 // some extra lineTo() would make isOval() fail
2046 path.reset();
2047 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2048 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002049 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002050
2051 // not back to the original point
2052 path.reset();
2053 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2054 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002055 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002056
2057 test_circle_with_add_paths(reporter);
2058}
2059
2060static void test_oval(skiatest::Reporter* reporter) {
2061 SkRect rect;
2062 SkMatrix m;
2063 SkPath path;
2064
2065 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2066 path.addOval(rect);
2067
2068 REPORTER_ASSERT(reporter, path.isOval(NULL));
2069
2070 m.setRotate(SkIntToScalar(90));
2071 SkPath tmp;
2072 path.transform(m, &tmp);
2073 // an oval rotated 90 degrees is still an oval.
2074 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2075
2076 m.reset();
2077 m.setRotate(SkIntToScalar(30));
2078 tmp.reset();
2079 path.transform(m, &tmp);
2080 // an oval rotated 30 degrees is not an oval anymore.
2081 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2082
2083 // since empty path being transformed.
2084 path.reset();
2085 tmp.reset();
2086 m.reset();
2087 path.transform(m, &tmp);
2088 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2089
2090 // empty path is not an oval
2091 tmp.reset();
2092 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2093
2094 // only has moveTo()s
2095 tmp.reset();
2096 tmp.moveTo(0, 0);
2097 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2098 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2099
2100 // mimic WebKit's calling convention,
2101 // call moveTo() first and then call addOval()
2102 path.reset();
2103 path.moveTo(0, 0);
2104 path.addOval(rect);
2105 REPORTER_ASSERT(reporter, path.isOval(NULL));
2106
2107 // copy path
2108 path.reset();
2109 tmp.reset();
2110 tmp.addOval(rect);
2111 path = tmp;
2112 REPORTER_ASSERT(reporter, path.isOval(NULL));
2113}
2114
caryclark@google.com42639cd2012-06-06 12:03:39 +00002115static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002116 SkTSize<SkScalar>::Make(3,4);
2117
reed@android.com3abec1d2009-03-02 05:36:20 +00002118 SkPath p, p2;
2119 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002120
reed@android.com3abec1d2009-03-02 05:36:20 +00002121 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002122 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002123 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002124 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002125 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002126 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2127 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2128 REPORTER_ASSERT(reporter, p == p2);
2129 REPORTER_ASSERT(reporter, !(p != p2));
2130
reed@android.comd252db02009-04-01 18:31:44 +00002131 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002132
reed@android.com3abec1d2009-03-02 05:36:20 +00002133 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002134
reed@android.com6b82d1a2009-06-03 02:35:01 +00002135 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2136 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002137 // we have quads or cubics
2138 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002139 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002140
reed@android.com6b82d1a2009-06-03 02:35:01 +00002141 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002142 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002143 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002144
reed@android.com6b82d1a2009-06-03 02:35:01 +00002145 p.addOval(bounds);
2146 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002147 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002148
reed@android.com6b82d1a2009-06-03 02:35:01 +00002149 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002150 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002151 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002152 // we have only lines
2153 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002154 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002155
2156 REPORTER_ASSERT(reporter, p != p2);
2157 REPORTER_ASSERT(reporter, !(p == p2));
2158
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002159 // do getPoints and getVerbs return the right result
2160 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2161 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002162 SkPoint pts[4];
2163 int count = p.getPoints(pts, 4);
2164 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002165 uint8_t verbs[6];
2166 verbs[5] = 0xff;
2167 p.getVerbs(verbs, 5);
2168 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2169 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2170 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2171 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2172 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2173 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002174 bounds2.set(pts, 4);
2175 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002176
reed@android.com3abec1d2009-03-02 05:36:20 +00002177 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2178 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002179 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002180
reed@android.com3abec1d2009-03-02 05:36:20 +00002181 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002182 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002183 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2184 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002185
reed@android.com3abec1d2009-03-02 05:36:20 +00002186 // now force p to not be a rect
2187 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2188 p.addRect(bounds);
2189 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002190
reed@google.com7e6c4d12012-05-10 14:05:43 +00002191 test_isLine(reporter);
2192 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002193 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002194 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002195 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002196 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002197 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002198 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002199 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002200 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002201 test_flattening(reporter);
2202 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002203 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002204 test_iter(reporter);
2205 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002206 test_circle(reporter);
2207 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002208 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002209 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002210 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002211 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002212 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002213 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002214 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002215 test_addrect_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002216}
2217
2218#include "TestClassDef.h"
2219DEFINE_TESTCLASS("Path", PathTestClass, TestPath)