blob: bd41e19ac76ba4f2f3276c6c898db69c0bb6344f [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.com56f233a2012-11-19 13:06:06 +00001055 if (!fail) {
1056 SkRect computed, expected;
1057 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1058 REPORTER_ASSERT(reporter, path.isRect(&computed));
1059 REPORTER_ASSERT(reporter, expected == computed);
1060 }
caryclark@google.comf1316942011-07-26 19:54:45 +00001061 if (tests[testIndex] == lastPass) {
1062 fail = true;
1063 }
1064 if (tests[testIndex] == lastClose) {
1065 close = false;
1066 }
1067 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001068
caryclark@google.comf1316942011-07-26 19:54:45 +00001069 // fail, close then line
1070 SkPath path1;
1071 path1.moveTo(r1[0].fX, r1[0].fY);
1072 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1073 path1.lineTo(r1[index].fX, r1[index].fY);
1074 }
1075 path1.close();
1076 path1.lineTo(1, 0);
1077 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001078
caryclark@google.comf1316942011-07-26 19:54:45 +00001079 // fail, move in the middle
1080 path1.reset();
1081 path1.moveTo(r1[0].fX, r1[0].fY);
1082 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1083 if (index == 2) {
1084 path1.moveTo(1, .5f);
1085 }
1086 path1.lineTo(r1[index].fX, r1[index].fY);
1087 }
1088 path1.close();
1089 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1090
1091 // fail, move on the edge
1092 path1.reset();
1093 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1094 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1095 path1.lineTo(r1[index].fX, r1[index].fY);
1096 }
1097 path1.close();
1098 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001099
caryclark@google.comf1316942011-07-26 19:54:45 +00001100 // fail, quad
1101 path1.reset();
1102 path1.moveTo(r1[0].fX, r1[0].fY);
1103 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1104 if (index == 2) {
1105 path1.quadTo(1, .5f, 1, .5f);
1106 }
1107 path1.lineTo(r1[index].fX, r1[index].fY);
1108 }
1109 path1.close();
1110 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001111
caryclark@google.comf1316942011-07-26 19:54:45 +00001112 // fail, cubic
1113 path1.reset();
1114 path1.moveTo(r1[0].fX, r1[0].fY);
1115 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1116 if (index == 2) {
1117 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1118 }
1119 path1.lineTo(r1[index].fX, r1[index].fY);
1120 }
1121 path1.close();
1122 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1123}
1124
caryclark@google.com56f233a2012-11-19 13:06:06 +00001125static void test_isNestedRects(skiatest::Reporter* reporter) {
1126 // passing tests (all moveTo / lineTo...
1127 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1128 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1129 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1130 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1131 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1132 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1133 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1134 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1135 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1136 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1137 {1, 0}, {.5f, 0}};
1138 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1139 {0, 1}, {0, .5f}};
1140 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1141 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1142 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1143
1144 // failing tests
1145 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1146 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1147 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1148 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1149 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1150 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1151 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1152 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1153
1154 // failing, no close
1155 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1156 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1157
1158 size_t testLen[] = {
1159 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1160 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1161 sizeof(rd), sizeof(re),
1162 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1163 sizeof(f7), sizeof(f8),
1164 sizeof(c1), sizeof(c2)
1165 };
1166 SkPoint* tests[] = {
1167 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1168 f1, f2, f3, f4, f5, f6, f7, f8,
1169 c1, c2
1170 };
1171 const SkPoint* lastPass = re;
1172 const SkPoint* lastClose = f8;
1173 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1174 size_t index;
1175 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1176 bool fail = false;
1177 bool close = true;
1178 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1179 SkPath path;
1180 if (rectFirst) {
1181 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1182 }
1183 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1184 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1185 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1186 }
1187 if (close) {
1188 path.close();
1189 }
1190 if (!rectFirst) {
1191 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1192 }
1193 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1194 if (!fail) {
1195 SkRect expected[2], computed[2];
1196 SkRect testBounds;
1197 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1198 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1199 expected[1] = testBounds;
1200 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1201 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1202 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1203 }
1204 if (tests[testIndex] == lastPass) {
1205 fail = true;
1206 }
1207 if (tests[testIndex] == lastClose) {
1208 close = false;
1209 }
1210 }
1211
1212 // fail, close then line
1213 SkPath path1;
1214 if (rectFirst) {
1215 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1216 }
1217 path1.moveTo(r1[0].fX, r1[0].fY);
1218 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1219 path1.lineTo(r1[index].fX, r1[index].fY);
1220 }
1221 path1.close();
1222 path1.lineTo(1, 0);
1223 if (!rectFirst) {
1224 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1225 }
1226 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1227
1228 // fail, move in the middle
1229 path1.reset();
1230 if (rectFirst) {
1231 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1232 }
1233 path1.moveTo(r1[0].fX, r1[0].fY);
1234 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1235 if (index == 2) {
1236 path1.moveTo(1, .5f);
1237 }
1238 path1.lineTo(r1[index].fX, r1[index].fY);
1239 }
1240 path1.close();
1241 if (!rectFirst) {
1242 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1243 }
1244 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1245
1246 // fail, move on the edge
1247 path1.reset();
1248 if (rectFirst) {
1249 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1250 }
1251 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1252 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1253 path1.lineTo(r1[index].fX, r1[index].fY);
1254 }
1255 path1.close();
1256 if (!rectFirst) {
1257 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1258 }
1259 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1260
1261 // fail, quad
1262 path1.reset();
1263 if (rectFirst) {
1264 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1265 }
1266 path1.moveTo(r1[0].fX, r1[0].fY);
1267 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1268 if (index == 2) {
1269 path1.quadTo(1, .5f, 1, .5f);
1270 }
1271 path1.lineTo(r1[index].fX, r1[index].fY);
1272 }
1273 path1.close();
1274 if (!rectFirst) {
1275 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1276 }
1277 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1278
1279 // fail, cubic
1280 path1.reset();
1281 if (rectFirst) {
1282 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1283 }
1284 path1.moveTo(r1[0].fX, r1[0].fY);
1285 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1286 if (index == 2) {
1287 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1288 }
1289 path1.lineTo(r1[index].fX, r1[index].fY);
1290 }
1291 path1.close();
1292 if (!rectFirst) {
1293 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1294 }
1295 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001296
caryclark@google.com56f233a2012-11-19 13:06:06 +00001297 // fail, not nested
1298 path1.reset();
1299 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1300 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1301 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1302 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001303
1304 // pass, stroke rect
1305 SkPath src, dst;
1306 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1307 SkPaint strokePaint;
1308 strokePaint.setStyle(SkPaint::kStroke_Style);
1309 strokePaint.setStrokeWidth(2);
1310 strokePaint.getFillPath(src, &dst);
1311 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001312}
1313
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001314static void write_and_read_back(skiatest::Reporter* reporter,
1315 const SkPath& p) {
1316 SkWriter32 writer(100);
1317 writer.writePath(p);
1318 size_t size = writer.size();
1319 SkAutoMalloc storage(size);
1320 writer.flatten(storage.get());
1321 SkReader32 reader(storage.get(), size);
1322
1323 SkPath readBack;
1324 REPORTER_ASSERT(reporter, readBack != p);
1325 reader.readPath(&readBack);
1326 REPORTER_ASSERT(reporter, readBack == p);
1327
rmistry@google.comd6176b02012-08-23 18:14:13 +00001328 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001329 p.getConvexityOrUnknown());
1330
1331 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1332
1333 const SkRect& origBounds = p.getBounds();
1334 const SkRect& readBackBounds = readBack.getBounds();
1335
1336 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1337}
1338
reed@google.com53effc52011-09-21 19:05:12 +00001339static void test_flattening(skiatest::Reporter* reporter) {
1340 SkPath p;
1341
1342 static const SkPoint pts[] = {
1343 { 0, 0 },
1344 { SkIntToScalar(10), SkIntToScalar(10) },
1345 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1346 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1347 };
1348 p.moveTo(pts[0]);
1349 p.lineTo(pts[1]);
1350 p.quadTo(pts[2], pts[3]);
1351 p.cubicTo(pts[4], pts[5], pts[6]);
1352
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001353 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001354
1355 // create a buffer that should be much larger than the path so we don't
1356 // kill our stack if writer goes too far.
1357 char buffer[1024];
1358 uint32_t size1 = p.writeToMemory(NULL);
1359 uint32_t size2 = p.writeToMemory(buffer);
1360 REPORTER_ASSERT(reporter, size1 == size2);
1361
1362 SkPath p2;
1363 uint32_t size3 = p2.readFromMemory(buffer);
1364 REPORTER_ASSERT(reporter, size1 == size3);
1365 REPORTER_ASSERT(reporter, p == p2);
1366
1367 char buffer2[1024];
1368 size3 = p2.writeToMemory(buffer2);
1369 REPORTER_ASSERT(reporter, size1 == size3);
1370 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001371
1372 // test persistence of the oval flag & convexity
1373 {
1374 SkPath oval;
1375 SkRect rect = SkRect::MakeWH(10, 10);
1376 oval.addOval(rect);
1377
1378 write_and_read_back(reporter, oval);
1379 }
reed@google.com53effc52011-09-21 19:05:12 +00001380}
1381
1382static void test_transform(skiatest::Reporter* reporter) {
1383 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001384
reed@google.com53effc52011-09-21 19:05:12 +00001385 static const SkPoint pts[] = {
1386 { 0, 0 },
1387 { SkIntToScalar(10), SkIntToScalar(10) },
1388 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1389 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1390 };
1391 p.moveTo(pts[0]);
1392 p.lineTo(pts[1]);
1393 p.quadTo(pts[2], pts[3]);
1394 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001395
reed@google.com53effc52011-09-21 19:05:12 +00001396 SkMatrix matrix;
1397 matrix.reset();
1398 p.transform(matrix, &p1);
1399 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001400
reed@google.com53effc52011-09-21 19:05:12 +00001401 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1402 p.transform(matrix, &p1);
1403 SkPoint pts1[7];
1404 int count = p1.getPoints(pts1, 7);
1405 REPORTER_ASSERT(reporter, 7 == count);
1406 for (int i = 0; i < count; ++i) {
1407 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1408 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1409 }
1410}
1411
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001412static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001413 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001414 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001415
caryclark@google.com56f233a2012-11-19 13:06:06 +00001416 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001417 const char* testPath;
1418 const size_t numResultPts;
1419 const SkRect resultBound;
1420 const SkPath::Verb* resultVerbs;
1421 const size_t numResultVerbs;
1422 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001423
schenney@chromium.org7e963602012-06-13 17:05:43 +00001424 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1425 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1426 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1427 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1428 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1429 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1430 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1431 static const SkPath::Verb resultVerbs8[] = {
1432 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1433 };
1434 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1435 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1436 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1437 static const SkPath::Verb resultVerbs12[] = {
1438 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1439 };
1440 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1441 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1442 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1443 static const SkPath::Verb resultVerbs16[] = {
1444 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1445 };
1446 static const struct zeroPathTestData gZeroLengthTests[] = {
1447 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001448 { "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 +00001449 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001450 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1451 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1452 { "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) },
1453 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1454 { "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) },
1455 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1456 { "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) },
1457 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1458 { "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) },
1459 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1460 { "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 +00001461 SK_ARRAY_COUNT(resultVerbs14)
1462 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001463 { "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) },
1464 { "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 +00001465 SK_ARRAY_COUNT(resultVerbs16)
1466 }
1467 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001468
schenney@chromium.org7e963602012-06-13 17:05:43 +00001469 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1470 p.reset();
1471 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1472 REPORTER_ASSERT(reporter, valid);
1473 REPORTER_ASSERT(reporter, !p.isEmpty());
1474 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1475 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1476 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1477 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1478 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1479 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001480 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001481}
1482
1483struct SegmentInfo {
1484 SkPath fPath;
1485 int fPointCount;
1486};
1487
reed@google.com10296cc2011-09-21 12:29:05 +00001488#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1489
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001490static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001491 SkPath p, p2;
1492
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001493 p.moveTo(0, 0);
1494 p.quadTo(100, 100, 200, 200);
1495 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1496 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001497 p2 = p;
1498 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001499 p.cubicTo(100, 100, 200, 200, 300, 300);
1500 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1501 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001502 p2 = p;
1503 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1504
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001505 p.reset();
1506 p.moveTo(0, 0);
1507 p.cubicTo(100, 100, 200, 200, 300, 300);
1508 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001509 p2 = p;
1510 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001511
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001512 REPORTER_ASSERT(reporter, !p.isEmpty());
1513}
1514
1515static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001516 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001517 SkPoint pts[4];
1518
1519 // Test an iterator with no path
1520 SkPath::Iter noPathIter;
1521 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001522
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001523 // Test that setting an empty path works
1524 noPathIter.setPath(p, false);
1525 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001526
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001527 // Test that close path makes no difference for an empty path
1528 noPathIter.setPath(p, true);
1529 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001530
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001531 // Test an iterator with an initial empty path
1532 SkPath::Iter iter(p, false);
1533 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1534
1535 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001536 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001537 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1538
rmistry@google.comd6176b02012-08-23 18:14:13 +00001539
schenney@chromium.org7e963602012-06-13 17:05:43 +00001540 struct iterTestData {
1541 const char* testPath;
1542 const bool forceClose;
1543 const bool consumeDegenerates;
1544 const size_t* numResultPtsPerVerb;
1545 const SkPoint* resultPts;
1546 const SkPath::Verb* resultVerbs;
1547 const size_t numResultVerbs;
1548 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001549
schenney@chromium.org7e963602012-06-13 17:05:43 +00001550 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1551 static const SkPath::Verb resultVerbs2[] = {
1552 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1553 };
1554 static const SkPath::Verb resultVerbs3[] = {
1555 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1556 };
1557 static const SkPath::Verb resultVerbs4[] = {
1558 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1559 };
1560 static const SkPath::Verb resultVerbs5[] = {
1561 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1562 };
1563 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001564 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1565 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1566 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1567 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001568 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001569 static const SkPoint resultPts2[] = {
1570 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1571 };
1572 static const SkPoint resultPts3[] = {
1573 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1574 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1575 };
1576 static const SkPoint resultPts4[] = {
1577 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1578 };
1579 static const SkPoint resultPts5[] = {
1580 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1581 };
1582 static const struct iterTestData gIterTests[] = {
1583 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001584 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1585 { "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 +00001586 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1587 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1588 { "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) },
1589 { "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 +00001590 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1591 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1592 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1593 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1594 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1595 { "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 +00001596 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001597
schenney@chromium.org7e963602012-06-13 17:05:43 +00001598 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1599 p.reset();
1600 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1601 REPORTER_ASSERT(reporter, valid);
1602 iter.setPath(p, gIterTests[i].forceClose);
1603 int j = 0, l = 0;
1604 do {
1605 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1606 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1607 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1608 }
1609 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1610 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1611 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001612
1613 // The GM degeneratesegments.cpp test is more extensive
1614}
1615
1616static void test_raw_iter(skiatest::Reporter* reporter) {
1617 SkPath p;
1618 SkPoint pts[4];
1619
1620 // Test an iterator with no path
1621 SkPath::RawIter noPathIter;
1622 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1623 // Test that setting an empty path works
1624 noPathIter.setPath(p);
1625 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001626
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001627 // Test an iterator with an initial empty path
1628 SkPath::RawIter iter(p);
1629 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1630
1631 // Test that a move-only path returns the move.
1632 p.moveTo(SK_Scalar1, 0);
1633 iter.setPath(p);
1634 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1635 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1636 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1637 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1638
1639 // No matter how many moves we add, we should get them all back
1640 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1641 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1642 iter.setPath(p);
1643 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1644 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1645 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1646 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1647 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1648 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1649 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1650 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1651 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1652 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1653
1654 // Initial close is never ever stored
1655 p.reset();
1656 p.close();
1657 iter.setPath(p);
1658 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1659
1660 // Move/close sequences
1661 p.reset();
1662 p.close(); // Not stored, no purpose
1663 p.moveTo(SK_Scalar1, 0);
1664 p.close();
1665 p.close(); // Not stored, no purpose
1666 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1667 p.close();
1668 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1669 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1670 p.close();
1671 iter.setPath(p);
1672 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1673 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1674 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1675 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1676 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1677 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1678 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1679 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1680 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1681 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1682 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1683 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1684 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1685 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1686 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1687 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1688 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1689 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1690 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1691 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1692 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1693 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1694
1695 // Generate random paths and verify
1696 SkPoint randomPts[25];
1697 for (int i = 0; i < 5; ++i) {
1698 for (int j = 0; j < 5; ++j) {
1699 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1700 }
1701 }
1702
1703 // Max of 10 segments, max 3 points per segment
1704 SkRandom rand(9876543);
1705 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001706 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001707 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001708
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001709 for (int i = 0; i < 500; ++i) {
1710 p.reset();
1711 bool lastWasClose = true;
1712 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001713 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001714 int numPoints = 0;
1715 int numVerbs = (rand.nextU() >> 16) % 10;
1716 int numIterVerbs = 0;
1717 for (int j = 0; j < numVerbs; ++j) {
1718 do {
1719 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1720 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001721 switch (nextVerb) {
1722 case SkPath::kMove_Verb:
1723 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1724 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001725 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001726 numPoints += 1;
1727 lastWasClose = false;
1728 haveMoveTo = true;
1729 break;
1730 case SkPath::kLine_Verb:
1731 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001732 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001733 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1734 haveMoveTo = true;
1735 }
1736 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1737 p.lineTo(expectedPts[numPoints]);
1738 numPoints += 1;
1739 lastWasClose = false;
1740 break;
1741 case SkPath::kQuad_Verb:
1742 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001743 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001744 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1745 haveMoveTo = true;
1746 }
1747 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1748 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1749 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1750 numPoints += 2;
1751 lastWasClose = false;
1752 break;
1753 case SkPath::kCubic_Verb:
1754 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001755 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001756 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1757 haveMoveTo = true;
1758 }
1759 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1760 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1761 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1762 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1763 expectedPts[numPoints + 2]);
1764 numPoints += 3;
1765 lastWasClose = false;
1766 break;
1767 case SkPath::kClose_Verb:
1768 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001769 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001770 lastWasClose = true;
1771 break;
1772 default:;
1773 }
1774 expectedVerbs[numIterVerbs++] = nextVerb;
1775 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001776
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001777 iter.setPath(p);
1778 numVerbs = numIterVerbs;
1779 numIterVerbs = 0;
1780 int numIterPts = 0;
1781 SkPoint lastMoveTo;
1782 SkPoint lastPt;
1783 lastMoveTo.set(0, 0);
1784 lastPt.set(0, 0);
1785 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1786 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1787 numIterVerbs++;
1788 switch (nextVerb) {
1789 case SkPath::kMove_Verb:
1790 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1791 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1792 lastPt = lastMoveTo = pts[0];
1793 numIterPts += 1;
1794 break;
1795 case SkPath::kLine_Verb:
1796 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1797 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1798 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1799 lastPt = pts[1];
1800 numIterPts += 1;
1801 break;
1802 case SkPath::kQuad_Verb:
1803 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1804 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1805 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1806 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1807 lastPt = pts[2];
1808 numIterPts += 2;
1809 break;
1810 case SkPath::kCubic_Verb:
1811 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1812 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1813 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1814 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1815 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1816 lastPt = pts[3];
1817 numIterPts += 3;
1818 break;
1819 case SkPath::kClose_Verb:
1820 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1821 lastPt = lastMoveTo;
1822 break;
1823 default:;
1824 }
1825 }
1826 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1827 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1828 }
1829}
1830
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001831static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001832 const SkPath& path,
1833 bool expectedCircle,
1834 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001835 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001836 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1837 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001838
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001839 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001840 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1841 }
1842}
1843
1844static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001845 const SkPath& path,
1846 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001847 SkPath tmp;
1848
1849 SkMatrix m;
1850 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1851 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001852 // this matrix reverses the direction.
1853 if (SkPath::kCCW_Direction == dir) {
1854 dir = SkPath::kCW_Direction;
1855 } else {
1856 SkASSERT(SkPath::kCW_Direction == dir);
1857 dir = SkPath::kCCW_Direction;
1858 }
1859 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001860}
1861
1862static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001863 const SkPath& path,
1864 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001865 SkPath tmp;
1866
1867 // translate at small offset
1868 SkMatrix m;
1869 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1870 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001871 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001872
1873 tmp.reset();
1874 m.reset();
1875
1876 // translate at a relatively big offset
1877 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1878 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001879 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001880}
1881
1882static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001883 const SkPath& path,
1884 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001885 for (int angle = 0; angle < 360; ++angle) {
1886 SkPath tmp;
1887 SkMatrix m;
1888 m.setRotate(SkIntToScalar(angle));
1889 path.transform(m, &tmp);
1890
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001891 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001892 // degrees is not an oval anymore, this can be improved. we made this
1893 // for the simplicity of our implementation.
1894 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001895 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001896 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001897 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001898 }
1899 }
1900}
1901
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001902static void test_circle_mirror_x(skiatest::Reporter* reporter,
1903 const SkPath& path,
1904 SkPath::Direction dir) {
1905 SkPath tmp;
1906 SkMatrix m;
1907 m.reset();
1908 m.setScaleX(-SK_Scalar1);
1909 path.transform(m, &tmp);
1910
1911 if (SkPath::kCW_Direction == dir) {
1912 dir = SkPath::kCCW_Direction;
1913 } else {
1914 SkASSERT(SkPath::kCCW_Direction == dir);
1915 dir = SkPath::kCW_Direction;
1916 }
1917
1918 check_for_circle(reporter, tmp, true, dir);
1919}
1920
1921static void test_circle_mirror_y(skiatest::Reporter* reporter,
1922 const SkPath& path,
1923 SkPath::Direction dir) {
1924 SkPath tmp;
1925 SkMatrix m;
1926 m.reset();
1927 m.setScaleY(-SK_Scalar1);
1928 path.transform(m, &tmp);
1929
1930 if (SkPath::kCW_Direction == dir) {
1931 dir = SkPath::kCCW_Direction;
1932 } else {
1933 SkASSERT(SkPath::kCCW_Direction == dir);
1934 dir = SkPath::kCW_Direction;
1935 }
1936
1937 check_for_circle(reporter, tmp, true, dir);
1938}
1939
1940static void test_circle_mirror_xy(skiatest::Reporter* reporter,
1941 const SkPath& path,
1942 SkPath::Direction dir) {
1943 SkPath tmp;
1944 SkMatrix m;
1945 m.reset();
1946 m.setScaleX(-SK_Scalar1);
1947 m.setScaleY(-SK_Scalar1);
1948 path.transform(m, &tmp);
1949
1950 check_for_circle(reporter, tmp, true, dir);
1951}
1952
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001953static void test_circle_with_direction(skiatest::Reporter* reporter,
1954 SkPath::Direction dir) {
1955 SkPath path;
1956
1957 // circle at origin
1958 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001959 check_for_circle(reporter, path, true, dir);
1960 test_circle_rotate(reporter, path, dir);
1961 test_circle_translate(reporter, path, dir);
1962 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001963
1964 // circle at an offset at (10, 10)
1965 path.reset();
1966 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1967 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001968 check_for_circle(reporter, path, true, dir);
1969 test_circle_rotate(reporter, path, dir);
1970 test_circle_translate(reporter, path, dir);
1971 test_circle_skew(reporter, path, dir);
1972 test_circle_mirror_x(reporter, path, dir);
1973 test_circle_mirror_y(reporter, path, dir);
1974 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001975}
1976
1977static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1978 SkPath path;
1979 SkPath circle;
1980 SkPath rect;
1981 SkPath empty;
1982
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001983 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
1984 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
1985
1986 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001987 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1988 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1989
1990 SkMatrix translate;
1991 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1992
1993 // For simplicity, all the path concatenation related operations
1994 // would mark it non-circle, though in theory it's still a circle.
1995
1996 // empty + circle (translate)
1997 path = empty;
1998 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001999 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002000
2001 // circle + empty (translate)
2002 path = circle;
2003 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002004 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002005
2006 // test reverseAddPath
2007 path = circle;
2008 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002009 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002010}
2011
2012static void test_circle(skiatest::Reporter* reporter) {
2013 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2014 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2015
2016 // multiple addCircle()
2017 SkPath path;
2018 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2019 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002020 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002021
2022 // some extra lineTo() would make isOval() fail
2023 path.reset();
2024 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2025 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002026 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002027
2028 // not back to the original point
2029 path.reset();
2030 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2031 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002032 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002033
2034 test_circle_with_add_paths(reporter);
2035}
2036
2037static void test_oval(skiatest::Reporter* reporter) {
2038 SkRect rect;
2039 SkMatrix m;
2040 SkPath path;
2041
2042 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2043 path.addOval(rect);
2044
2045 REPORTER_ASSERT(reporter, path.isOval(NULL));
2046
2047 m.setRotate(SkIntToScalar(90));
2048 SkPath tmp;
2049 path.transform(m, &tmp);
2050 // an oval rotated 90 degrees is still an oval.
2051 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2052
2053 m.reset();
2054 m.setRotate(SkIntToScalar(30));
2055 tmp.reset();
2056 path.transform(m, &tmp);
2057 // an oval rotated 30 degrees is not an oval anymore.
2058 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2059
2060 // since empty path being transformed.
2061 path.reset();
2062 tmp.reset();
2063 m.reset();
2064 path.transform(m, &tmp);
2065 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2066
2067 // empty path is not an oval
2068 tmp.reset();
2069 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2070
2071 // only has moveTo()s
2072 tmp.reset();
2073 tmp.moveTo(0, 0);
2074 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2075 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2076
2077 // mimic WebKit's calling convention,
2078 // call moveTo() first and then call addOval()
2079 path.reset();
2080 path.moveTo(0, 0);
2081 path.addOval(rect);
2082 REPORTER_ASSERT(reporter, path.isOval(NULL));
2083
2084 // copy path
2085 path.reset();
2086 tmp.reset();
2087 tmp.addOval(rect);
2088 path = tmp;
2089 REPORTER_ASSERT(reporter, path.isOval(NULL));
2090}
2091
caryclark@google.com42639cd2012-06-06 12:03:39 +00002092static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002093 SkTSize<SkScalar>::Make(3,4);
2094
reed@android.com3abec1d2009-03-02 05:36:20 +00002095 SkPath p, p2;
2096 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002097
reed@android.com3abec1d2009-03-02 05:36:20 +00002098 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002099 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002100 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002101 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002102 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002103 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2104 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2105 REPORTER_ASSERT(reporter, p == p2);
2106 REPORTER_ASSERT(reporter, !(p != p2));
2107
reed@android.comd252db02009-04-01 18:31:44 +00002108 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002109
reed@android.com3abec1d2009-03-02 05:36:20 +00002110 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002111
reed@android.com6b82d1a2009-06-03 02:35:01 +00002112 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2113 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002114 // we have quads or cubics
2115 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002116 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002117
reed@android.com6b82d1a2009-06-03 02:35:01 +00002118 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002119 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002120 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002121
reed@android.com6b82d1a2009-06-03 02:35:01 +00002122 p.addOval(bounds);
2123 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002124 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002125
reed@android.com6b82d1a2009-06-03 02:35:01 +00002126 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002127 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002128 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002129 // we have only lines
2130 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002131 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002132
2133 REPORTER_ASSERT(reporter, p != p2);
2134 REPORTER_ASSERT(reporter, !(p == p2));
2135
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002136 // do getPoints and getVerbs return the right result
2137 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2138 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002139 SkPoint pts[4];
2140 int count = p.getPoints(pts, 4);
2141 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002142 uint8_t verbs[6];
2143 verbs[5] = 0xff;
2144 p.getVerbs(verbs, 5);
2145 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2146 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2147 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2148 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2149 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2150 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002151 bounds2.set(pts, 4);
2152 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002153
reed@android.com3abec1d2009-03-02 05:36:20 +00002154 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2155 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002156 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002157
reed@android.com3abec1d2009-03-02 05:36:20 +00002158 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002159 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002160 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2161 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002162
reed@android.com3abec1d2009-03-02 05:36:20 +00002163 // now force p to not be a rect
2164 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2165 p.addRect(bounds);
2166 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002167
reed@google.com7e6c4d12012-05-10 14:05:43 +00002168 test_isLine(reporter);
2169 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002170 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002171 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002172 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002173 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002174 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002175 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002176 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002177 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002178 test_flattening(reporter);
2179 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002180 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002181 test_iter(reporter);
2182 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002183 test_circle(reporter);
2184 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002185 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002186 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002187 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002188 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002189 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002190 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002191 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002192 test_addrect_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002193}
2194
2195#include "TestClassDef.h"
2196DEFINE_TESTCLASS("Path", PathTestClass, TestPath)