blob: f1894c3940c269fe09d179c84e2c396c27bf7e4d [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));
378
379 static const char* gDegen[] = {
380 "M 10 10",
381 "M 10 10 M 20 20",
382 "M 10 10 L 20 20",
383 "M 10 10 L 10 10 L 10 10",
384 "M 10 10 Q 10 10 10 10",
385 "M 10 10 C 10 10 10 10 10 10",
386 };
387 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
388 path.reset();
389 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
390 REPORTER_ASSERT(reporter, valid);
391 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
392 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000393
reed@google.com3e71a882012-01-10 18:44:37 +0000394 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000395 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000396 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000397 "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 +0000398 // rect with top two corners replaced by cubics with identical middle
399 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000400 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
401 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000402 };
403 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
404 path.reset();
405 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
406 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000407 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000408 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000409
reed@google.com3e71a882012-01-10 18:44:37 +0000410 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000411 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000412 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000413 "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 +0000414 // rect with top two corners replaced by cubics with identical middle
415 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000416 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
417 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000418 };
419 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
420 path.reset();
421 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
422 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000423 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000424 }
reed@google.comac8543f2012-01-30 20:51:25 +0000425
426 // Test two donuts, each wound a different direction. Only the outer contour
427 // determines the cheap direction
428 path.reset();
429 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
430 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000431 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000432
reed@google.comac8543f2012-01-30 20:51:25 +0000433 path.reset();
434 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
435 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000436 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000437
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000438#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000439 // triangle with one point really far from the origin.
440 path.reset();
441 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000442 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
443 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
444 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000445 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000446#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000447}
448
reed@google.comffdb0182011-11-14 19:29:14 +0000449static void add_rect(SkPath* path, const SkRect& r) {
450 path->moveTo(r.fLeft, r.fTop);
451 path->lineTo(r.fRight, r.fTop);
452 path->lineTo(r.fRight, r.fBottom);
453 path->lineTo(r.fLeft, r.fBottom);
454 path->close();
455}
456
457static void test_bounds(skiatest::Reporter* reporter) {
458 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000459 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
460 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
461 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
462 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000463 };
464
465 SkPath path0, path1;
466 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
467 path0.addRect(rects[i]);
468 add_rect(&path1, rects[i]);
469 }
470
471 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
472}
473
reed@google.com55b5f4b2011-09-07 12:23:41 +0000474static void stroke_cubic(const SkPoint pts[4]) {
475 SkPath path;
476 path.moveTo(pts[0]);
477 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000478
reed@google.com55b5f4b2011-09-07 12:23:41 +0000479 SkPaint paint;
480 paint.setStyle(SkPaint::kStroke_Style);
481 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000482
reed@google.com55b5f4b2011-09-07 12:23:41 +0000483 SkPath fill;
484 paint.getFillPath(path, &fill);
485}
486
487// just ensure this can run w/o any SkASSERTS firing in the debug build
488// we used to assert due to differences in how we determine a degenerate vector
489// but that was fixed with the introduction of SkPoint::CanNormalize
490static void stroke_tiny_cubic() {
491 SkPoint p0[] = {
492 { 372.0f, 92.0f },
493 { 372.0f, 92.0f },
494 { 372.0f, 92.0f },
495 { 372.0f, 92.0f },
496 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000497
reed@google.com55b5f4b2011-09-07 12:23:41 +0000498 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000499
reed@google.com55b5f4b2011-09-07 12:23:41 +0000500 SkPoint p1[] = {
501 { 372.0f, 92.0f },
502 { 372.0007f, 92.000755f },
503 { 371.99927f, 92.003922f },
504 { 371.99826f, 92.003899f },
505 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000506
reed@google.com55b5f4b2011-09-07 12:23:41 +0000507 stroke_cubic(p1);
508}
509
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000510static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
511 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000512 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000513 SkPoint mv;
514 SkPoint pts[4];
515 SkPath::Verb v;
516 int nMT = 0;
517 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000518 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000519 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
520 switch (v) {
521 case SkPath::kMove_Verb:
522 mv = pts[0];
523 ++nMT;
524 break;
525 case SkPath::kClose_Verb:
526 REPORTER_ASSERT(reporter, mv == pts[0]);
527 ++nCL;
528 break;
529 default:
530 break;
531 }
532 }
533 // if we force a close on the interator we should have a close
534 // for every moveTo
535 REPORTER_ASSERT(reporter, !i || nMT == nCL);
536 }
537}
538
539static void test_close(skiatest::Reporter* reporter) {
540 SkPath closePt;
541 closePt.moveTo(0, 0);
542 closePt.close();
543 check_close(reporter, closePt);
544
545 SkPath openPt;
546 openPt.moveTo(0, 0);
547 check_close(reporter, openPt);
548
549 SkPath empty;
550 check_close(reporter, empty);
551 empty.close();
552 check_close(reporter, empty);
553
554 SkPath rect;
555 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
556 check_close(reporter, rect);
557 rect.close();
558 check_close(reporter, rect);
559
560 SkPath quad;
561 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
562 check_close(reporter, quad);
563 quad.close();
564 check_close(reporter, quad);
565
566 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000567 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000568 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
569 check_close(reporter, cubic);
570 cubic.close();
571 check_close(reporter, cubic);
572
573 SkPath line;
574 line.moveTo(SK_Scalar1, SK_Scalar1);
575 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
576 check_close(reporter, line);
577 line.close();
578 check_close(reporter, line);
579
580 SkPath rect2;
581 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
582 rect2.close();
583 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
584 check_close(reporter, rect2);
585 rect2.close();
586 check_close(reporter, rect2);
587
588 SkPath oval3;
589 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
590 oval3.close();
591 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
592 check_close(reporter, oval3);
593 oval3.close();
594 check_close(reporter, oval3);
595
596 SkPath moves;
597 moves.moveTo(SK_Scalar1, SK_Scalar1);
598 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
599 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
600 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
601 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000602
603 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000604}
605
reed@google.com7c424812011-05-15 04:38:34 +0000606static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
607 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000608 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
609 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000610 REPORTER_ASSERT(reporter, c == expected);
611}
612
613static void test_convexity2(skiatest::Reporter* reporter) {
614 SkPath pt;
615 pt.moveTo(0, 0);
616 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000617 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000618 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000619
reed@google.com7c424812011-05-15 04:38:34 +0000620 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000621 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
622 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000623 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000624 check_convexity(reporter, line, SkPath::kConvex_Convexity);
625 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000626
reed@google.com7c424812011-05-15 04:38:34 +0000627 SkPath triLeft;
628 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000629 triLeft.lineTo(SK_Scalar1, 0);
630 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000631 triLeft.close();
632 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000633 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000634
reed@google.com7c424812011-05-15 04:38:34 +0000635 SkPath triRight;
636 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000637 triRight.lineTo(-SK_Scalar1, 0);
638 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000639 triRight.close();
640 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000641 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000642
reed@google.com7c424812011-05-15 04:38:34 +0000643 SkPath square;
644 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000645 square.lineTo(SK_Scalar1, 0);
646 square.lineTo(SK_Scalar1, SK_Scalar1);
647 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000648 square.close();
649 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000650 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000651
reed@google.com7c424812011-05-15 04:38:34 +0000652 SkPath redundantSquare;
653 redundantSquare.moveTo(0, 0);
654 redundantSquare.lineTo(0, 0);
655 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000656 redundantSquare.lineTo(SK_Scalar1, 0);
657 redundantSquare.lineTo(SK_Scalar1, 0);
658 redundantSquare.lineTo(SK_Scalar1, 0);
659 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
660 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
661 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
662 redundantSquare.lineTo(0, SK_Scalar1);
663 redundantSquare.lineTo(0, SK_Scalar1);
664 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000665 redundantSquare.close();
666 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000667 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000668
reed@google.com7c424812011-05-15 04:38:34 +0000669 SkPath bowTie;
670 bowTie.moveTo(0, 0);
671 bowTie.lineTo(0, 0);
672 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000673 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
674 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
675 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
676 bowTie.lineTo(SK_Scalar1, 0);
677 bowTie.lineTo(SK_Scalar1, 0);
678 bowTie.lineTo(SK_Scalar1, 0);
679 bowTie.lineTo(0, SK_Scalar1);
680 bowTie.lineTo(0, SK_Scalar1);
681 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000682 bowTie.close();
683 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000684 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000685
reed@google.com7c424812011-05-15 04:38:34 +0000686 SkPath spiral;
687 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000688 spiral.lineTo(100*SK_Scalar1, 0);
689 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
690 spiral.lineTo(0, 100*SK_Scalar1);
691 spiral.lineTo(0, 50*SK_Scalar1);
692 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
693 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000694 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000695 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000696 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000697
reed@google.com7c424812011-05-15 04:38:34 +0000698 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000699 dent.moveTo(0, 0);
700 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
701 dent.lineTo(0, 100*SK_Scalar1);
702 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
703 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000704 dent.close();
705 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000706 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000707}
708
reed@android.com6b82d1a2009-06-03 02:35:01 +0000709static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
710 const SkRect& bounds) {
711 REPORTER_ASSERT(reporter, p.isConvex());
712 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000713
reed@android.com6b82d1a2009-06-03 02:35:01 +0000714 SkPath p2(p);
715 REPORTER_ASSERT(reporter, p2.isConvex());
716 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
717
718 SkPath other;
719 other.swap(p2);
720 REPORTER_ASSERT(reporter, other.isConvex());
721 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
722}
723
reed@google.com04863fa2011-05-15 04:08:24 +0000724static void setFromString(SkPath* path, const char str[]) {
725 bool first = true;
726 while (str) {
727 SkScalar x, y;
728 str = SkParse::FindScalar(str, &x);
729 if (NULL == str) {
730 break;
731 }
732 str = SkParse::FindScalar(str, &y);
733 SkASSERT(str);
734 if (first) {
735 path->moveTo(x, y);
736 first = false;
737 } else {
738 path->lineTo(x, y);
739 }
740 }
741}
742
743static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000744 SkPath path;
745
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000746 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000747 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000748 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000749 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000750 check_convexity(reporter, path, SkPath::kConcave_Convexity);
751
reed@google.com04863fa2011-05-15 04:08:24 +0000752 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000753 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000754 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000755 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000756
reed@google.com04863fa2011-05-15 04:08:24 +0000757 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000758 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000759 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000760 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000761
reed@google.com04863fa2011-05-15 04:08:24 +0000762 static const struct {
763 const char* fPathStr;
764 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000765 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000766 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000767 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
768 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
769 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
770 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
771 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
772 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
773 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
774 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000775 };
776
777 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
778 SkPath path;
779 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000780 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
781 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000782 }
783}
784
reed@google.com7e6c4d12012-05-10 14:05:43 +0000785static void test_isLine(skiatest::Reporter* reporter) {
786 SkPath path;
787 SkPoint pts[2];
788 const SkScalar value = SkIntToScalar(5);
789
790 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000791
reed@google.com7e6c4d12012-05-10 14:05:43 +0000792 // set some non-zero values
793 pts[0].set(value, value);
794 pts[1].set(value, value);
795 REPORTER_ASSERT(reporter, !path.isLine(pts));
796 // check that pts was untouched
797 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
798 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
799
800 const SkScalar moveX = SkIntToScalar(1);
801 const SkScalar moveY = SkIntToScalar(2);
802 SkASSERT(value != moveX && value != moveY);
803
804 path.moveTo(moveX, moveY);
805 REPORTER_ASSERT(reporter, !path.isLine(NULL));
806 REPORTER_ASSERT(reporter, !path.isLine(pts));
807 // check that pts was untouched
808 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
809 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
810
811 const SkScalar lineX = SkIntToScalar(2);
812 const SkScalar lineY = SkIntToScalar(2);
813 SkASSERT(value != lineX && value != lineY);
814
815 path.lineTo(lineX, lineY);
816 REPORTER_ASSERT(reporter, path.isLine(NULL));
817
818 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
819 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
820 REPORTER_ASSERT(reporter, path.isLine(pts));
821 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
822 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
823
824 path.lineTo(0, 0); // too many points/verbs
825 REPORTER_ASSERT(reporter, !path.isLine(NULL));
826 REPORTER_ASSERT(reporter, !path.isLine(pts));
827 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
828 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
829}
830
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000831static void test_conservativelyContains(skiatest::Reporter* reporter) {
832 SkPath path;
833
834 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
835 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
836
837 // A circle that bounds kBaseRect (with a significant amount of slop)
838 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
839 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
840 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
841
842 // round-rect radii
843 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +0000844
caryclark@google.com56f233a2012-11-19 13:06:06 +0000845 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000846 SkRect fQueryRect;
847 bool fInRect;
848 bool fInCircle;
849 bool fInRR;
850 } kQueries[] = {
851 {kBaseRect, true, true, false},
852
853 // rect well inside of kBaseRect
854 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
855 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
856 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
857 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
858 true, true, true},
859
860 // rects with edges off by one from kBaseRect's edges
861 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
862 kBaseRect.width(), kBaseRect.height() + 1),
863 false, true, false},
864 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
865 kBaseRect.width() + 1, kBaseRect.height()),
866 false, true, false},
867 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
868 kBaseRect.width() + 1, kBaseRect.height() + 1),
869 false, true, false},
870 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
871 kBaseRect.width(), kBaseRect.height()),
872 false, true, false},
873 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
874 kBaseRect.width(), kBaseRect.height()),
875 false, true, false},
876 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
877 kBaseRect.width() + 2, kBaseRect.height()),
878 false, true, false},
879 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
880 kBaseRect.width() + 2, kBaseRect.height()),
881 false, true, false},
882
883 // zero-w/h rects at each corner of kBaseRect
884 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
885 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
886 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
887 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
888
889 // far away rect
890 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
891 SkIntToScalar(10), SkIntToScalar(10)),
892 false, false, false},
893
894 // very large rect containing kBaseRect
895 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
896 kBaseRect.fTop - 5 * kBaseRect.height(),
897 11 * kBaseRect.width(), 11 * kBaseRect.height()),
898 false, false, false},
899
900 // skinny rect that spans same y-range as kBaseRect
901 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
902 SkIntToScalar(1), kBaseRect.height()),
903 true, true, true},
904
905 // short rect that spans same x-range as kBaseRect
906 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
907 true, true, true},
908
909 // skinny rect that spans slightly larger y-range than kBaseRect
910 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
911 SkIntToScalar(1), kBaseRect.height() + 1),
912 false, true, false},
913
914 // short rect that spans slightly larger x-range than kBaseRect
915 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
916 kBaseRect.width() + 1, SkScalar(1)),
917 false, true, false},
918 };
919
920 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +0000921 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000922 SkRect qRect = kQueries[q].fQueryRect;
923 if (inv & 0x1) {
924 SkTSwap(qRect.fLeft, qRect.fRight);
925 }
926 if (inv & 0x2) {
927 SkTSwap(qRect.fTop, qRect.fBottom);
928 }
929 for (int d = 0; d < 2; ++d) {
930 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
931 path.reset();
932 path.addRect(kBaseRect, dir);
933 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
934 path.conservativelyContainsRect(qRect));
935
936 path.reset();
937 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
938 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
939 path.conservativelyContainsRect(qRect));
940
941 path.reset();
942 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
943 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
944 path.conservativelyContainsRect(qRect));
945 }
946 // Slightly non-convex shape, shouldn't contain any rects.
947 path.reset();
948 path.moveTo(0, 0);
949 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
950 path.lineTo(SkIntToScalar(100), 0);
951 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
952 path.lineTo(0, SkIntToScalar(100));
953 path.close();
954 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
955 }
956 }
957
958 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
959 path.reset();
960 path.moveTo(0, 0);
961 path.lineTo(SkIntToScalar(100), 0);
962 path.lineTo(0, SkIntToScalar(100));
963
964 // inside, on along top edge
965 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
966 SkIntToScalar(10),
967 SkIntToScalar(10))));
968 // above
969 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
970 SkRect::MakeXYWH(SkIntToScalar(50),
971 SkIntToScalar(-10),
972 SkIntToScalar(10),
973 SkIntToScalar(10))));
974 // to the left
975 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
976 SkIntToScalar(5),
977 SkIntToScalar(5),
978 SkIntToScalar(5))));
979
980 // outside the diagonal edge
981 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
982 SkIntToScalar(200),
983 SkIntToScalar(20),
984 SkIntToScalar(5))));
985}
986
caryclark@google.comf1316942011-07-26 19:54:45 +0000987// Simple isRect test is inline TestPath, below.
988// test_isRect provides more extensive testing.
989static void test_isRect(skiatest::Reporter* reporter) {
990 // passing tests (all moveTo / lineTo...
991 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
992 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
993 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
994 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
995 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
996 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
997 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
998 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
999 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1000 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1001 {1, 0}, {.5f, 0}};
1002 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1003 {0, 1}, {0, .5f}};
1004 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1005 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1006 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001007 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001008
caryclark@google.comf1316942011-07-26 19:54:45 +00001009 // failing tests
1010 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1011 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1012 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1013 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1014 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1015 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1016 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1017 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001018 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1019 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1020 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001021
caryclark@google.comf1316942011-07-26 19:54:45 +00001022 // failing, no close
1023 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1024 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1025
1026 size_t testLen[] = {
1027 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1028 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001029 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001030 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001031 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001032 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001033 };
1034 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001035 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1036 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001037 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001038 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001039 SkPoint* lastPass = rf;
1040 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001041 bool fail = false;
1042 bool close = true;
1043 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1044 size_t index;
1045 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1046 SkPath path;
1047 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1048 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1049 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1050 }
1051 if (close) {
1052 path.close();
1053 }
1054 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001055 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1056
caryclark@google.com56f233a2012-11-19 13:06:06 +00001057 if (!fail) {
1058 SkRect computed, expected;
1059 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1060 REPORTER_ASSERT(reporter, path.isRect(&computed));
1061 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001062
caryclark@google.comf68154a2012-11-21 15:18:06 +00001063 bool isClosed;
1064 SkPath::Direction direction, cheapDirection;
1065 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1066 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1067 REPORTER_ASSERT(reporter, isClosed == close);
1068 REPORTER_ASSERT(reporter, direction == cheapDirection);
1069 } else {
1070 SkRect computed;
1071 computed.set(123, 456, 789, 1011);
1072 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1073 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1074 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1075
1076 bool isClosed = (bool) -1;
1077 SkPath::Direction direction = (SkPath::Direction) -1;
1078 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1079 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1080 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001081 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001082
caryclark@google.comf1316942011-07-26 19:54:45 +00001083 if (tests[testIndex] == lastPass) {
1084 fail = true;
1085 }
1086 if (tests[testIndex] == lastClose) {
1087 close = false;
1088 }
1089 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001090
caryclark@google.comf1316942011-07-26 19:54:45 +00001091 // fail, close then line
1092 SkPath path1;
1093 path1.moveTo(r1[0].fX, r1[0].fY);
1094 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1095 path1.lineTo(r1[index].fX, r1[index].fY);
1096 }
1097 path1.close();
1098 path1.lineTo(1, 0);
1099 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001100
caryclark@google.comf1316942011-07-26 19:54:45 +00001101 // fail, move in the middle
1102 path1.reset();
1103 path1.moveTo(r1[0].fX, r1[0].fY);
1104 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1105 if (index == 2) {
1106 path1.moveTo(1, .5f);
1107 }
1108 path1.lineTo(r1[index].fX, r1[index].fY);
1109 }
1110 path1.close();
1111 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1112
1113 // fail, move on the edge
1114 path1.reset();
1115 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1116 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1117 path1.lineTo(r1[index].fX, r1[index].fY);
1118 }
1119 path1.close();
1120 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001121
caryclark@google.comf1316942011-07-26 19:54:45 +00001122 // fail, quad
1123 path1.reset();
1124 path1.moveTo(r1[0].fX, r1[0].fY);
1125 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1126 if (index == 2) {
1127 path1.quadTo(1, .5f, 1, .5f);
1128 }
1129 path1.lineTo(r1[index].fX, r1[index].fY);
1130 }
1131 path1.close();
1132 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001133
caryclark@google.comf1316942011-07-26 19:54:45 +00001134 // fail, cubic
1135 path1.reset();
1136 path1.moveTo(r1[0].fX, r1[0].fY);
1137 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1138 if (index == 2) {
1139 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1140 }
1141 path1.lineTo(r1[index].fX, r1[index].fY);
1142 }
1143 path1.close();
1144 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1145}
1146
caryclark@google.com56f233a2012-11-19 13:06:06 +00001147static void test_isNestedRects(skiatest::Reporter* reporter) {
1148 // passing tests (all moveTo / lineTo...
1149 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1150 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1151 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1152 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1153 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1154 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1155 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1156 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1157 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1158 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1159 {1, 0}, {.5f, 0}};
1160 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1161 {0, 1}, {0, .5f}};
1162 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1163 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1164 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1165
1166 // failing tests
1167 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1168 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1169 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1170 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1171 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1172 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1173 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1174 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1175
1176 // failing, no close
1177 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1178 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1179
1180 size_t testLen[] = {
1181 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1182 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1183 sizeof(rd), sizeof(re),
1184 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1185 sizeof(f7), sizeof(f8),
1186 sizeof(c1), sizeof(c2)
1187 };
1188 SkPoint* tests[] = {
1189 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1190 f1, f2, f3, f4, f5, f6, f7, f8,
1191 c1, c2
1192 };
1193 const SkPoint* lastPass = re;
1194 const SkPoint* lastClose = f8;
1195 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1196 size_t index;
1197 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1198 bool fail = false;
1199 bool close = true;
1200 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1201 SkPath path;
1202 if (rectFirst) {
1203 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1204 }
1205 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1206 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1207 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1208 }
1209 if (close) {
1210 path.close();
1211 }
1212 if (!rectFirst) {
1213 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1214 }
1215 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1216 if (!fail) {
1217 SkRect expected[2], computed[2];
1218 SkRect testBounds;
1219 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1220 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1221 expected[1] = testBounds;
1222 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1223 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1224 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1225 }
1226 if (tests[testIndex] == lastPass) {
1227 fail = true;
1228 }
1229 if (tests[testIndex] == lastClose) {
1230 close = false;
1231 }
1232 }
1233
1234 // fail, close then line
1235 SkPath path1;
1236 if (rectFirst) {
1237 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1238 }
1239 path1.moveTo(r1[0].fX, r1[0].fY);
1240 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1241 path1.lineTo(r1[index].fX, r1[index].fY);
1242 }
1243 path1.close();
1244 path1.lineTo(1, 0);
1245 if (!rectFirst) {
1246 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1247 }
1248 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1249
1250 // fail, move in the middle
1251 path1.reset();
1252 if (rectFirst) {
1253 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1254 }
1255 path1.moveTo(r1[0].fX, r1[0].fY);
1256 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1257 if (index == 2) {
1258 path1.moveTo(1, .5f);
1259 }
1260 path1.lineTo(r1[index].fX, r1[index].fY);
1261 }
1262 path1.close();
1263 if (!rectFirst) {
1264 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1265 }
1266 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1267
1268 // fail, move on the edge
1269 path1.reset();
1270 if (rectFirst) {
1271 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1272 }
1273 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1274 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1275 path1.lineTo(r1[index].fX, r1[index].fY);
1276 }
1277 path1.close();
1278 if (!rectFirst) {
1279 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1280 }
1281 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1282
1283 // fail, quad
1284 path1.reset();
1285 if (rectFirst) {
1286 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1287 }
1288 path1.moveTo(r1[0].fX, r1[0].fY);
1289 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1290 if (index == 2) {
1291 path1.quadTo(1, .5f, 1, .5f);
1292 }
1293 path1.lineTo(r1[index].fX, r1[index].fY);
1294 }
1295 path1.close();
1296 if (!rectFirst) {
1297 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1298 }
1299 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1300
1301 // fail, cubic
1302 path1.reset();
1303 if (rectFirst) {
1304 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1305 }
1306 path1.moveTo(r1[0].fX, r1[0].fY);
1307 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1308 if (index == 2) {
1309 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1310 }
1311 path1.lineTo(r1[index].fX, r1[index].fY);
1312 }
1313 path1.close();
1314 if (!rectFirst) {
1315 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1316 }
1317 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001318
caryclark@google.com56f233a2012-11-19 13:06:06 +00001319 // fail, not nested
1320 path1.reset();
1321 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1322 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1323 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1324 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001325
1326 // pass, stroke rect
1327 SkPath src, dst;
1328 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1329 SkPaint strokePaint;
1330 strokePaint.setStyle(SkPaint::kStroke_Style);
1331 strokePaint.setStrokeWidth(2);
1332 strokePaint.getFillPath(src, &dst);
1333 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001334}
1335
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001336static void write_and_read_back(skiatest::Reporter* reporter,
1337 const SkPath& p) {
1338 SkWriter32 writer(100);
1339 writer.writePath(p);
1340 size_t size = writer.size();
1341 SkAutoMalloc storage(size);
1342 writer.flatten(storage.get());
1343 SkReader32 reader(storage.get(), size);
1344
1345 SkPath readBack;
1346 REPORTER_ASSERT(reporter, readBack != p);
1347 reader.readPath(&readBack);
1348 REPORTER_ASSERT(reporter, readBack == p);
1349
rmistry@google.comd6176b02012-08-23 18:14:13 +00001350 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001351 p.getConvexityOrUnknown());
1352
1353 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1354
1355 const SkRect& origBounds = p.getBounds();
1356 const SkRect& readBackBounds = readBack.getBounds();
1357
1358 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1359}
1360
reed@google.com53effc52011-09-21 19:05:12 +00001361static void test_flattening(skiatest::Reporter* reporter) {
1362 SkPath p;
1363
1364 static const SkPoint pts[] = {
1365 { 0, 0 },
1366 { SkIntToScalar(10), SkIntToScalar(10) },
1367 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1368 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1369 };
1370 p.moveTo(pts[0]);
1371 p.lineTo(pts[1]);
1372 p.quadTo(pts[2], pts[3]);
1373 p.cubicTo(pts[4], pts[5], pts[6]);
1374
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001375 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001376
1377 // create a buffer that should be much larger than the path so we don't
1378 // kill our stack if writer goes too far.
1379 char buffer[1024];
1380 uint32_t size1 = p.writeToMemory(NULL);
1381 uint32_t size2 = p.writeToMemory(buffer);
1382 REPORTER_ASSERT(reporter, size1 == size2);
1383
1384 SkPath p2;
1385 uint32_t size3 = p2.readFromMemory(buffer);
1386 REPORTER_ASSERT(reporter, size1 == size3);
1387 REPORTER_ASSERT(reporter, p == p2);
1388
1389 char buffer2[1024];
1390 size3 = p2.writeToMemory(buffer2);
1391 REPORTER_ASSERT(reporter, size1 == size3);
1392 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001393
1394 // test persistence of the oval flag & convexity
1395 {
1396 SkPath oval;
1397 SkRect rect = SkRect::MakeWH(10, 10);
1398 oval.addOval(rect);
1399
1400 write_and_read_back(reporter, oval);
1401 }
reed@google.com53effc52011-09-21 19:05:12 +00001402}
1403
1404static void test_transform(skiatest::Reporter* reporter) {
1405 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001406
reed@google.com53effc52011-09-21 19:05:12 +00001407 static const SkPoint pts[] = {
1408 { 0, 0 },
1409 { SkIntToScalar(10), SkIntToScalar(10) },
1410 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1411 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1412 };
1413 p.moveTo(pts[0]);
1414 p.lineTo(pts[1]);
1415 p.quadTo(pts[2], pts[3]);
1416 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001417
reed@google.com53effc52011-09-21 19:05:12 +00001418 SkMatrix matrix;
1419 matrix.reset();
1420 p.transform(matrix, &p1);
1421 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001422
reed@google.com53effc52011-09-21 19:05:12 +00001423 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1424 p.transform(matrix, &p1);
1425 SkPoint pts1[7];
1426 int count = p1.getPoints(pts1, 7);
1427 REPORTER_ASSERT(reporter, 7 == count);
1428 for (int i = 0; i < count; ++i) {
1429 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1430 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1431 }
1432}
1433
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001434static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001435 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001436 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001437
caryclark@google.com56f233a2012-11-19 13:06:06 +00001438 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001439 const char* testPath;
1440 const size_t numResultPts;
1441 const SkRect resultBound;
1442 const SkPath::Verb* resultVerbs;
1443 const size_t numResultVerbs;
1444 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001445
schenney@chromium.org7e963602012-06-13 17:05:43 +00001446 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1447 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1448 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1449 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1450 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1451 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1452 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1453 static const SkPath::Verb resultVerbs8[] = {
1454 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1455 };
1456 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1457 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1458 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1459 static const SkPath::Verb resultVerbs12[] = {
1460 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1461 };
1462 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1463 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1464 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1465 static const SkPath::Verb resultVerbs16[] = {
1466 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1467 };
1468 static const struct zeroPathTestData gZeroLengthTests[] = {
1469 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001470 { "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 +00001471 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001472 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1473 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1474 { "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) },
1475 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1476 { "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) },
1477 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1478 { "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) },
1479 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1480 { "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) },
1481 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1482 { "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 +00001483 SK_ARRAY_COUNT(resultVerbs14)
1484 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001485 { "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) },
1486 { "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 +00001487 SK_ARRAY_COUNT(resultVerbs16)
1488 }
1489 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001490
schenney@chromium.org7e963602012-06-13 17:05:43 +00001491 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1492 p.reset();
1493 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1494 REPORTER_ASSERT(reporter, valid);
1495 REPORTER_ASSERT(reporter, !p.isEmpty());
1496 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1497 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1498 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1499 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1500 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1501 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001502 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001503}
1504
1505struct SegmentInfo {
1506 SkPath fPath;
1507 int fPointCount;
1508};
1509
reed@google.com10296cc2011-09-21 12:29:05 +00001510#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1511
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001512static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001513 SkPath p, p2;
1514
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001515 p.moveTo(0, 0);
1516 p.quadTo(100, 100, 200, 200);
1517 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1518 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001519 p2 = p;
1520 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001521 p.cubicTo(100, 100, 200, 200, 300, 300);
1522 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1523 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001524 p2 = p;
1525 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1526
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001527 p.reset();
1528 p.moveTo(0, 0);
1529 p.cubicTo(100, 100, 200, 200, 300, 300);
1530 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001531 p2 = p;
1532 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001533
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001534 REPORTER_ASSERT(reporter, !p.isEmpty());
1535}
1536
1537static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001538 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001539 SkPoint pts[4];
1540
1541 // Test an iterator with no path
1542 SkPath::Iter noPathIter;
1543 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001544
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001545 // Test that setting an empty path works
1546 noPathIter.setPath(p, false);
1547 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001548
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001549 // Test that close path makes no difference for an empty path
1550 noPathIter.setPath(p, true);
1551 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001552
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001553 // Test an iterator with an initial empty path
1554 SkPath::Iter iter(p, false);
1555 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1556
1557 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001558 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001559 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1560
rmistry@google.comd6176b02012-08-23 18:14:13 +00001561
schenney@chromium.org7e963602012-06-13 17:05:43 +00001562 struct iterTestData {
1563 const char* testPath;
1564 const bool forceClose;
1565 const bool consumeDegenerates;
1566 const size_t* numResultPtsPerVerb;
1567 const SkPoint* resultPts;
1568 const SkPath::Verb* resultVerbs;
1569 const size_t numResultVerbs;
1570 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001571
schenney@chromium.org7e963602012-06-13 17:05:43 +00001572 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1573 static const SkPath::Verb resultVerbs2[] = {
1574 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1575 };
1576 static const SkPath::Verb resultVerbs3[] = {
1577 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1578 };
1579 static const SkPath::Verb resultVerbs4[] = {
1580 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1581 };
1582 static const SkPath::Verb resultVerbs5[] = {
1583 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1584 };
1585 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001586 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1587 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1588 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1589 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001590 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001591 static const SkPoint resultPts2[] = {
1592 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1593 };
1594 static const SkPoint resultPts3[] = {
1595 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1596 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1597 };
1598 static const SkPoint resultPts4[] = {
1599 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1600 };
1601 static const SkPoint resultPts5[] = {
1602 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1603 };
1604 static const struct iterTestData gIterTests[] = {
1605 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001606 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1607 { "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 +00001608 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1609 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1610 { "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) },
1611 { "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 +00001612 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1613 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1614 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1615 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1616 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1617 { "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 +00001618 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001619
schenney@chromium.org7e963602012-06-13 17:05:43 +00001620 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1621 p.reset();
1622 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1623 REPORTER_ASSERT(reporter, valid);
1624 iter.setPath(p, gIterTests[i].forceClose);
1625 int j = 0, l = 0;
1626 do {
1627 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1628 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1629 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1630 }
1631 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1632 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1633 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001634
1635 // The GM degeneratesegments.cpp test is more extensive
1636}
1637
1638static void test_raw_iter(skiatest::Reporter* reporter) {
1639 SkPath p;
1640 SkPoint pts[4];
1641
1642 // Test an iterator with no path
1643 SkPath::RawIter noPathIter;
1644 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1645 // Test that setting an empty path works
1646 noPathIter.setPath(p);
1647 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001648
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001649 // Test an iterator with an initial empty path
1650 SkPath::RawIter iter(p);
1651 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1652
1653 // Test that a move-only path returns the move.
1654 p.moveTo(SK_Scalar1, 0);
1655 iter.setPath(p);
1656 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1657 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1658 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1659 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1660
1661 // No matter how many moves we add, we should get them all back
1662 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1663 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1664 iter.setPath(p);
1665 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1666 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1667 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1668 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1669 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1670 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1671 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1672 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1673 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1674 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1675
1676 // Initial close is never ever stored
1677 p.reset();
1678 p.close();
1679 iter.setPath(p);
1680 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1681
1682 // Move/close sequences
1683 p.reset();
1684 p.close(); // Not stored, no purpose
1685 p.moveTo(SK_Scalar1, 0);
1686 p.close();
1687 p.close(); // Not stored, no purpose
1688 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1689 p.close();
1690 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1691 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1692 p.close();
1693 iter.setPath(p);
1694 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1695 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1696 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1697 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1698 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1699 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1700 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1701 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1702 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1703 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1704 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1705 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1706 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1707 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1708 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1709 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1710 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1711 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1712 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1713 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1714 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1715 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1716
1717 // Generate random paths and verify
1718 SkPoint randomPts[25];
1719 for (int i = 0; i < 5; ++i) {
1720 for (int j = 0; j < 5; ++j) {
1721 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1722 }
1723 }
1724
1725 // Max of 10 segments, max 3 points per segment
1726 SkRandom rand(9876543);
1727 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001728 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001729 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001730
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001731 for (int i = 0; i < 500; ++i) {
1732 p.reset();
1733 bool lastWasClose = true;
1734 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001735 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001736 int numPoints = 0;
1737 int numVerbs = (rand.nextU() >> 16) % 10;
1738 int numIterVerbs = 0;
1739 for (int j = 0; j < numVerbs; ++j) {
1740 do {
1741 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1742 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001743 switch (nextVerb) {
1744 case SkPath::kMove_Verb:
1745 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1746 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001747 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001748 numPoints += 1;
1749 lastWasClose = false;
1750 haveMoveTo = true;
1751 break;
1752 case SkPath::kLine_Verb:
1753 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001754 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001755 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1756 haveMoveTo = true;
1757 }
1758 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1759 p.lineTo(expectedPts[numPoints]);
1760 numPoints += 1;
1761 lastWasClose = false;
1762 break;
1763 case SkPath::kQuad_Verb:
1764 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001765 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001766 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1767 haveMoveTo = true;
1768 }
1769 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1770 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1771 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1772 numPoints += 2;
1773 lastWasClose = false;
1774 break;
1775 case SkPath::kCubic_Verb:
1776 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001777 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001778 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1779 haveMoveTo = true;
1780 }
1781 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1782 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1783 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1784 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1785 expectedPts[numPoints + 2]);
1786 numPoints += 3;
1787 lastWasClose = false;
1788 break;
1789 case SkPath::kClose_Verb:
1790 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001791 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001792 lastWasClose = true;
1793 break;
1794 default:;
1795 }
1796 expectedVerbs[numIterVerbs++] = nextVerb;
1797 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001798
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001799 iter.setPath(p);
1800 numVerbs = numIterVerbs;
1801 numIterVerbs = 0;
1802 int numIterPts = 0;
1803 SkPoint lastMoveTo;
1804 SkPoint lastPt;
1805 lastMoveTo.set(0, 0);
1806 lastPt.set(0, 0);
1807 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1808 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1809 numIterVerbs++;
1810 switch (nextVerb) {
1811 case SkPath::kMove_Verb:
1812 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1813 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1814 lastPt = lastMoveTo = pts[0];
1815 numIterPts += 1;
1816 break;
1817 case SkPath::kLine_Verb:
1818 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1819 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1820 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1821 lastPt = pts[1];
1822 numIterPts += 1;
1823 break;
1824 case SkPath::kQuad_Verb:
1825 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1826 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1827 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1828 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1829 lastPt = pts[2];
1830 numIterPts += 2;
1831 break;
1832 case SkPath::kCubic_Verb:
1833 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1834 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1835 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1836 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1837 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1838 lastPt = pts[3];
1839 numIterPts += 3;
1840 break;
1841 case SkPath::kClose_Verb:
1842 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1843 lastPt = lastMoveTo;
1844 break;
1845 default:;
1846 }
1847 }
1848 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1849 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1850 }
1851}
1852
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001853static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001854 const SkPath& path,
1855 bool expectedCircle,
1856 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001857 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001858 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1859 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001860
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001861 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001862 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1863 }
1864}
1865
1866static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001867 const SkPath& path,
1868 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001869 SkPath tmp;
1870
1871 SkMatrix m;
1872 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1873 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001874 // this matrix reverses the direction.
1875 if (SkPath::kCCW_Direction == dir) {
1876 dir = SkPath::kCW_Direction;
1877 } else {
1878 SkASSERT(SkPath::kCW_Direction == dir);
1879 dir = SkPath::kCCW_Direction;
1880 }
1881 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001882}
1883
1884static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001885 const SkPath& path,
1886 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001887 SkPath tmp;
1888
1889 // translate at small offset
1890 SkMatrix m;
1891 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1892 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001893 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001894
1895 tmp.reset();
1896 m.reset();
1897
1898 // translate at a relatively big offset
1899 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1900 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001901 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001902}
1903
1904static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001905 const SkPath& path,
1906 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001907 for (int angle = 0; angle < 360; ++angle) {
1908 SkPath tmp;
1909 SkMatrix m;
1910 m.setRotate(SkIntToScalar(angle));
1911 path.transform(m, &tmp);
1912
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001913 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001914 // degrees is not an oval anymore, this can be improved. we made this
1915 // for the simplicity of our implementation.
1916 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001917 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001918 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001919 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001920 }
1921 }
1922}
1923
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001924static void test_circle_mirror_x(skiatest::Reporter* reporter,
1925 const SkPath& path,
1926 SkPath::Direction dir) {
1927 SkPath tmp;
1928 SkMatrix m;
1929 m.reset();
1930 m.setScaleX(-SK_Scalar1);
1931 path.transform(m, &tmp);
1932
1933 if (SkPath::kCW_Direction == dir) {
1934 dir = SkPath::kCCW_Direction;
1935 } else {
1936 SkASSERT(SkPath::kCCW_Direction == dir);
1937 dir = SkPath::kCW_Direction;
1938 }
1939
1940 check_for_circle(reporter, tmp, true, dir);
1941}
1942
1943static void test_circle_mirror_y(skiatest::Reporter* reporter,
1944 const SkPath& path,
1945 SkPath::Direction dir) {
1946 SkPath tmp;
1947 SkMatrix m;
1948 m.reset();
1949 m.setScaleY(-SK_Scalar1);
1950 path.transform(m, &tmp);
1951
1952 if (SkPath::kCW_Direction == dir) {
1953 dir = SkPath::kCCW_Direction;
1954 } else {
1955 SkASSERT(SkPath::kCCW_Direction == dir);
1956 dir = SkPath::kCW_Direction;
1957 }
1958
1959 check_for_circle(reporter, tmp, true, dir);
1960}
1961
1962static void test_circle_mirror_xy(skiatest::Reporter* reporter,
1963 const SkPath& path,
1964 SkPath::Direction dir) {
1965 SkPath tmp;
1966 SkMatrix m;
1967 m.reset();
1968 m.setScaleX(-SK_Scalar1);
1969 m.setScaleY(-SK_Scalar1);
1970 path.transform(m, &tmp);
1971
1972 check_for_circle(reporter, tmp, true, dir);
1973}
1974
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001975static void test_circle_with_direction(skiatest::Reporter* reporter,
1976 SkPath::Direction dir) {
1977 SkPath path;
1978
1979 // circle at origin
1980 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001981 check_for_circle(reporter, path, true, dir);
1982 test_circle_rotate(reporter, path, dir);
1983 test_circle_translate(reporter, path, dir);
1984 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001985
1986 // circle at an offset at (10, 10)
1987 path.reset();
1988 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1989 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001990 check_for_circle(reporter, path, true, dir);
1991 test_circle_rotate(reporter, path, dir);
1992 test_circle_translate(reporter, path, dir);
1993 test_circle_skew(reporter, path, dir);
1994 test_circle_mirror_x(reporter, path, dir);
1995 test_circle_mirror_y(reporter, path, dir);
1996 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001997}
1998
1999static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2000 SkPath path;
2001 SkPath circle;
2002 SkPath rect;
2003 SkPath empty;
2004
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002005 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2006 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2007
2008 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002009 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2010 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2011
2012 SkMatrix translate;
2013 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2014
2015 // For simplicity, all the path concatenation related operations
2016 // would mark it non-circle, though in theory it's still a circle.
2017
2018 // empty + circle (translate)
2019 path = empty;
2020 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002021 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002022
2023 // circle + empty (translate)
2024 path = circle;
2025 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002026 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002027
2028 // test reverseAddPath
2029 path = circle;
2030 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002031 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002032}
2033
2034static void test_circle(skiatest::Reporter* reporter) {
2035 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2036 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2037
2038 // multiple addCircle()
2039 SkPath path;
2040 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2041 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002042 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002043
2044 // some extra lineTo() would make isOval() fail
2045 path.reset();
2046 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2047 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002048 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002049
2050 // not back to the original point
2051 path.reset();
2052 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2053 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002054 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002055
2056 test_circle_with_add_paths(reporter);
2057}
2058
2059static void test_oval(skiatest::Reporter* reporter) {
2060 SkRect rect;
2061 SkMatrix m;
2062 SkPath path;
2063
2064 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2065 path.addOval(rect);
2066
2067 REPORTER_ASSERT(reporter, path.isOval(NULL));
2068
2069 m.setRotate(SkIntToScalar(90));
2070 SkPath tmp;
2071 path.transform(m, &tmp);
2072 // an oval rotated 90 degrees is still an oval.
2073 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2074
2075 m.reset();
2076 m.setRotate(SkIntToScalar(30));
2077 tmp.reset();
2078 path.transform(m, &tmp);
2079 // an oval rotated 30 degrees is not an oval anymore.
2080 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2081
2082 // since empty path being transformed.
2083 path.reset();
2084 tmp.reset();
2085 m.reset();
2086 path.transform(m, &tmp);
2087 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2088
2089 // empty path is not an oval
2090 tmp.reset();
2091 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2092
2093 // only has moveTo()s
2094 tmp.reset();
2095 tmp.moveTo(0, 0);
2096 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2097 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2098
2099 // mimic WebKit's calling convention,
2100 // call moveTo() first and then call addOval()
2101 path.reset();
2102 path.moveTo(0, 0);
2103 path.addOval(rect);
2104 REPORTER_ASSERT(reporter, path.isOval(NULL));
2105
2106 // copy path
2107 path.reset();
2108 tmp.reset();
2109 tmp.addOval(rect);
2110 path = tmp;
2111 REPORTER_ASSERT(reporter, path.isOval(NULL));
2112}
2113
caryclark@google.com42639cd2012-06-06 12:03:39 +00002114static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002115 SkTSize<SkScalar>::Make(3,4);
2116
reed@android.com3abec1d2009-03-02 05:36:20 +00002117 SkPath p, p2;
2118 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002119
reed@android.com3abec1d2009-03-02 05:36:20 +00002120 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002121 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002122 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002123 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002124 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002125 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2126 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2127 REPORTER_ASSERT(reporter, p == p2);
2128 REPORTER_ASSERT(reporter, !(p != p2));
2129
reed@android.comd252db02009-04-01 18:31:44 +00002130 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002131
reed@android.com3abec1d2009-03-02 05:36:20 +00002132 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002133
reed@android.com6b82d1a2009-06-03 02:35:01 +00002134 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2135 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002136 // we have quads or cubics
2137 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002138 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002139
reed@android.com6b82d1a2009-06-03 02:35:01 +00002140 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002141 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002142 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002143
reed@android.com6b82d1a2009-06-03 02:35:01 +00002144 p.addOval(bounds);
2145 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002146 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002147
reed@android.com6b82d1a2009-06-03 02:35:01 +00002148 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002149 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002150 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002151 // we have only lines
2152 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002153 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002154
2155 REPORTER_ASSERT(reporter, p != p2);
2156 REPORTER_ASSERT(reporter, !(p == p2));
2157
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002158 // do getPoints and getVerbs return the right result
2159 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2160 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002161 SkPoint pts[4];
2162 int count = p.getPoints(pts, 4);
2163 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002164 uint8_t verbs[6];
2165 verbs[5] = 0xff;
2166 p.getVerbs(verbs, 5);
2167 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2168 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2169 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2170 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2171 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2172 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002173 bounds2.set(pts, 4);
2174 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002175
reed@android.com3abec1d2009-03-02 05:36:20 +00002176 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2177 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002178 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002179
reed@android.com3abec1d2009-03-02 05:36:20 +00002180 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002181 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002182 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2183 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002184
reed@android.com3abec1d2009-03-02 05:36:20 +00002185 // now force p to not be a rect
2186 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2187 p.addRect(bounds);
2188 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002189
reed@google.com7e6c4d12012-05-10 14:05:43 +00002190 test_isLine(reporter);
2191 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002192 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002193 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002194 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002195 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002196 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002197 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002198 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002199 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002200 test_flattening(reporter);
2201 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002202 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002203 test_iter(reporter);
2204 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002205 test_circle(reporter);
2206 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002207 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002208 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002209 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002210 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002211 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002212 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002213 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002214 test_addrect_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002215}
2216
2217#include "TestClassDef.h"
2218DEFINE_TESTCLASS("Path", PathTestClass, TestPath)