blob: e65326cef38caa0792e8412684b3b4b418ca7621 [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.com7a90daf2013-04-10 18:44:00 +000034static void test_bad_cubic_crbug229478() {
35 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000036 { 4595.91064f, -11596.9873f },
37 { 4597.2168f, -11595.9414f },
38 { 4598.52344f, -11594.8955f },
39 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +000040 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000041
reed@google.com7a90daf2013-04-10 18:44:00 +000042 SkPath path;
43 path.moveTo(pts[0]);
44 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000045
reed@google.com7a90daf2013-04-10 18:44:00 +000046 SkPaint paint;
47 paint.setStyle(SkPaint::kStroke_Style);
48 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000049
reed@google.com7a90daf2013-04-10 18:44:00 +000050 SkPath dst;
51 // Before the fix, this would infinite-recurse, and run out of stack
52 // because we would keep trying to subdivide a degenerate cubic segment.
53 paint.getFillPath(path, &dst, NULL);
54}
55
reed@google.com64d62952013-01-18 17:49:28 +000056static void build_path_170666(SkPath& path) {
57 path.moveTo(17.9459f, 21.6344f);
58 path.lineTo(139.545f, -47.8105f);
59 path.lineTo(139.545f, -47.8105f);
60 path.lineTo(131.07f, -47.3888f);
61 path.lineTo(131.07f, -47.3888f);
62 path.lineTo(122.586f, -46.9532f);
63 path.lineTo(122.586f, -46.9532f);
64 path.lineTo(18076.6f, 31390.9f);
65 path.lineTo(18076.6f, 31390.9f);
66 path.lineTo(18085.1f, 31390.5f);
67 path.lineTo(18085.1f, 31390.5f);
68 path.lineTo(18076.6f, 31390.9f);
69 path.lineTo(18076.6f, 31390.9f);
70 path.lineTo(17955, 31460.3f);
71 path.lineTo(17955, 31460.3f);
72 path.lineTo(17963.5f, 31459.9f);
73 path.lineTo(17963.5f, 31459.9f);
74 path.lineTo(17971.9f, 31459.5f);
75 path.lineTo(17971.9f, 31459.5f);
76 path.lineTo(17.9551f, 21.6205f);
77 path.lineTo(17.9551f, 21.6205f);
78 path.lineTo(9.47091f, 22.0561f);
79 path.lineTo(9.47091f, 22.0561f);
80 path.lineTo(17.9459f, 21.6344f);
81 path.lineTo(17.9459f, 21.6344f);
82 path.close();path.moveTo(0.995934f, 22.4779f);
83 path.lineTo(0.986725f, 22.4918f);
84 path.lineTo(0.986725f, 22.4918f);
85 path.lineTo(17955, 31460.4f);
86 path.lineTo(17955, 31460.4f);
87 path.lineTo(17971.9f, 31459.5f);
88 path.lineTo(17971.9f, 31459.5f);
89 path.lineTo(18093.6f, 31390.1f);
90 path.lineTo(18093.6f, 31390.1f);
91 path.lineTo(18093.6f, 31390);
92 path.lineTo(18093.6f, 31390);
93 path.lineTo(139.555f, -47.8244f);
94 path.lineTo(139.555f, -47.8244f);
95 path.lineTo(122.595f, -46.9671f);
96 path.lineTo(122.595f, -46.9671f);
97 path.lineTo(0.995934f, 22.4779f);
98 path.lineTo(0.995934f, 22.4779f);
99 path.close();
100 path.moveTo(5.43941f, 25.5223f);
101 path.lineTo(798267, -28871.1f);
102 path.lineTo(798267, -28871.1f);
103 path.lineTo(3.12512e+06f, -113102);
104 path.lineTo(3.12512e+06f, -113102);
105 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
106 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
107 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
108 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
109 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
110 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
111 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
112 path.lineTo(2.78271e+08f, -1.00733e+07f);
113 path.lineTo(2.78271e+08f, -1.00733e+07f);
114 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
115 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
116 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
117 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
118 path.lineTo(2.77473e+08f, -1.00444e+07f);
119 path.lineTo(2.77473e+08f, -1.00444e+07f);
120 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
121 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
122 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
123 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
124 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
125 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
126 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
127 path.lineTo(798284, -28872);
128 path.lineTo(798284, -28872);
129 path.lineTo(22.4044f, 24.6677f);
130 path.lineTo(22.4044f, 24.6677f);
131 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
132 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
133 path.close();
134}
135
136static void build_path_simple_170666(SkPath& path) {
137 path.moveTo(126.677f, 24.1591f);
138 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
139}
140
141// This used to assert in the SK_DEBUG build, as the clip step would fail with
142// too-few interations in our cubic-line intersection code. That code now runs
143// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000144static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000145 SkPath path;
146 SkPaint paint;
147 paint.setAntiAlias(true);
148
149 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000150
reed@google.com64d62952013-01-18 17:49:28 +0000151 build_path_simple_170666(path);
152 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000153
reed@google.com64d62952013-01-18 17:49:28 +0000154 build_path_170666(path);
155 surface->getCanvas()->drawPath(path, paint);
156}
157
reed@google.coma8790de2012-10-24 21:04:04 +0000158// Make sure we stay non-finite once we get there (unless we reset or rewind).
159static void test_addrect_isfinite(skiatest::Reporter* reporter) {
160 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000161
reed@google.coma8790de2012-10-24 21:04:04 +0000162 path.addRect(SkRect::MakeWH(50, 100));
163 REPORTER_ASSERT(reporter, path.isFinite());
164
165 path.moveTo(0, 0);
166 path.lineTo(SK_ScalarInfinity, 42);
167 REPORTER_ASSERT(reporter, !path.isFinite());
168
169 path.addRect(SkRect::MakeWH(50, 100));
170 REPORTER_ASSERT(reporter, !path.isFinite());
171
172 path.reset();
173 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000174
reed@google.coma8790de2012-10-24 21:04:04 +0000175 path.addRect(SkRect::MakeWH(50, 100));
176 REPORTER_ASSERT(reporter, path.isFinite());
177}
178
reed@google.com848148e2013-01-15 15:51:59 +0000179static void build_big_path(SkPath* path, bool reducedCase) {
180 if (reducedCase) {
181 path->moveTo(577330, 1971.72f);
182 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
183 } else {
184 path->moveTo(60.1631f, 7.70567f);
185 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
186 path->lineTo(577379, 1977.77f);
187 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
188 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
189 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
190 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
191 path->quadTo(577198, 1972, 577238, 1971.31f);
192 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
193 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
194 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
195 path->moveTo(306.718f, -32.912f);
196 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
197 }
198}
199
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000200static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000201 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
202
203 // This path used to assert, because our cubic-chopping code incorrectly
204 // moved control points after the chop. This test should be run in SK_DEBUG
205 // mode to ensure that we no long assert.
206 SkPath path;
207 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
208 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000209
reed@google.com848148e2013-01-15 15:51:59 +0000210 SkPaint paint;
211 for (int doAA = 0; doAA <= 1; ++doAA) {
212 paint.setAntiAlias(SkToBool(doAA));
213 surface->getCanvas()->drawPath(path, paint);
214 }
215 }
216}
217
reed@google.com8cae8352012-09-14 15:18:41 +0000218// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
219// which triggered an assert, from a tricky cubic. This test replicates that
220// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
221// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000222static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000223 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000224 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
225 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
226 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
227 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000228 };
229
230 SkPath path;
231 path.moveTo(pts[0]);
232 path.cubicTo(pts[1], pts[2], pts[3]);
233
234 SkPaint paint;
235 paint.setAntiAlias(true);
236
237 SkSurface* surface = new_surface(19, 130);
238 surface->getCanvas()->drawPath(path, paint);
239 surface->unref();
240}
reed@android.com3abec1d2009-03-02 05:36:20 +0000241
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000242// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
243//
244static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
245 SkPath path;
246 path.quadTo(157, 366, 286, 208);
247 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000248
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000249 SkMatrix matrix;
250 matrix.setScale(1000*1000, 1000*1000);
251
252 // Be sure that path::transform correctly updates isFinite and the bounds
253 // if the transformation overflows. The previous bug was that isFinite was
254 // set to true in this case, but the bounds were not set to empty (which
255 // they should be).
256 while (path.isFinite()) {
257 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
258 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
259 path.transform(matrix);
260 }
261 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
262
263 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
264 path.transform(matrix);
265 // we need to still be non-finite
266 REPORTER_ASSERT(reporter, !path.isFinite());
267 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
268}
269
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000270static void add_corner_arc(SkPath* path, const SkRect& rect,
271 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000272 int startAngle)
273{
274
275 SkScalar rx = SkMinScalar(rect.width(), xIn);
276 SkScalar ry = SkMinScalar(rect.height(), yIn);
277
278 SkRect arcRect;
279 arcRect.set(-rx, -ry, rx, ry);
280 switch (startAngle) {
281 case 0:
282 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
283 break;
284 case 90:
285 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
286 break;
287 case 180:
288 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
289 break;
290 case 270:
291 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
292 break;
293 default:
294 break;
295 }
296
297 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
298}
299
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000300static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000301 SkScalar xCorner, SkScalar yCorner) {
302 // we are lazy here and use the same x & y for each corner
303 add_corner_arc(path, r, xCorner, yCorner, 270);
304 add_corner_arc(path, r, xCorner, yCorner, 0);
305 add_corner_arc(path, r, xCorner, yCorner, 90);
306 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000307 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000308}
309
310// Chrome creates its own round rects with each corner possibly being different.
311// Performance will suffer if they are not convex.
312// Note: PathBench::ArbRoundRectBench performs almost exactly
313// the same test (but with drawing)
314static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000315 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000316 SkRect r;
317
318 for (int i = 0; i < 5000; ++i) {
319
robertphillips@google.com158618e2012-10-23 16:56:56 +0000320 SkScalar size = rand.nextUScalar1() * 30;
321 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000322 continue;
323 }
324 r.fLeft = rand.nextUScalar1() * 300;
325 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000326 r.fRight = r.fLeft + 2 * size;
327 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000328
329 SkPath temp;
330
331 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
332
333 REPORTER_ASSERT(reporter, temp.isConvex());
334 }
335}
336
robertphillips@google.com158618e2012-10-23 16:56:56 +0000337// Chrome will sometimes create a 0 radius round rect. The degenerate
338// quads prevent the path from being converted to a rect
339// Note: PathBench::ArbRoundRectBench performs almost exactly
340// the same test (but with drawing)
341static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000342 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000343 SkRect r;
344
345 for (int i = 0; i < 5000; ++i) {
346
347 SkScalar size = rand.nextUScalar1() * 30;
348 if (size < SK_Scalar1) {
349 continue;
350 }
351 r.fLeft = rand.nextUScalar1() * 300;
352 r.fTop = rand.nextUScalar1() * 300;
353 r.fRight = r.fLeft + 2 * size;
354 r.fBottom = r.fTop + 2 * size;
355
356 SkPath temp;
357
358 make_arb_round_rect(&temp, r, 0, 0);
359
robertphillips@google.com158618e2012-10-23 16:56:56 +0000360 SkRect result;
361 REPORTER_ASSERT(reporter, temp.isRect(&result));
362 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000363 }
364}
365
reed@google.com0bb18bb2012-07-26 15:20:36 +0000366static void test_rect_isfinite(skiatest::Reporter* reporter) {
367 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000368 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000369 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000370
reed@google.com0bb18bb2012-07-26 15:20:36 +0000371 SkRect r;
372 r.setEmpty();
373 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000374 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000375 REPORTER_ASSERT(reporter, !r.isFinite());
376 r.set(0, 0, nan, 0);
377 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000378
reed@google.com0bb18bb2012-07-26 15:20:36 +0000379 SkPoint pts[] = {
380 { 0, 0 },
381 { SK_Scalar1, 0 },
382 { 0, SK_Scalar1 },
383 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
reed@google.com0bb18bb2012-07-26 15:20:36 +0000385 bool isFine = r.setBoundsCheck(pts, 3);
386 REPORTER_ASSERT(reporter, isFine);
387 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000388
reed@google.com0bb18bb2012-07-26 15:20:36 +0000389 pts[1].set(inf, 0);
390 isFine = r.setBoundsCheck(pts, 3);
391 REPORTER_ASSERT(reporter, !isFine);
392 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000393
reed@google.com0bb18bb2012-07-26 15:20:36 +0000394 pts[1].set(nan, 0);
395 isFine = r.setBoundsCheck(pts, 3);
396 REPORTER_ASSERT(reporter, !isFine);
397 REPORTER_ASSERT(reporter, r.isEmpty());
398}
399
400static void test_path_isfinite(skiatest::Reporter* reporter) {
401 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000402 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000403 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000404
reed@google.com0bb18bb2012-07-26 15:20:36 +0000405 SkPath path;
406 REPORTER_ASSERT(reporter, path.isFinite());
407
408 path.reset();
409 REPORTER_ASSERT(reporter, path.isFinite());
410
411 path.reset();
412 path.moveTo(SK_Scalar1, 0);
413 REPORTER_ASSERT(reporter, path.isFinite());
414
415 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000416 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000417 REPORTER_ASSERT(reporter, !path.isFinite());
418
419 path.reset();
420 path.moveTo(nan, 0);
421 REPORTER_ASSERT(reporter, !path.isFinite());
422}
423
424static void test_isfinite(skiatest::Reporter* reporter) {
425 test_rect_isfinite(reporter);
426 test_path_isfinite(reporter);
427}
428
reed@google.com744faba2012-05-29 19:54:52 +0000429// assert that we always
430// start with a moveTo
431// only have 1 moveTo
432// only have Lines after that
433// end with a single close
434// only have (at most) 1 close
435//
436static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000437 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000438 SkPath::RawIter iter(path);
439 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000440
441 bool firstTime = true;
442 bool foundClose = false;
443 for (;;) {
444 switch (iter.next(pts)) {
445 case SkPath::kMove_Verb:
446 REPORTER_ASSERT(reporter, firstTime);
447 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
448 srcPts++;
449 firstTime = false;
450 break;
451 case SkPath::kLine_Verb:
452 REPORTER_ASSERT(reporter, !firstTime);
453 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
454 srcPts++;
455 break;
456 case SkPath::kQuad_Verb:
457 REPORTER_ASSERT(reporter, !"unexpected quad verb");
458 break;
459 case SkPath::kCubic_Verb:
460 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
461 break;
462 case SkPath::kClose_Verb:
463 REPORTER_ASSERT(reporter, !firstTime);
464 REPORTER_ASSERT(reporter, !foundClose);
465 REPORTER_ASSERT(reporter, expectClose);
466 foundClose = true;
467 break;
468 case SkPath::kDone_Verb:
469 goto DONE;
470 }
471 }
472DONE:
473 REPORTER_ASSERT(reporter, foundClose == expectClose);
474}
475
476static void test_addPoly(skiatest::Reporter* reporter) {
477 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000478 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000479
reed@google.com744faba2012-05-29 19:54:52 +0000480 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
481 pts[i].fX = rand.nextSScalar1();
482 pts[i].fY = rand.nextSScalar1();
483 }
484
485 for (int doClose = 0; doClose <= 1; ++doClose) {
486 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
487 SkPath path;
488 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000489 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000490 }
491 }
492}
493
reed@google.com8b06f1a2012-05-29 12:03:46 +0000494static void test_strokerec(skiatest::Reporter* reporter) {
495 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
496 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000497
reed@google.com8b06f1a2012-05-29 12:03:46 +0000498 rec.setHairlineStyle();
499 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000500
reed@google.com8b06f1a2012-05-29 12:03:46 +0000501 rec.setStrokeStyle(SK_Scalar1, false);
502 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000503
reed@google.com8b06f1a2012-05-29 12:03:46 +0000504 rec.setStrokeStyle(SK_Scalar1, true);
505 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000506
reed@google.com8b06f1a2012-05-29 12:03:46 +0000507 rec.setStrokeStyle(0, false);
508 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000509
reed@google.com8b06f1a2012-05-29 12:03:46 +0000510 rec.setStrokeStyle(0, true);
511 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
512}
513
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000514// Set this for paths that don't have a consistent direction such as a bowtie.
515// (cheapComputeDirection is not expected to catch these.)
516static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
517
518static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
519 SkPath::Direction expected) {
520 if (expected == kDontCheckDir) {
521 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000522 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000523 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
524
525 SkPath::Direction dir;
526 if (copy.cheapComputeDirection(&dir)) {
527 REPORTER_ASSERT(reporter, dir == expected);
528 } else {
529 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
530 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000531}
532
reed@google.com3e71a882012-01-10 18:44:37 +0000533static void test_direction(skiatest::Reporter* reporter) {
534 size_t i;
535 SkPath path;
536 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
537 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
538 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000539 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000540
541 static const char* gDegen[] = {
542 "M 10 10",
543 "M 10 10 M 20 20",
544 "M 10 10 L 20 20",
545 "M 10 10 L 10 10 L 10 10",
546 "M 10 10 Q 10 10 10 10",
547 "M 10 10 C 10 10 10 10 10 10",
548 };
549 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
550 path.reset();
551 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
552 REPORTER_ASSERT(reporter, valid);
553 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
554 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000555
reed@google.com3e71a882012-01-10 18:44:37 +0000556 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000557 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000558 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000559 "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 +0000560 // rect with top two corners replaced by cubics with identical middle
561 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000562 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
563 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000564 };
565 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
566 path.reset();
567 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
568 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000569 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000570 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000571
reed@google.com3e71a882012-01-10 18:44:37 +0000572 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000573 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000574 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000575 "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 +0000576 // rect with top two corners replaced by cubics with identical middle
577 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000578 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
579 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000580 };
581 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
582 path.reset();
583 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
584 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000585 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000586 }
reed@google.comac8543f2012-01-30 20:51:25 +0000587
588 // Test two donuts, each wound a different direction. Only the outer contour
589 // determines the cheap direction
590 path.reset();
591 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
592 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000593 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000594
reed@google.comac8543f2012-01-30 20:51:25 +0000595 path.reset();
596 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
597 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000598 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000599
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000600#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000601 // triangle with one point really far from the origin.
602 path.reset();
603 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000604 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
605 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
606 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000607 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000608#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000609}
610
reed@google.comffdb0182011-11-14 19:29:14 +0000611static void add_rect(SkPath* path, const SkRect& r) {
612 path->moveTo(r.fLeft, r.fTop);
613 path->lineTo(r.fRight, r.fTop);
614 path->lineTo(r.fRight, r.fBottom);
615 path->lineTo(r.fLeft, r.fBottom);
616 path->close();
617}
618
619static void test_bounds(skiatest::Reporter* reporter) {
620 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000621 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
622 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
623 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
624 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000625 };
626
627 SkPath path0, path1;
628 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
629 path0.addRect(rects[i]);
630 add_rect(&path1, rects[i]);
631 }
632
633 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
634}
635
reed@google.com55b5f4b2011-09-07 12:23:41 +0000636static void stroke_cubic(const SkPoint pts[4]) {
637 SkPath path;
638 path.moveTo(pts[0]);
639 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000640
reed@google.com55b5f4b2011-09-07 12:23:41 +0000641 SkPaint paint;
642 paint.setStyle(SkPaint::kStroke_Style);
643 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000644
reed@google.com55b5f4b2011-09-07 12:23:41 +0000645 SkPath fill;
646 paint.getFillPath(path, &fill);
647}
648
649// just ensure this can run w/o any SkASSERTS firing in the debug build
650// we used to assert due to differences in how we determine a degenerate vector
651// but that was fixed with the introduction of SkPoint::CanNormalize
652static void stroke_tiny_cubic() {
653 SkPoint p0[] = {
654 { 372.0f, 92.0f },
655 { 372.0f, 92.0f },
656 { 372.0f, 92.0f },
657 { 372.0f, 92.0f },
658 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000659
reed@google.com55b5f4b2011-09-07 12:23:41 +0000660 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000661
reed@google.com55b5f4b2011-09-07 12:23:41 +0000662 SkPoint p1[] = {
663 { 372.0f, 92.0f },
664 { 372.0007f, 92.000755f },
665 { 371.99927f, 92.003922f },
666 { 371.99826f, 92.003899f },
667 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000668
reed@google.com55b5f4b2011-09-07 12:23:41 +0000669 stroke_cubic(p1);
670}
671
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000672static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
673 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000674 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000675 SkPoint mv;
676 SkPoint pts[4];
677 SkPath::Verb v;
678 int nMT = 0;
679 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000680 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000681 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
682 switch (v) {
683 case SkPath::kMove_Verb:
684 mv = pts[0];
685 ++nMT;
686 break;
687 case SkPath::kClose_Verb:
688 REPORTER_ASSERT(reporter, mv == pts[0]);
689 ++nCL;
690 break;
691 default:
692 break;
693 }
694 }
695 // if we force a close on the interator we should have a close
696 // for every moveTo
697 REPORTER_ASSERT(reporter, !i || nMT == nCL);
698 }
699}
700
701static void test_close(skiatest::Reporter* reporter) {
702 SkPath closePt;
703 closePt.moveTo(0, 0);
704 closePt.close();
705 check_close(reporter, closePt);
706
707 SkPath openPt;
708 openPt.moveTo(0, 0);
709 check_close(reporter, openPt);
710
711 SkPath empty;
712 check_close(reporter, empty);
713 empty.close();
714 check_close(reporter, empty);
715
716 SkPath rect;
717 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
718 check_close(reporter, rect);
719 rect.close();
720 check_close(reporter, rect);
721
722 SkPath quad;
723 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
724 check_close(reporter, quad);
725 quad.close();
726 check_close(reporter, quad);
727
728 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000729 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000730 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
731 check_close(reporter, cubic);
732 cubic.close();
733 check_close(reporter, cubic);
734
735 SkPath line;
736 line.moveTo(SK_Scalar1, SK_Scalar1);
737 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
738 check_close(reporter, line);
739 line.close();
740 check_close(reporter, line);
741
742 SkPath rect2;
743 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
744 rect2.close();
745 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
746 check_close(reporter, rect2);
747 rect2.close();
748 check_close(reporter, rect2);
749
750 SkPath oval3;
751 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
752 oval3.close();
753 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
754 check_close(reporter, oval3);
755 oval3.close();
756 check_close(reporter, oval3);
757
758 SkPath moves;
759 moves.moveTo(SK_Scalar1, SK_Scalar1);
760 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
761 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
762 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
763 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000764
765 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000766}
767
reed@google.com7c424812011-05-15 04:38:34 +0000768static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
769 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000770 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
771 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000772 REPORTER_ASSERT(reporter, c == expected);
773}
774
775static void test_convexity2(skiatest::Reporter* reporter) {
776 SkPath pt;
777 pt.moveTo(0, 0);
778 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000779 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000780 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000781
reed@google.com7c424812011-05-15 04:38:34 +0000782 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000783 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
784 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000785 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000786 check_convexity(reporter, line, SkPath::kConvex_Convexity);
787 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000788
reed@google.com7c424812011-05-15 04:38:34 +0000789 SkPath triLeft;
790 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000791 triLeft.lineTo(SK_Scalar1, 0);
792 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000793 triLeft.close();
794 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000795 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000796
reed@google.com7c424812011-05-15 04:38:34 +0000797 SkPath triRight;
798 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000799 triRight.lineTo(-SK_Scalar1, 0);
800 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000801 triRight.close();
802 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000803 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000804
reed@google.com7c424812011-05-15 04:38:34 +0000805 SkPath square;
806 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000807 square.lineTo(SK_Scalar1, 0);
808 square.lineTo(SK_Scalar1, SK_Scalar1);
809 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000810 square.close();
811 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000812 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000813
reed@google.com7c424812011-05-15 04:38:34 +0000814 SkPath redundantSquare;
815 redundantSquare.moveTo(0, 0);
816 redundantSquare.lineTo(0, 0);
817 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000818 redundantSquare.lineTo(SK_Scalar1, 0);
819 redundantSquare.lineTo(SK_Scalar1, 0);
820 redundantSquare.lineTo(SK_Scalar1, 0);
821 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
822 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
823 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
824 redundantSquare.lineTo(0, SK_Scalar1);
825 redundantSquare.lineTo(0, SK_Scalar1);
826 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000827 redundantSquare.close();
828 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000829 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000830
reed@google.com7c424812011-05-15 04:38:34 +0000831 SkPath bowTie;
832 bowTie.moveTo(0, 0);
833 bowTie.lineTo(0, 0);
834 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000835 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
836 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
837 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
838 bowTie.lineTo(SK_Scalar1, 0);
839 bowTie.lineTo(SK_Scalar1, 0);
840 bowTie.lineTo(SK_Scalar1, 0);
841 bowTie.lineTo(0, SK_Scalar1);
842 bowTie.lineTo(0, SK_Scalar1);
843 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000844 bowTie.close();
845 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000846 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000847
reed@google.com7c424812011-05-15 04:38:34 +0000848 SkPath spiral;
849 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000850 spiral.lineTo(100*SK_Scalar1, 0);
851 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
852 spiral.lineTo(0, 100*SK_Scalar1);
853 spiral.lineTo(0, 50*SK_Scalar1);
854 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
855 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000856 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000857 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000858 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000859
reed@google.com7c424812011-05-15 04:38:34 +0000860 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000861 dent.moveTo(0, 0);
862 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
863 dent.lineTo(0, 100*SK_Scalar1);
864 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
865 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000866 dent.close();
867 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000868 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000869}
870
reed@android.com6b82d1a2009-06-03 02:35:01 +0000871static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
872 const SkRect& bounds) {
873 REPORTER_ASSERT(reporter, p.isConvex());
874 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000875
reed@android.com6b82d1a2009-06-03 02:35:01 +0000876 SkPath p2(p);
877 REPORTER_ASSERT(reporter, p2.isConvex());
878 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
879
880 SkPath other;
881 other.swap(p2);
882 REPORTER_ASSERT(reporter, other.isConvex());
883 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
884}
885
reed@google.com04863fa2011-05-15 04:08:24 +0000886static void setFromString(SkPath* path, const char str[]) {
887 bool first = true;
888 while (str) {
889 SkScalar x, y;
890 str = SkParse::FindScalar(str, &x);
891 if (NULL == str) {
892 break;
893 }
894 str = SkParse::FindScalar(str, &y);
895 SkASSERT(str);
896 if (first) {
897 path->moveTo(x, y);
898 first = false;
899 } else {
900 path->lineTo(x, y);
901 }
902 }
903}
904
905static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000906 SkPath path;
907
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000908 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000909 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000910 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000911 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000912 check_convexity(reporter, path, SkPath::kConcave_Convexity);
913
reed@google.com04863fa2011-05-15 04:08:24 +0000914 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000915 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000916 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000917 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000918
reed@google.com04863fa2011-05-15 04:08:24 +0000919 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000920 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000921 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000922 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000923
reed@google.com04863fa2011-05-15 04:08:24 +0000924 static const struct {
925 const char* fPathStr;
926 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000927 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000928 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000929 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
930 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
931 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
932 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
933 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
934 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
935 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
936 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000937 };
938
939 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
940 SkPath path;
941 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000942 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
943 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000944 }
945}
946
reed@google.com7e6c4d12012-05-10 14:05:43 +0000947static void test_isLine(skiatest::Reporter* reporter) {
948 SkPath path;
949 SkPoint pts[2];
950 const SkScalar value = SkIntToScalar(5);
951
952 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000953
reed@google.com7e6c4d12012-05-10 14:05:43 +0000954 // set some non-zero values
955 pts[0].set(value, value);
956 pts[1].set(value, value);
957 REPORTER_ASSERT(reporter, !path.isLine(pts));
958 // check that pts was untouched
959 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
960 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
961
962 const SkScalar moveX = SkIntToScalar(1);
963 const SkScalar moveY = SkIntToScalar(2);
964 SkASSERT(value != moveX && value != moveY);
965
966 path.moveTo(moveX, moveY);
967 REPORTER_ASSERT(reporter, !path.isLine(NULL));
968 REPORTER_ASSERT(reporter, !path.isLine(pts));
969 // check that pts was untouched
970 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
971 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
972
973 const SkScalar lineX = SkIntToScalar(2);
974 const SkScalar lineY = SkIntToScalar(2);
975 SkASSERT(value != lineX && value != lineY);
976
977 path.lineTo(lineX, lineY);
978 REPORTER_ASSERT(reporter, path.isLine(NULL));
979
980 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
981 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
982 REPORTER_ASSERT(reporter, path.isLine(pts));
983 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
984 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
985
986 path.lineTo(0, 0); // too many points/verbs
987 REPORTER_ASSERT(reporter, !path.isLine(NULL));
988 REPORTER_ASSERT(reporter, !path.isLine(pts));
989 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
990 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
991}
992
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000993static void test_conservativelyContains(skiatest::Reporter* reporter) {
994 SkPath path;
995
996 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
997 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
998
999 // A circle that bounds kBaseRect (with a significant amount of slop)
1000 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1001 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1002 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1003
1004 // round-rect radii
1005 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001006
caryclark@google.com56f233a2012-11-19 13:06:06 +00001007 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001008 SkRect fQueryRect;
1009 bool fInRect;
1010 bool fInCircle;
1011 bool fInRR;
1012 } kQueries[] = {
1013 {kBaseRect, true, true, false},
1014
1015 // rect well inside of kBaseRect
1016 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1017 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1018 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1019 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1020 true, true, true},
1021
1022 // rects with edges off by one from kBaseRect's edges
1023 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1024 kBaseRect.width(), kBaseRect.height() + 1),
1025 false, true, false},
1026 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1027 kBaseRect.width() + 1, kBaseRect.height()),
1028 false, true, false},
1029 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1030 kBaseRect.width() + 1, kBaseRect.height() + 1),
1031 false, true, false},
1032 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1033 kBaseRect.width(), kBaseRect.height()),
1034 false, true, false},
1035 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1036 kBaseRect.width(), kBaseRect.height()),
1037 false, true, false},
1038 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1039 kBaseRect.width() + 2, kBaseRect.height()),
1040 false, true, false},
1041 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1042 kBaseRect.width() + 2, kBaseRect.height()),
1043 false, true, false},
1044
1045 // zero-w/h rects at each corner of kBaseRect
1046 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1047 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1048 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1049 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1050
1051 // far away rect
1052 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1053 SkIntToScalar(10), SkIntToScalar(10)),
1054 false, false, false},
1055
1056 // very large rect containing kBaseRect
1057 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1058 kBaseRect.fTop - 5 * kBaseRect.height(),
1059 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1060 false, false, false},
1061
1062 // skinny rect that spans same y-range as kBaseRect
1063 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1064 SkIntToScalar(1), kBaseRect.height()),
1065 true, true, true},
1066
1067 // short rect that spans same x-range as kBaseRect
1068 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1069 true, true, true},
1070
1071 // skinny rect that spans slightly larger y-range than kBaseRect
1072 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1073 SkIntToScalar(1), kBaseRect.height() + 1),
1074 false, true, false},
1075
1076 // short rect that spans slightly larger x-range than kBaseRect
1077 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1078 kBaseRect.width() + 1, SkScalar(1)),
1079 false, true, false},
1080 };
1081
1082 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001083 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001084 SkRect qRect = kQueries[q].fQueryRect;
1085 if (inv & 0x1) {
1086 SkTSwap(qRect.fLeft, qRect.fRight);
1087 }
1088 if (inv & 0x2) {
1089 SkTSwap(qRect.fTop, qRect.fBottom);
1090 }
1091 for (int d = 0; d < 2; ++d) {
1092 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1093 path.reset();
1094 path.addRect(kBaseRect, dir);
1095 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1096 path.conservativelyContainsRect(qRect));
1097
1098 path.reset();
1099 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1100 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1101 path.conservativelyContainsRect(qRect));
1102
1103 path.reset();
1104 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1105 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1106 path.conservativelyContainsRect(qRect));
1107 }
1108 // Slightly non-convex shape, shouldn't contain any rects.
1109 path.reset();
1110 path.moveTo(0, 0);
1111 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1112 path.lineTo(SkIntToScalar(100), 0);
1113 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1114 path.lineTo(0, SkIntToScalar(100));
1115 path.close();
1116 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1117 }
1118 }
1119
1120 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1121 path.reset();
1122 path.moveTo(0, 0);
1123 path.lineTo(SkIntToScalar(100), 0);
1124 path.lineTo(0, SkIntToScalar(100));
1125
1126 // inside, on along top edge
1127 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1128 SkIntToScalar(10),
1129 SkIntToScalar(10))));
1130 // above
1131 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1132 SkRect::MakeXYWH(SkIntToScalar(50),
1133 SkIntToScalar(-10),
1134 SkIntToScalar(10),
1135 SkIntToScalar(10))));
1136 // to the left
1137 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1138 SkIntToScalar(5),
1139 SkIntToScalar(5),
1140 SkIntToScalar(5))));
1141
1142 // outside the diagonal edge
1143 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1144 SkIntToScalar(200),
1145 SkIntToScalar(20),
1146 SkIntToScalar(5))));
1147}
1148
caryclark@google.comf1316942011-07-26 19:54:45 +00001149// Simple isRect test is inline TestPath, below.
1150// test_isRect provides more extensive testing.
1151static void test_isRect(skiatest::Reporter* reporter) {
1152 // passing tests (all moveTo / lineTo...
1153 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1154 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1155 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1156 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1157 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1158 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1159 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1160 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1161 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1162 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1163 {1, 0}, {.5f, 0}};
1164 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1165 {0, 1}, {0, .5f}};
1166 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1167 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1168 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001169 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001170
caryclark@google.comf1316942011-07-26 19:54:45 +00001171 // failing tests
1172 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1173 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1174 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1175 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1176 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1177 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1178 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1179 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001180 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1181 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1182 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001183
caryclark@google.comf1316942011-07-26 19:54:45 +00001184 // failing, no close
1185 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1186 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1187
1188 size_t testLen[] = {
1189 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1190 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001191 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001192 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001193 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001194 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001195 };
1196 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001197 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1198 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001199 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001200 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001201 SkPoint* lastPass = rf;
1202 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001203 bool fail = false;
1204 bool close = true;
1205 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1206 size_t index;
1207 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1208 SkPath path;
1209 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1210 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1211 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1212 }
1213 if (close) {
1214 path.close();
1215 }
1216 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001217 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1218
caryclark@google.com56f233a2012-11-19 13:06:06 +00001219 if (!fail) {
1220 SkRect computed, expected;
1221 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1222 REPORTER_ASSERT(reporter, path.isRect(&computed));
1223 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001224
caryclark@google.comf68154a2012-11-21 15:18:06 +00001225 bool isClosed;
1226 SkPath::Direction direction, cheapDirection;
1227 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1228 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1229 REPORTER_ASSERT(reporter, isClosed == close);
1230 REPORTER_ASSERT(reporter, direction == cheapDirection);
1231 } else {
1232 SkRect computed;
1233 computed.set(123, 456, 789, 1011);
1234 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1235 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1236 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1237
1238 bool isClosed = (bool) -1;
1239 SkPath::Direction direction = (SkPath::Direction) -1;
1240 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1241 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1242 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001243 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001244
caryclark@google.comf1316942011-07-26 19:54:45 +00001245 if (tests[testIndex] == lastPass) {
1246 fail = true;
1247 }
1248 if (tests[testIndex] == lastClose) {
1249 close = false;
1250 }
1251 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001252
caryclark@google.comf1316942011-07-26 19:54:45 +00001253 // fail, close then line
1254 SkPath path1;
1255 path1.moveTo(r1[0].fX, r1[0].fY);
1256 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1257 path1.lineTo(r1[index].fX, r1[index].fY);
1258 }
1259 path1.close();
1260 path1.lineTo(1, 0);
1261 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001262
caryclark@google.comf1316942011-07-26 19:54:45 +00001263 // fail, move in the middle
1264 path1.reset();
1265 path1.moveTo(r1[0].fX, r1[0].fY);
1266 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1267 if (index == 2) {
1268 path1.moveTo(1, .5f);
1269 }
1270 path1.lineTo(r1[index].fX, r1[index].fY);
1271 }
1272 path1.close();
1273 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1274
1275 // fail, move on the edge
1276 path1.reset();
1277 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1278 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1279 path1.lineTo(r1[index].fX, r1[index].fY);
1280 }
1281 path1.close();
1282 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001283
caryclark@google.comf1316942011-07-26 19:54:45 +00001284 // fail, quad
1285 path1.reset();
1286 path1.moveTo(r1[0].fX, r1[0].fY);
1287 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1288 if (index == 2) {
1289 path1.quadTo(1, .5f, 1, .5f);
1290 }
1291 path1.lineTo(r1[index].fX, r1[index].fY);
1292 }
1293 path1.close();
1294 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001295
caryclark@google.comf1316942011-07-26 19:54:45 +00001296 // fail, cubic
1297 path1.reset();
1298 path1.moveTo(r1[0].fX, r1[0].fY);
1299 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1300 if (index == 2) {
1301 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1302 }
1303 path1.lineTo(r1[index].fX, r1[index].fY);
1304 }
1305 path1.close();
1306 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1307}
1308
caryclark@google.com56f233a2012-11-19 13:06:06 +00001309static void test_isNestedRects(skiatest::Reporter* reporter) {
1310 // passing tests (all moveTo / lineTo...
1311 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1312 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1313 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1314 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1315 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1316 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1317 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1318 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1319 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1320 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1321 {1, 0}, {.5f, 0}};
1322 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1323 {0, 1}, {0, .5f}};
1324 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1325 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1326 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1327
1328 // failing tests
1329 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1330 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1331 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1332 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1333 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1334 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1335 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1336 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1337
1338 // failing, no close
1339 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1340 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1341
1342 size_t testLen[] = {
1343 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1344 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1345 sizeof(rd), sizeof(re),
1346 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1347 sizeof(f7), sizeof(f8),
1348 sizeof(c1), sizeof(c2)
1349 };
1350 SkPoint* tests[] = {
1351 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1352 f1, f2, f3, f4, f5, f6, f7, f8,
1353 c1, c2
1354 };
1355 const SkPoint* lastPass = re;
1356 const SkPoint* lastClose = f8;
1357 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1358 size_t index;
1359 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1360 bool fail = false;
1361 bool close = true;
1362 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1363 SkPath path;
1364 if (rectFirst) {
1365 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1366 }
1367 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1368 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1369 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1370 }
1371 if (close) {
1372 path.close();
1373 }
1374 if (!rectFirst) {
1375 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1376 }
1377 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1378 if (!fail) {
1379 SkRect expected[2], computed[2];
1380 SkRect testBounds;
1381 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1382 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1383 expected[1] = testBounds;
1384 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1385 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1386 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1387 }
1388 if (tests[testIndex] == lastPass) {
1389 fail = true;
1390 }
1391 if (tests[testIndex] == lastClose) {
1392 close = false;
1393 }
1394 }
1395
1396 // fail, close then line
1397 SkPath path1;
1398 if (rectFirst) {
1399 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1400 }
1401 path1.moveTo(r1[0].fX, r1[0].fY);
1402 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1403 path1.lineTo(r1[index].fX, r1[index].fY);
1404 }
1405 path1.close();
1406 path1.lineTo(1, 0);
1407 if (!rectFirst) {
1408 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1409 }
1410 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1411
1412 // fail, move in the middle
1413 path1.reset();
1414 if (rectFirst) {
1415 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1416 }
1417 path1.moveTo(r1[0].fX, r1[0].fY);
1418 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1419 if (index == 2) {
1420 path1.moveTo(1, .5f);
1421 }
1422 path1.lineTo(r1[index].fX, r1[index].fY);
1423 }
1424 path1.close();
1425 if (!rectFirst) {
1426 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1427 }
1428 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1429
1430 // fail, move on the edge
1431 path1.reset();
1432 if (rectFirst) {
1433 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1434 }
1435 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1436 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1437 path1.lineTo(r1[index].fX, r1[index].fY);
1438 }
1439 path1.close();
1440 if (!rectFirst) {
1441 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1442 }
1443 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1444
1445 // fail, quad
1446 path1.reset();
1447 if (rectFirst) {
1448 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1449 }
1450 path1.moveTo(r1[0].fX, r1[0].fY);
1451 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1452 if (index == 2) {
1453 path1.quadTo(1, .5f, 1, .5f);
1454 }
1455 path1.lineTo(r1[index].fX, r1[index].fY);
1456 }
1457 path1.close();
1458 if (!rectFirst) {
1459 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1460 }
1461 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1462
1463 // fail, cubic
1464 path1.reset();
1465 if (rectFirst) {
1466 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1467 }
1468 path1.moveTo(r1[0].fX, r1[0].fY);
1469 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1470 if (index == 2) {
1471 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1472 }
1473 path1.lineTo(r1[index].fX, r1[index].fY);
1474 }
1475 path1.close();
1476 if (!rectFirst) {
1477 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1478 }
1479 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001480
caryclark@google.com56f233a2012-11-19 13:06:06 +00001481 // fail, not nested
1482 path1.reset();
1483 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1484 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1485 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1486 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001487
1488 // pass, stroke rect
1489 SkPath src, dst;
1490 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1491 SkPaint strokePaint;
1492 strokePaint.setStyle(SkPaint::kStroke_Style);
1493 strokePaint.setStrokeWidth(2);
1494 strokePaint.getFillPath(src, &dst);
1495 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001496}
1497
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001498static void write_and_read_back(skiatest::Reporter* reporter,
1499 const SkPath& p) {
1500 SkWriter32 writer(100);
1501 writer.writePath(p);
1502 size_t size = writer.size();
1503 SkAutoMalloc storage(size);
1504 writer.flatten(storage.get());
1505 SkReader32 reader(storage.get(), size);
1506
1507 SkPath readBack;
1508 REPORTER_ASSERT(reporter, readBack != p);
1509 reader.readPath(&readBack);
1510 REPORTER_ASSERT(reporter, readBack == p);
1511
rmistry@google.comd6176b02012-08-23 18:14:13 +00001512 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001513 p.getConvexityOrUnknown());
1514
1515 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1516
1517 const SkRect& origBounds = p.getBounds();
1518 const SkRect& readBackBounds = readBack.getBounds();
1519
1520 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1521}
1522
reed@google.com53effc52011-09-21 19:05:12 +00001523static void test_flattening(skiatest::Reporter* reporter) {
1524 SkPath p;
1525
1526 static const SkPoint pts[] = {
1527 { 0, 0 },
1528 { SkIntToScalar(10), SkIntToScalar(10) },
1529 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1530 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1531 };
1532 p.moveTo(pts[0]);
1533 p.lineTo(pts[1]);
1534 p.quadTo(pts[2], pts[3]);
1535 p.cubicTo(pts[4], pts[5], pts[6]);
1536
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001537 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001538
1539 // create a buffer that should be much larger than the path so we don't
1540 // kill our stack if writer goes too far.
1541 char buffer[1024];
1542 uint32_t size1 = p.writeToMemory(NULL);
1543 uint32_t size2 = p.writeToMemory(buffer);
1544 REPORTER_ASSERT(reporter, size1 == size2);
1545
1546 SkPath p2;
1547 uint32_t size3 = p2.readFromMemory(buffer);
1548 REPORTER_ASSERT(reporter, size1 == size3);
1549 REPORTER_ASSERT(reporter, p == p2);
1550
1551 char buffer2[1024];
1552 size3 = p2.writeToMemory(buffer2);
1553 REPORTER_ASSERT(reporter, size1 == size3);
1554 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001555
1556 // test persistence of the oval flag & convexity
1557 {
1558 SkPath oval;
1559 SkRect rect = SkRect::MakeWH(10, 10);
1560 oval.addOval(rect);
1561
1562 write_and_read_back(reporter, oval);
1563 }
reed@google.com53effc52011-09-21 19:05:12 +00001564}
1565
1566static void test_transform(skiatest::Reporter* reporter) {
1567 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001568
reed@google.com53effc52011-09-21 19:05:12 +00001569 static const SkPoint pts[] = {
1570 { 0, 0 },
1571 { SkIntToScalar(10), SkIntToScalar(10) },
1572 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1573 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1574 };
1575 p.moveTo(pts[0]);
1576 p.lineTo(pts[1]);
1577 p.quadTo(pts[2], pts[3]);
1578 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001579
reed@google.com53effc52011-09-21 19:05:12 +00001580 SkMatrix matrix;
1581 matrix.reset();
1582 p.transform(matrix, &p1);
1583 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001584
reed@google.com53effc52011-09-21 19:05:12 +00001585 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1586 p.transform(matrix, &p1);
1587 SkPoint pts1[7];
1588 int count = p1.getPoints(pts1, 7);
1589 REPORTER_ASSERT(reporter, 7 == count);
1590 for (int i = 0; i < count; ++i) {
1591 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1592 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1593 }
1594}
1595
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001596static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001597 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001598 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001599
caryclark@google.com56f233a2012-11-19 13:06:06 +00001600 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001601 const char* testPath;
1602 const size_t numResultPts;
1603 const SkRect resultBound;
1604 const SkPath::Verb* resultVerbs;
1605 const size_t numResultVerbs;
1606 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001607
schenney@chromium.org7e963602012-06-13 17:05:43 +00001608 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1609 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1610 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1611 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1612 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1613 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1614 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1615 static const SkPath::Verb resultVerbs8[] = {
1616 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1617 };
1618 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1619 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1620 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1621 static const SkPath::Verb resultVerbs12[] = {
1622 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1623 };
1624 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1625 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1626 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1627 static const SkPath::Verb resultVerbs16[] = {
1628 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1629 };
1630 static const struct zeroPathTestData gZeroLengthTests[] = {
1631 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001632 { "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 +00001633 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001634 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1635 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1636 { "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) },
1637 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1638 { "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) },
1639 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1640 { "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) },
1641 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1642 { "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) },
1643 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1644 { "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 +00001645 SK_ARRAY_COUNT(resultVerbs14)
1646 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001647 { "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) },
1648 { "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 +00001649 SK_ARRAY_COUNT(resultVerbs16)
1650 }
1651 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001652
schenney@chromium.org7e963602012-06-13 17:05:43 +00001653 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1654 p.reset();
1655 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1656 REPORTER_ASSERT(reporter, valid);
1657 REPORTER_ASSERT(reporter, !p.isEmpty());
1658 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1659 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1660 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1661 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1662 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1663 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001664 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001665}
1666
1667struct SegmentInfo {
1668 SkPath fPath;
1669 int fPointCount;
1670};
1671
reed@google.com10296cc2011-09-21 12:29:05 +00001672#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1673
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001674static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001675 SkPath p, p2;
1676
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001677 p.moveTo(0, 0);
1678 p.quadTo(100, 100, 200, 200);
1679 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1680 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001681 p2 = p;
1682 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001683 p.cubicTo(100, 100, 200, 200, 300, 300);
1684 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1685 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001686 p2 = p;
1687 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1688
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001689 p.reset();
1690 p.moveTo(0, 0);
1691 p.cubicTo(100, 100, 200, 200, 300, 300);
1692 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001693 p2 = p;
1694 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001695
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001696 REPORTER_ASSERT(reporter, !p.isEmpty());
1697}
1698
1699static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001700 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001701 SkPoint pts[4];
1702
1703 // Test an iterator with no path
1704 SkPath::Iter noPathIter;
1705 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001706
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001707 // Test that setting an empty path works
1708 noPathIter.setPath(p, false);
1709 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001710
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001711 // Test that close path makes no difference for an empty path
1712 noPathIter.setPath(p, true);
1713 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001714
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001715 // Test an iterator with an initial empty path
1716 SkPath::Iter iter(p, false);
1717 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1718
1719 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001720 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001721 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1722
rmistry@google.comd6176b02012-08-23 18:14:13 +00001723
schenney@chromium.org7e963602012-06-13 17:05:43 +00001724 struct iterTestData {
1725 const char* testPath;
1726 const bool forceClose;
1727 const bool consumeDegenerates;
1728 const size_t* numResultPtsPerVerb;
1729 const SkPoint* resultPts;
1730 const SkPath::Verb* resultVerbs;
1731 const size_t numResultVerbs;
1732 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001733
schenney@chromium.org7e963602012-06-13 17:05:43 +00001734 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1735 static const SkPath::Verb resultVerbs2[] = {
1736 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1737 };
1738 static const SkPath::Verb resultVerbs3[] = {
1739 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1740 };
1741 static const SkPath::Verb resultVerbs4[] = {
1742 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1743 };
1744 static const SkPath::Verb resultVerbs5[] = {
1745 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1746 };
1747 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001748 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1749 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1750 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1751 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001752 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001753 static const SkPoint resultPts2[] = {
1754 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1755 };
1756 static const SkPoint resultPts3[] = {
1757 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1758 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1759 };
1760 static const SkPoint resultPts4[] = {
1761 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1762 };
1763 static const SkPoint resultPts5[] = {
1764 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1765 };
1766 static const struct iterTestData gIterTests[] = {
1767 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001768 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1769 { "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 +00001770 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1771 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1772 { "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) },
1773 { "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 +00001774 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1775 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1776 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1777 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1778 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1779 { "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 +00001780 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001781
schenney@chromium.org7e963602012-06-13 17:05:43 +00001782 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1783 p.reset();
1784 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1785 REPORTER_ASSERT(reporter, valid);
1786 iter.setPath(p, gIterTests[i].forceClose);
1787 int j = 0, l = 0;
1788 do {
1789 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1790 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1791 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1792 }
1793 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1794 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1795 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001796
1797 // The GM degeneratesegments.cpp test is more extensive
1798}
1799
1800static void test_raw_iter(skiatest::Reporter* reporter) {
1801 SkPath p;
1802 SkPoint pts[4];
1803
1804 // Test an iterator with no path
1805 SkPath::RawIter noPathIter;
1806 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1807 // Test that setting an empty path works
1808 noPathIter.setPath(p);
1809 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001810
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001811 // Test an iterator with an initial empty path
1812 SkPath::RawIter iter(p);
1813 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1814
1815 // Test that a move-only path returns the move.
1816 p.moveTo(SK_Scalar1, 0);
1817 iter.setPath(p);
1818 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1819 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1820 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1821 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1822
1823 // No matter how many moves we add, we should get them all back
1824 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1825 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1826 iter.setPath(p);
1827 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1828 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1829 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1830 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1831 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1832 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1833 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1834 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1835 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1836 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1837
1838 // Initial close is never ever stored
1839 p.reset();
1840 p.close();
1841 iter.setPath(p);
1842 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1843
1844 // Move/close sequences
1845 p.reset();
1846 p.close(); // Not stored, no purpose
1847 p.moveTo(SK_Scalar1, 0);
1848 p.close();
1849 p.close(); // Not stored, no purpose
1850 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1851 p.close();
1852 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1853 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1854 p.close();
1855 iter.setPath(p);
1856 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1857 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1858 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1859 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1860 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1861 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1862 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1863 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1864 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1865 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1866 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1867 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1868 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1869 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1870 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1871 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1872 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1873 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1874 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1875 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1876 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1877 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1878
1879 // Generate random paths and verify
1880 SkPoint randomPts[25];
1881 for (int i = 0; i < 5; ++i) {
1882 for (int j = 0; j < 5; ++j) {
1883 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1884 }
1885 }
1886
1887 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001888 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001889 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001890 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001891 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001892
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001893 for (int i = 0; i < 500; ++i) {
1894 p.reset();
1895 bool lastWasClose = true;
1896 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001897 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001898 int numPoints = 0;
1899 int numVerbs = (rand.nextU() >> 16) % 10;
1900 int numIterVerbs = 0;
1901 for (int j = 0; j < numVerbs; ++j) {
1902 do {
1903 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1904 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001905 switch (nextVerb) {
1906 case SkPath::kMove_Verb:
1907 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1908 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001909 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001910 numPoints += 1;
1911 lastWasClose = false;
1912 haveMoveTo = true;
1913 break;
1914 case SkPath::kLine_Verb:
1915 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001916 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001917 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1918 haveMoveTo = true;
1919 }
1920 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1921 p.lineTo(expectedPts[numPoints]);
1922 numPoints += 1;
1923 lastWasClose = false;
1924 break;
1925 case SkPath::kQuad_Verb:
1926 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001927 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001928 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1929 haveMoveTo = true;
1930 }
1931 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1932 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1933 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1934 numPoints += 2;
1935 lastWasClose = false;
1936 break;
1937 case SkPath::kCubic_Verb:
1938 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001939 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001940 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1941 haveMoveTo = true;
1942 }
1943 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1944 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1945 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1946 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1947 expectedPts[numPoints + 2]);
1948 numPoints += 3;
1949 lastWasClose = false;
1950 break;
1951 case SkPath::kClose_Verb:
1952 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001953 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001954 lastWasClose = true;
1955 break;
1956 default:;
1957 }
1958 expectedVerbs[numIterVerbs++] = nextVerb;
1959 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001960
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001961 iter.setPath(p);
1962 numVerbs = numIterVerbs;
1963 numIterVerbs = 0;
1964 int numIterPts = 0;
1965 SkPoint lastMoveTo;
1966 SkPoint lastPt;
1967 lastMoveTo.set(0, 0);
1968 lastPt.set(0, 0);
1969 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1970 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1971 numIterVerbs++;
1972 switch (nextVerb) {
1973 case SkPath::kMove_Verb:
1974 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1975 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1976 lastPt = lastMoveTo = pts[0];
1977 numIterPts += 1;
1978 break;
1979 case SkPath::kLine_Verb:
1980 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1981 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1982 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1983 lastPt = pts[1];
1984 numIterPts += 1;
1985 break;
1986 case SkPath::kQuad_Verb:
1987 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1988 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1989 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1990 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1991 lastPt = pts[2];
1992 numIterPts += 2;
1993 break;
1994 case SkPath::kCubic_Verb:
1995 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1996 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1997 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1998 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1999 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2000 lastPt = pts[3];
2001 numIterPts += 3;
2002 break;
2003 case SkPath::kClose_Verb:
2004 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2005 lastPt = lastMoveTo;
2006 break;
2007 default:;
2008 }
2009 }
2010 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2011 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2012 }
2013}
2014
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002015static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002016 const SkPath& path,
2017 bool expectedCircle,
2018 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002019 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002020 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2021 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002022
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002023 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002024 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2025 }
2026}
2027
2028static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002029 const SkPath& path,
2030 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002031 SkPath tmp;
2032
2033 SkMatrix m;
2034 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2035 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002036 // this matrix reverses the direction.
2037 if (SkPath::kCCW_Direction == dir) {
2038 dir = SkPath::kCW_Direction;
2039 } else {
2040 SkASSERT(SkPath::kCW_Direction == dir);
2041 dir = SkPath::kCCW_Direction;
2042 }
2043 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002044}
2045
2046static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002047 const SkPath& path,
2048 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002049 SkPath tmp;
2050
2051 // translate at small offset
2052 SkMatrix m;
2053 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2054 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002055 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002056
2057 tmp.reset();
2058 m.reset();
2059
2060 // translate at a relatively big offset
2061 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2062 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002063 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002064}
2065
2066static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002067 const SkPath& path,
2068 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002069 for (int angle = 0; angle < 360; ++angle) {
2070 SkPath tmp;
2071 SkMatrix m;
2072 m.setRotate(SkIntToScalar(angle));
2073 path.transform(m, &tmp);
2074
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002075 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002076 // degrees is not an oval anymore, this can be improved. we made this
2077 // for the simplicity of our implementation.
2078 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002079 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002080 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002081 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002082 }
2083 }
2084}
2085
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002086static void test_circle_mirror_x(skiatest::Reporter* reporter,
2087 const SkPath& path,
2088 SkPath::Direction dir) {
2089 SkPath tmp;
2090 SkMatrix m;
2091 m.reset();
2092 m.setScaleX(-SK_Scalar1);
2093 path.transform(m, &tmp);
2094
2095 if (SkPath::kCW_Direction == dir) {
2096 dir = SkPath::kCCW_Direction;
2097 } else {
2098 SkASSERT(SkPath::kCCW_Direction == dir);
2099 dir = SkPath::kCW_Direction;
2100 }
2101
2102 check_for_circle(reporter, tmp, true, dir);
2103}
2104
2105static void test_circle_mirror_y(skiatest::Reporter* reporter,
2106 const SkPath& path,
2107 SkPath::Direction dir) {
2108 SkPath tmp;
2109 SkMatrix m;
2110 m.reset();
2111 m.setScaleY(-SK_Scalar1);
2112 path.transform(m, &tmp);
2113
2114 if (SkPath::kCW_Direction == dir) {
2115 dir = SkPath::kCCW_Direction;
2116 } else {
2117 SkASSERT(SkPath::kCCW_Direction == dir);
2118 dir = SkPath::kCW_Direction;
2119 }
2120
2121 check_for_circle(reporter, tmp, true, dir);
2122}
2123
2124static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2125 const SkPath& path,
2126 SkPath::Direction dir) {
2127 SkPath tmp;
2128 SkMatrix m;
2129 m.reset();
2130 m.setScaleX(-SK_Scalar1);
2131 m.setScaleY(-SK_Scalar1);
2132 path.transform(m, &tmp);
2133
2134 check_for_circle(reporter, tmp, true, dir);
2135}
2136
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002137static void test_circle_with_direction(skiatest::Reporter* reporter,
2138 SkPath::Direction dir) {
2139 SkPath path;
2140
2141 // circle at origin
2142 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002143 check_for_circle(reporter, path, true, dir);
2144 test_circle_rotate(reporter, path, dir);
2145 test_circle_translate(reporter, path, dir);
2146 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002147
2148 // circle at an offset at (10, 10)
2149 path.reset();
2150 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2151 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002152 check_for_circle(reporter, path, true, dir);
2153 test_circle_rotate(reporter, path, dir);
2154 test_circle_translate(reporter, path, dir);
2155 test_circle_skew(reporter, path, dir);
2156 test_circle_mirror_x(reporter, path, dir);
2157 test_circle_mirror_y(reporter, path, dir);
2158 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002159}
2160
2161static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2162 SkPath path;
2163 SkPath circle;
2164 SkPath rect;
2165 SkPath empty;
2166
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002167 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2168 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2169
2170 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002171 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2172 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2173
2174 SkMatrix translate;
2175 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2176
2177 // For simplicity, all the path concatenation related operations
2178 // would mark it non-circle, though in theory it's still a circle.
2179
2180 // empty + circle (translate)
2181 path = empty;
2182 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002183 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002184
2185 // circle + empty (translate)
2186 path = circle;
2187 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002188 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002189
2190 // test reverseAddPath
2191 path = circle;
2192 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002193 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002194}
2195
2196static void test_circle(skiatest::Reporter* reporter) {
2197 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2198 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2199
2200 // multiple addCircle()
2201 SkPath path;
2202 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2203 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002204 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002205
2206 // some extra lineTo() would make isOval() fail
2207 path.reset();
2208 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2209 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002210 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002211
2212 // not back to the original point
2213 path.reset();
2214 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2215 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002216 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002217
2218 test_circle_with_add_paths(reporter);
2219}
2220
2221static void test_oval(skiatest::Reporter* reporter) {
2222 SkRect rect;
2223 SkMatrix m;
2224 SkPath path;
2225
2226 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2227 path.addOval(rect);
2228
2229 REPORTER_ASSERT(reporter, path.isOval(NULL));
2230
2231 m.setRotate(SkIntToScalar(90));
2232 SkPath tmp;
2233 path.transform(m, &tmp);
2234 // an oval rotated 90 degrees is still an oval.
2235 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2236
2237 m.reset();
2238 m.setRotate(SkIntToScalar(30));
2239 tmp.reset();
2240 path.transform(m, &tmp);
2241 // an oval rotated 30 degrees is not an oval anymore.
2242 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2243
2244 // since empty path being transformed.
2245 path.reset();
2246 tmp.reset();
2247 m.reset();
2248 path.transform(m, &tmp);
2249 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2250
2251 // empty path is not an oval
2252 tmp.reset();
2253 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2254
2255 // only has moveTo()s
2256 tmp.reset();
2257 tmp.moveTo(0, 0);
2258 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2259 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2260
2261 // mimic WebKit's calling convention,
2262 // call moveTo() first and then call addOval()
2263 path.reset();
2264 path.moveTo(0, 0);
2265 path.addOval(rect);
2266 REPORTER_ASSERT(reporter, path.isOval(NULL));
2267
2268 // copy path
2269 path.reset();
2270 tmp.reset();
2271 tmp.addOval(rect);
2272 path = tmp;
2273 REPORTER_ASSERT(reporter, path.isOval(NULL));
2274}
2275
caryclark@google.com42639cd2012-06-06 12:03:39 +00002276static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002277 SkTSize<SkScalar>::Make(3,4);
2278
reed@android.com3abec1d2009-03-02 05:36:20 +00002279 SkPath p, p2;
2280 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002281
reed@android.com3abec1d2009-03-02 05:36:20 +00002282 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002283 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002284 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002285 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002286 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002287 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2288 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2289 REPORTER_ASSERT(reporter, p == p2);
2290 REPORTER_ASSERT(reporter, !(p != p2));
2291
reed@android.comd252db02009-04-01 18:31:44 +00002292 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002293
reed@android.com3abec1d2009-03-02 05:36:20 +00002294 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002295
reed@android.com6b82d1a2009-06-03 02:35:01 +00002296 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2297 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002298 // we have quads or cubics
2299 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002300 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002301
reed@android.com6b82d1a2009-06-03 02:35:01 +00002302 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002303 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002304 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002305
reed@android.com6b82d1a2009-06-03 02:35:01 +00002306 p.addOval(bounds);
2307 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002308 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002309
reed@android.com6b82d1a2009-06-03 02:35:01 +00002310 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002311 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002312 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002313 // we have only lines
2314 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002315 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002316
2317 REPORTER_ASSERT(reporter, p != p2);
2318 REPORTER_ASSERT(reporter, !(p == p2));
2319
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002320 // do getPoints and getVerbs return the right result
2321 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2322 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002323 SkPoint pts[4];
2324 int count = p.getPoints(pts, 4);
2325 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002326 uint8_t verbs[6];
2327 verbs[5] = 0xff;
2328 p.getVerbs(verbs, 5);
2329 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2330 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2331 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2332 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2333 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2334 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002335 bounds2.set(pts, 4);
2336 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002337
reed@android.com3abec1d2009-03-02 05:36:20 +00002338 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2339 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002340 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002341
reed@android.com3abec1d2009-03-02 05:36:20 +00002342 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002343 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002344 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2345 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002346
reed@android.com3abec1d2009-03-02 05:36:20 +00002347 // now force p to not be a rect
2348 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2349 p.addRect(bounds);
2350 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002351
reed@google.com7e6c4d12012-05-10 14:05:43 +00002352 test_isLine(reporter);
2353 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002354 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002355 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002356 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002357 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002358 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002359 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002360 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002361 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002362 test_flattening(reporter);
2363 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002364 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002365 test_iter(reporter);
2366 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002367 test_circle(reporter);
2368 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002369 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002370 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002371 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002372 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002373 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002374 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002375 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002376 test_tricky_cubic();
2377 test_clipped_cubic();
2378 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002379 test_bad_cubic_crbug229478();
reed@android.com3abec1d2009-03-02 05:36:20 +00002380}
2381
2382#include "TestClassDef.h"
2383DEFINE_TESTCLASS("Path", PathTestClass, TestPath)