blob: 0d4e1255ab5f264494e8fd7cad2cb8b1c6f3b22f [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}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001007
caryclark@google.comf1316942011-07-26 19:54:45 +00001008 // failing tests
1009 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1010 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1011 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1012 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1013 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1014 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1015 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1016 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
rmistry@google.comd6176b02012-08-23 18:14:13 +00001017
caryclark@google.comf1316942011-07-26 19:54:45 +00001018 // failing, no close
1019 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1020 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1021
1022 size_t testLen[] = {
1023 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1024 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1025 sizeof(rd), sizeof(re),
1026 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1027 sizeof(f7), sizeof(f8),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001028 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001029 };
1030 SkPoint* tests[] = {
1031 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1032 f1, f2, f3, f4, f5, f6, f7, f8,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001033 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001034 };
1035 SkPoint* lastPass = re;
1036 SkPoint* lastClose = f8;
1037 bool fail = false;
1038 bool close = true;
1039 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1040 size_t index;
1041 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1042 SkPath path;
1043 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1044 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1045 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1046 }
1047 if (close) {
1048 path.close();
1049 }
1050 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001051 if (!fail) {
1052 SkRect computed, expected;
1053 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1054 REPORTER_ASSERT(reporter, path.isRect(&computed));
1055 REPORTER_ASSERT(reporter, expected == computed);
1056 }
caryclark@google.comf1316942011-07-26 19:54:45 +00001057 if (tests[testIndex] == lastPass) {
1058 fail = true;
1059 }
1060 if (tests[testIndex] == lastClose) {
1061 close = false;
1062 }
1063 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001064
caryclark@google.comf1316942011-07-26 19:54:45 +00001065 // fail, close then line
1066 SkPath path1;
1067 path1.moveTo(r1[0].fX, r1[0].fY);
1068 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1069 path1.lineTo(r1[index].fX, r1[index].fY);
1070 }
1071 path1.close();
1072 path1.lineTo(1, 0);
1073 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001074
caryclark@google.comf1316942011-07-26 19:54:45 +00001075 // fail, move in the middle
1076 path1.reset();
1077 path1.moveTo(r1[0].fX, r1[0].fY);
1078 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1079 if (index == 2) {
1080 path1.moveTo(1, .5f);
1081 }
1082 path1.lineTo(r1[index].fX, r1[index].fY);
1083 }
1084 path1.close();
1085 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1086
1087 // fail, move on the edge
1088 path1.reset();
1089 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1090 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1091 path1.lineTo(r1[index].fX, r1[index].fY);
1092 }
1093 path1.close();
1094 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001095
caryclark@google.comf1316942011-07-26 19:54:45 +00001096 // fail, quad
1097 path1.reset();
1098 path1.moveTo(r1[0].fX, r1[0].fY);
1099 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1100 if (index == 2) {
1101 path1.quadTo(1, .5f, 1, .5f);
1102 }
1103 path1.lineTo(r1[index].fX, r1[index].fY);
1104 }
1105 path1.close();
1106 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001107
caryclark@google.comf1316942011-07-26 19:54:45 +00001108 // fail, cubic
1109 path1.reset();
1110 path1.moveTo(r1[0].fX, r1[0].fY);
1111 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1112 if (index == 2) {
1113 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1114 }
1115 path1.lineTo(r1[index].fX, r1[index].fY);
1116 }
1117 path1.close();
1118 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1119}
1120
caryclark@google.com56f233a2012-11-19 13:06:06 +00001121static void test_isNestedRects(skiatest::Reporter* reporter) {
1122 // passing tests (all moveTo / lineTo...
1123 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1124 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1125 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1126 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1127 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1128 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1129 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1130 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1131 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1132 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1133 {1, 0}, {.5f, 0}};
1134 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1135 {0, 1}, {0, .5f}};
1136 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1137 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1138 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1139
1140 // failing tests
1141 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1142 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1143 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1144 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1145 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1146 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1147 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1148 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1149
1150 // failing, no close
1151 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1152 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1153
1154 size_t testLen[] = {
1155 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1156 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1157 sizeof(rd), sizeof(re),
1158 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1159 sizeof(f7), sizeof(f8),
1160 sizeof(c1), sizeof(c2)
1161 };
1162 SkPoint* tests[] = {
1163 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1164 f1, f2, f3, f4, f5, f6, f7, f8,
1165 c1, c2
1166 };
1167 const SkPoint* lastPass = re;
1168 const SkPoint* lastClose = f8;
1169 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1170 size_t index;
1171 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1172 bool fail = false;
1173 bool close = true;
1174 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1175 SkPath path;
1176 if (rectFirst) {
1177 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1178 }
1179 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1180 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1181 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1182 }
1183 if (close) {
1184 path.close();
1185 }
1186 if (!rectFirst) {
1187 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1188 }
1189 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1190 if (!fail) {
1191 SkRect expected[2], computed[2];
1192 SkRect testBounds;
1193 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1194 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1195 expected[1] = testBounds;
1196 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1197 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1198 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1199 }
1200 if (tests[testIndex] == lastPass) {
1201 fail = true;
1202 }
1203 if (tests[testIndex] == lastClose) {
1204 close = false;
1205 }
1206 }
1207
1208 // fail, close then line
1209 SkPath path1;
1210 if (rectFirst) {
1211 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1212 }
1213 path1.moveTo(r1[0].fX, r1[0].fY);
1214 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1215 path1.lineTo(r1[index].fX, r1[index].fY);
1216 }
1217 path1.close();
1218 path1.lineTo(1, 0);
1219 if (!rectFirst) {
1220 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1221 }
1222 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1223
1224 // fail, move in the middle
1225 path1.reset();
1226 if (rectFirst) {
1227 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1228 }
1229 path1.moveTo(r1[0].fX, r1[0].fY);
1230 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1231 if (index == 2) {
1232 path1.moveTo(1, .5f);
1233 }
1234 path1.lineTo(r1[index].fX, r1[index].fY);
1235 }
1236 path1.close();
1237 if (!rectFirst) {
1238 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1239 }
1240 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1241
1242 // fail, move on the edge
1243 path1.reset();
1244 if (rectFirst) {
1245 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1246 }
1247 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1248 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1249 path1.lineTo(r1[index].fX, r1[index].fY);
1250 }
1251 path1.close();
1252 if (!rectFirst) {
1253 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1254 }
1255 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1256
1257 // fail, quad
1258 path1.reset();
1259 if (rectFirst) {
1260 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1261 }
1262 path1.moveTo(r1[0].fX, r1[0].fY);
1263 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1264 if (index == 2) {
1265 path1.quadTo(1, .5f, 1, .5f);
1266 }
1267 path1.lineTo(r1[index].fX, r1[index].fY);
1268 }
1269 path1.close();
1270 if (!rectFirst) {
1271 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1272 }
1273 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1274
1275 // fail, cubic
1276 path1.reset();
1277 if (rectFirst) {
1278 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1279 }
1280 path1.moveTo(r1[0].fX, r1[0].fY);
1281 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1282 if (index == 2) {
1283 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1284 }
1285 path1.lineTo(r1[index].fX, r1[index].fY);
1286 }
1287 path1.close();
1288 if (!rectFirst) {
1289 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1290 }
1291 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1292
1293 // fail, not nested
1294 path1.reset();
1295 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1296 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1297 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1298 }
1299}
1300
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001301static void write_and_read_back(skiatest::Reporter* reporter,
1302 const SkPath& p) {
1303 SkWriter32 writer(100);
1304 writer.writePath(p);
1305 size_t size = writer.size();
1306 SkAutoMalloc storage(size);
1307 writer.flatten(storage.get());
1308 SkReader32 reader(storage.get(), size);
1309
1310 SkPath readBack;
1311 REPORTER_ASSERT(reporter, readBack != p);
1312 reader.readPath(&readBack);
1313 REPORTER_ASSERT(reporter, readBack == p);
1314
rmistry@google.comd6176b02012-08-23 18:14:13 +00001315 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001316 p.getConvexityOrUnknown());
1317
1318 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1319
1320 const SkRect& origBounds = p.getBounds();
1321 const SkRect& readBackBounds = readBack.getBounds();
1322
1323 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1324}
1325
reed@google.com53effc52011-09-21 19:05:12 +00001326static void test_flattening(skiatest::Reporter* reporter) {
1327 SkPath p;
1328
1329 static const SkPoint pts[] = {
1330 { 0, 0 },
1331 { SkIntToScalar(10), SkIntToScalar(10) },
1332 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1333 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1334 };
1335 p.moveTo(pts[0]);
1336 p.lineTo(pts[1]);
1337 p.quadTo(pts[2], pts[3]);
1338 p.cubicTo(pts[4], pts[5], pts[6]);
1339
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001340 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001341
1342 // create a buffer that should be much larger than the path so we don't
1343 // kill our stack if writer goes too far.
1344 char buffer[1024];
1345 uint32_t size1 = p.writeToMemory(NULL);
1346 uint32_t size2 = p.writeToMemory(buffer);
1347 REPORTER_ASSERT(reporter, size1 == size2);
1348
1349 SkPath p2;
1350 uint32_t size3 = p2.readFromMemory(buffer);
1351 REPORTER_ASSERT(reporter, size1 == size3);
1352 REPORTER_ASSERT(reporter, p == p2);
1353
1354 char buffer2[1024];
1355 size3 = p2.writeToMemory(buffer2);
1356 REPORTER_ASSERT(reporter, size1 == size3);
1357 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001358
1359 // test persistence of the oval flag & convexity
1360 {
1361 SkPath oval;
1362 SkRect rect = SkRect::MakeWH(10, 10);
1363 oval.addOval(rect);
1364
1365 write_and_read_back(reporter, oval);
1366 }
reed@google.com53effc52011-09-21 19:05:12 +00001367}
1368
1369static void test_transform(skiatest::Reporter* reporter) {
1370 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001371
reed@google.com53effc52011-09-21 19:05:12 +00001372 static const SkPoint pts[] = {
1373 { 0, 0 },
1374 { SkIntToScalar(10), SkIntToScalar(10) },
1375 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1376 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1377 };
1378 p.moveTo(pts[0]);
1379 p.lineTo(pts[1]);
1380 p.quadTo(pts[2], pts[3]);
1381 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001382
reed@google.com53effc52011-09-21 19:05:12 +00001383 SkMatrix matrix;
1384 matrix.reset();
1385 p.transform(matrix, &p1);
1386 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001387
reed@google.com53effc52011-09-21 19:05:12 +00001388 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1389 p.transform(matrix, &p1);
1390 SkPoint pts1[7];
1391 int count = p1.getPoints(pts1, 7);
1392 REPORTER_ASSERT(reporter, 7 == count);
1393 for (int i = 0; i < count; ++i) {
1394 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1395 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1396 }
1397}
1398
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001399static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001400 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001401 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001402
caryclark@google.com56f233a2012-11-19 13:06:06 +00001403 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001404 const char* testPath;
1405 const size_t numResultPts;
1406 const SkRect resultBound;
1407 const SkPath::Verb* resultVerbs;
1408 const size_t numResultVerbs;
1409 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001410
schenney@chromium.org7e963602012-06-13 17:05:43 +00001411 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1412 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1413 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1414 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1415 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1416 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1417 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1418 static const SkPath::Verb resultVerbs8[] = {
1419 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1420 };
1421 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1422 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1423 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1424 static const SkPath::Verb resultVerbs12[] = {
1425 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1426 };
1427 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1428 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1429 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1430 static const SkPath::Verb resultVerbs16[] = {
1431 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1432 };
1433 static const struct zeroPathTestData gZeroLengthTests[] = {
1434 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001435 { "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 +00001436 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001437 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1438 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1439 { "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) },
1440 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1441 { "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) },
1442 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1443 { "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) },
1444 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1445 { "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) },
1446 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1447 { "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 +00001448 SK_ARRAY_COUNT(resultVerbs14)
1449 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001450 { "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) },
1451 { "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 +00001452 SK_ARRAY_COUNT(resultVerbs16)
1453 }
1454 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001455
schenney@chromium.org7e963602012-06-13 17:05:43 +00001456 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1457 p.reset();
1458 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1459 REPORTER_ASSERT(reporter, valid);
1460 REPORTER_ASSERT(reporter, !p.isEmpty());
1461 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1462 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1463 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1464 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1465 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1466 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001467 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001468}
1469
1470struct SegmentInfo {
1471 SkPath fPath;
1472 int fPointCount;
1473};
1474
reed@google.com10296cc2011-09-21 12:29:05 +00001475#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1476
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001477static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001478 SkPath p, p2;
1479
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001480 p.moveTo(0, 0);
1481 p.quadTo(100, 100, 200, 200);
1482 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1483 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001484 p2 = p;
1485 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001486 p.cubicTo(100, 100, 200, 200, 300, 300);
1487 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1488 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001489 p2 = p;
1490 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1491
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001492 p.reset();
1493 p.moveTo(0, 0);
1494 p.cubicTo(100, 100, 200, 200, 300, 300);
1495 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001496 p2 = p;
1497 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001498
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001499 REPORTER_ASSERT(reporter, !p.isEmpty());
1500}
1501
1502static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001503 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001504 SkPoint pts[4];
1505
1506 // Test an iterator with no path
1507 SkPath::Iter noPathIter;
1508 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001509
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001510 // Test that setting an empty path works
1511 noPathIter.setPath(p, false);
1512 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001513
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001514 // Test that close path makes no difference for an empty path
1515 noPathIter.setPath(p, true);
1516 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001517
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001518 // Test an iterator with an initial empty path
1519 SkPath::Iter iter(p, false);
1520 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1521
1522 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001523 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001524 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1525
rmistry@google.comd6176b02012-08-23 18:14:13 +00001526
schenney@chromium.org7e963602012-06-13 17:05:43 +00001527 struct iterTestData {
1528 const char* testPath;
1529 const bool forceClose;
1530 const bool consumeDegenerates;
1531 const size_t* numResultPtsPerVerb;
1532 const SkPoint* resultPts;
1533 const SkPath::Verb* resultVerbs;
1534 const size_t numResultVerbs;
1535 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001536
schenney@chromium.org7e963602012-06-13 17:05:43 +00001537 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1538 static const SkPath::Verb resultVerbs2[] = {
1539 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1540 };
1541 static const SkPath::Verb resultVerbs3[] = {
1542 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1543 };
1544 static const SkPath::Verb resultVerbs4[] = {
1545 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1546 };
1547 static const SkPath::Verb resultVerbs5[] = {
1548 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1549 };
1550 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001551 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1552 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1553 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1554 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001555 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001556 static const SkPoint resultPts2[] = {
1557 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1558 };
1559 static const SkPoint resultPts3[] = {
1560 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1561 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1562 };
1563 static const SkPoint resultPts4[] = {
1564 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1565 };
1566 static const SkPoint resultPts5[] = {
1567 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1568 };
1569 static const struct iterTestData gIterTests[] = {
1570 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001571 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1572 { "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 +00001573 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1574 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1575 { "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) },
1576 { "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 +00001577 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1578 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1579 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1580 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1581 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1582 { "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 +00001583 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001584
schenney@chromium.org7e963602012-06-13 17:05:43 +00001585 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1586 p.reset();
1587 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1588 REPORTER_ASSERT(reporter, valid);
1589 iter.setPath(p, gIterTests[i].forceClose);
1590 int j = 0, l = 0;
1591 do {
1592 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1593 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1594 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1595 }
1596 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1597 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1598 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001599
1600 // The GM degeneratesegments.cpp test is more extensive
1601}
1602
1603static void test_raw_iter(skiatest::Reporter* reporter) {
1604 SkPath p;
1605 SkPoint pts[4];
1606
1607 // Test an iterator with no path
1608 SkPath::RawIter noPathIter;
1609 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1610 // Test that setting an empty path works
1611 noPathIter.setPath(p);
1612 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001613
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001614 // Test an iterator with an initial empty path
1615 SkPath::RawIter iter(p);
1616 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1617
1618 // Test that a move-only path returns the move.
1619 p.moveTo(SK_Scalar1, 0);
1620 iter.setPath(p);
1621 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1622 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1623 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1624 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1625
1626 // No matter how many moves we add, we should get them all back
1627 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1628 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1629 iter.setPath(p);
1630 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1631 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1632 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1633 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1634 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1635 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1636 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1637 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1638 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1639 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1640
1641 // Initial close is never ever stored
1642 p.reset();
1643 p.close();
1644 iter.setPath(p);
1645 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1646
1647 // Move/close sequences
1648 p.reset();
1649 p.close(); // Not stored, no purpose
1650 p.moveTo(SK_Scalar1, 0);
1651 p.close();
1652 p.close(); // Not stored, no purpose
1653 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1654 p.close();
1655 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1656 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1657 p.close();
1658 iter.setPath(p);
1659 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1660 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1661 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1662 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1663 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1664 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1665 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1666 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1667 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1668 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1669 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1670 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1671 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1672 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1673 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1674 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1675 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1676 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1677 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1678 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1679 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1680 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1681
1682 // Generate random paths and verify
1683 SkPoint randomPts[25];
1684 for (int i = 0; i < 5; ++i) {
1685 for (int j = 0; j < 5; ++j) {
1686 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1687 }
1688 }
1689
1690 // Max of 10 segments, max 3 points per segment
1691 SkRandom rand(9876543);
1692 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001693 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001694 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001695
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001696 for (int i = 0; i < 500; ++i) {
1697 p.reset();
1698 bool lastWasClose = true;
1699 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001700 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001701 int numPoints = 0;
1702 int numVerbs = (rand.nextU() >> 16) % 10;
1703 int numIterVerbs = 0;
1704 for (int j = 0; j < numVerbs; ++j) {
1705 do {
1706 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1707 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001708 switch (nextVerb) {
1709 case SkPath::kMove_Verb:
1710 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1711 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001712 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001713 numPoints += 1;
1714 lastWasClose = false;
1715 haveMoveTo = true;
1716 break;
1717 case SkPath::kLine_Verb:
1718 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001719 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001720 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1721 haveMoveTo = true;
1722 }
1723 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1724 p.lineTo(expectedPts[numPoints]);
1725 numPoints += 1;
1726 lastWasClose = false;
1727 break;
1728 case SkPath::kQuad_Verb:
1729 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001730 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001731 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1732 haveMoveTo = true;
1733 }
1734 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1735 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1736 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1737 numPoints += 2;
1738 lastWasClose = false;
1739 break;
1740 case SkPath::kCubic_Verb:
1741 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001742 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001743 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1744 haveMoveTo = true;
1745 }
1746 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1747 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1748 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1749 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1750 expectedPts[numPoints + 2]);
1751 numPoints += 3;
1752 lastWasClose = false;
1753 break;
1754 case SkPath::kClose_Verb:
1755 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001756 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001757 lastWasClose = true;
1758 break;
1759 default:;
1760 }
1761 expectedVerbs[numIterVerbs++] = nextVerb;
1762 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001763
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001764 iter.setPath(p);
1765 numVerbs = numIterVerbs;
1766 numIterVerbs = 0;
1767 int numIterPts = 0;
1768 SkPoint lastMoveTo;
1769 SkPoint lastPt;
1770 lastMoveTo.set(0, 0);
1771 lastPt.set(0, 0);
1772 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1773 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1774 numIterVerbs++;
1775 switch (nextVerb) {
1776 case SkPath::kMove_Verb:
1777 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1778 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1779 lastPt = lastMoveTo = pts[0];
1780 numIterPts += 1;
1781 break;
1782 case SkPath::kLine_Verb:
1783 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1784 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1785 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1786 lastPt = pts[1];
1787 numIterPts += 1;
1788 break;
1789 case SkPath::kQuad_Verb:
1790 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1791 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1792 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1793 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1794 lastPt = pts[2];
1795 numIterPts += 2;
1796 break;
1797 case SkPath::kCubic_Verb:
1798 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1799 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1800 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1801 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1802 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1803 lastPt = pts[3];
1804 numIterPts += 3;
1805 break;
1806 case SkPath::kClose_Verb:
1807 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1808 lastPt = lastMoveTo;
1809 break;
1810 default:;
1811 }
1812 }
1813 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1814 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1815 }
1816}
1817
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001818static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001819 const SkPath& path,
1820 bool expectedCircle,
1821 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001822 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001823 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1824 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001825
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001826 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001827 REPORTER_ASSERT(reporter, rect.height() == rect.width());
1828 }
1829}
1830
1831static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001832 const SkPath& path,
1833 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001834 SkPath tmp;
1835
1836 SkMatrix m;
1837 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1838 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001839 // this matrix reverses the direction.
1840 if (SkPath::kCCW_Direction == dir) {
1841 dir = SkPath::kCW_Direction;
1842 } else {
1843 SkASSERT(SkPath::kCW_Direction == dir);
1844 dir = SkPath::kCCW_Direction;
1845 }
1846 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001847}
1848
1849static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001850 const SkPath& path,
1851 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001852 SkPath tmp;
1853
1854 // translate at small offset
1855 SkMatrix m;
1856 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1857 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001858 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001859
1860 tmp.reset();
1861 m.reset();
1862
1863 // translate at a relatively big offset
1864 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1865 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001866 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001867}
1868
1869static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001870 const SkPath& path,
1871 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001872 for (int angle = 0; angle < 360; ++angle) {
1873 SkPath tmp;
1874 SkMatrix m;
1875 m.setRotate(SkIntToScalar(angle));
1876 path.transform(m, &tmp);
1877
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001878 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001879 // degrees is not an oval anymore, this can be improved. we made this
1880 // for the simplicity of our implementation.
1881 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001882 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001883 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001884 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001885 }
1886 }
1887}
1888
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001889static void test_circle_mirror_x(skiatest::Reporter* reporter,
1890 const SkPath& path,
1891 SkPath::Direction dir) {
1892 SkPath tmp;
1893 SkMatrix m;
1894 m.reset();
1895 m.setScaleX(-SK_Scalar1);
1896 path.transform(m, &tmp);
1897
1898 if (SkPath::kCW_Direction == dir) {
1899 dir = SkPath::kCCW_Direction;
1900 } else {
1901 SkASSERT(SkPath::kCCW_Direction == dir);
1902 dir = SkPath::kCW_Direction;
1903 }
1904
1905 check_for_circle(reporter, tmp, true, dir);
1906}
1907
1908static void test_circle_mirror_y(skiatest::Reporter* reporter,
1909 const SkPath& path,
1910 SkPath::Direction dir) {
1911 SkPath tmp;
1912 SkMatrix m;
1913 m.reset();
1914 m.setScaleY(-SK_Scalar1);
1915 path.transform(m, &tmp);
1916
1917 if (SkPath::kCW_Direction == dir) {
1918 dir = SkPath::kCCW_Direction;
1919 } else {
1920 SkASSERT(SkPath::kCCW_Direction == dir);
1921 dir = SkPath::kCW_Direction;
1922 }
1923
1924 check_for_circle(reporter, tmp, true, dir);
1925}
1926
1927static void test_circle_mirror_xy(skiatest::Reporter* reporter,
1928 const SkPath& path,
1929 SkPath::Direction dir) {
1930 SkPath tmp;
1931 SkMatrix m;
1932 m.reset();
1933 m.setScaleX(-SK_Scalar1);
1934 m.setScaleY(-SK_Scalar1);
1935 path.transform(m, &tmp);
1936
1937 check_for_circle(reporter, tmp, true, dir);
1938}
1939
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001940static void test_circle_with_direction(skiatest::Reporter* reporter,
1941 SkPath::Direction dir) {
1942 SkPath path;
1943
1944 // circle at origin
1945 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001946 check_for_circle(reporter, path, true, dir);
1947 test_circle_rotate(reporter, path, dir);
1948 test_circle_translate(reporter, path, dir);
1949 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001950
1951 // circle at an offset at (10, 10)
1952 path.reset();
1953 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1954 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001955 check_for_circle(reporter, path, true, dir);
1956 test_circle_rotate(reporter, path, dir);
1957 test_circle_translate(reporter, path, dir);
1958 test_circle_skew(reporter, path, dir);
1959 test_circle_mirror_x(reporter, path, dir);
1960 test_circle_mirror_y(reporter, path, dir);
1961 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001962}
1963
1964static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1965 SkPath path;
1966 SkPath circle;
1967 SkPath rect;
1968 SkPath empty;
1969
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001970 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
1971 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
1972
1973 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001974 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1975 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1976
1977 SkMatrix translate;
1978 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1979
1980 // For simplicity, all the path concatenation related operations
1981 // would mark it non-circle, though in theory it's still a circle.
1982
1983 // empty + circle (translate)
1984 path = empty;
1985 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001986 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001987
1988 // circle + empty (translate)
1989 path = circle;
1990 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001991 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001992
1993 // test reverseAddPath
1994 path = circle;
1995 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001996 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001997}
1998
1999static void test_circle(skiatest::Reporter* reporter) {
2000 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2001 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2002
2003 // multiple addCircle()
2004 SkPath path;
2005 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2006 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002007 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002008
2009 // some extra lineTo() would make isOval() fail
2010 path.reset();
2011 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2012 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002013 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002014
2015 // not back to the original point
2016 path.reset();
2017 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2018 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002019 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002020
2021 test_circle_with_add_paths(reporter);
2022}
2023
2024static void test_oval(skiatest::Reporter* reporter) {
2025 SkRect rect;
2026 SkMatrix m;
2027 SkPath path;
2028
2029 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2030 path.addOval(rect);
2031
2032 REPORTER_ASSERT(reporter, path.isOval(NULL));
2033
2034 m.setRotate(SkIntToScalar(90));
2035 SkPath tmp;
2036 path.transform(m, &tmp);
2037 // an oval rotated 90 degrees is still an oval.
2038 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2039
2040 m.reset();
2041 m.setRotate(SkIntToScalar(30));
2042 tmp.reset();
2043 path.transform(m, &tmp);
2044 // an oval rotated 30 degrees is not an oval anymore.
2045 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2046
2047 // since empty path being transformed.
2048 path.reset();
2049 tmp.reset();
2050 m.reset();
2051 path.transform(m, &tmp);
2052 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2053
2054 // empty path is not an oval
2055 tmp.reset();
2056 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2057
2058 // only has moveTo()s
2059 tmp.reset();
2060 tmp.moveTo(0, 0);
2061 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2062 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2063
2064 // mimic WebKit's calling convention,
2065 // call moveTo() first and then call addOval()
2066 path.reset();
2067 path.moveTo(0, 0);
2068 path.addOval(rect);
2069 REPORTER_ASSERT(reporter, path.isOval(NULL));
2070
2071 // copy path
2072 path.reset();
2073 tmp.reset();
2074 tmp.addOval(rect);
2075 path = tmp;
2076 REPORTER_ASSERT(reporter, path.isOval(NULL));
2077}
2078
caryclark@google.com42639cd2012-06-06 12:03:39 +00002079static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002080 SkTSize<SkScalar>::Make(3,4);
2081
reed@android.com3abec1d2009-03-02 05:36:20 +00002082 SkPath p, p2;
2083 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002084
reed@android.com3abec1d2009-03-02 05:36:20 +00002085 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002086 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002087 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002088 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002089 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002090 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2091 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2092 REPORTER_ASSERT(reporter, p == p2);
2093 REPORTER_ASSERT(reporter, !(p != p2));
2094
reed@android.comd252db02009-04-01 18:31:44 +00002095 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002096
reed@android.com3abec1d2009-03-02 05:36:20 +00002097 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002098
reed@android.com6b82d1a2009-06-03 02:35:01 +00002099 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2100 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002101 // we have quads or cubics
2102 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002103 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002104
reed@android.com6b82d1a2009-06-03 02:35:01 +00002105 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002106 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002107 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002108
reed@android.com6b82d1a2009-06-03 02:35:01 +00002109 p.addOval(bounds);
2110 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002111 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002112
reed@android.com6b82d1a2009-06-03 02:35:01 +00002113 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002114 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002115 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002116 // we have only lines
2117 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002118 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002119
2120 REPORTER_ASSERT(reporter, p != p2);
2121 REPORTER_ASSERT(reporter, !(p == p2));
2122
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002123 // do getPoints and getVerbs return the right result
2124 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2125 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002126 SkPoint pts[4];
2127 int count = p.getPoints(pts, 4);
2128 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002129 uint8_t verbs[6];
2130 verbs[5] = 0xff;
2131 p.getVerbs(verbs, 5);
2132 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2133 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2134 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2135 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2136 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2137 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002138 bounds2.set(pts, 4);
2139 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002140
reed@android.com3abec1d2009-03-02 05:36:20 +00002141 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2142 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002143 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002144
reed@android.com3abec1d2009-03-02 05:36:20 +00002145 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002146 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002147 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2148 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002149
reed@android.com3abec1d2009-03-02 05:36:20 +00002150 // now force p to not be a rect
2151 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2152 p.addRect(bounds);
2153 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002154
reed@google.com7e6c4d12012-05-10 14:05:43 +00002155 test_isLine(reporter);
2156 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002157 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002158 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002159 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002160 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002161 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002162 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002163 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002164 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002165 test_flattening(reporter);
2166 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002167 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002168 test_iter(reporter);
2169 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002170 test_circle(reporter);
2171 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002172 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002173 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002174 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002175 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002176 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002177 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002178 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002179 test_addrect_isfinite(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002180}
2181
2182#include "TestClassDef.h"
2183DEFINE_TESTCLASS("Path", PathTestClass, TestPath)