blob: be118467b01c056aa0b978fccc4ec9f3a209c9ea [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;
368 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000369
reed@google.com0bb18bb2012-07-26 15:20:36 +0000370 SkRect r;
371 r.setEmpty();
372 REPORTER_ASSERT(reporter, r.isFinite());
373 r.set(0, 0, inf, -inf);
374 REPORTER_ASSERT(reporter, !r.isFinite());
375 r.set(0, 0, nan, 0);
376 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000377
reed@google.com0bb18bb2012-07-26 15:20:36 +0000378 SkPoint pts[] = {
379 { 0, 0 },
380 { SK_Scalar1, 0 },
381 { 0, SK_Scalar1 },
382 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000383
reed@google.com0bb18bb2012-07-26 15:20:36 +0000384 bool isFine = r.setBoundsCheck(pts, 3);
385 REPORTER_ASSERT(reporter, isFine);
386 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000387
reed@google.com0bb18bb2012-07-26 15:20:36 +0000388 pts[1].set(inf, 0);
389 isFine = r.setBoundsCheck(pts, 3);
390 REPORTER_ASSERT(reporter, !isFine);
391 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000392
reed@google.com0bb18bb2012-07-26 15:20:36 +0000393 pts[1].set(nan, 0);
394 isFine = r.setBoundsCheck(pts, 3);
395 REPORTER_ASSERT(reporter, !isFine);
396 REPORTER_ASSERT(reporter, r.isEmpty());
397}
398
399static void test_path_isfinite(skiatest::Reporter* reporter) {
400 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000401 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000402 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000403
reed@google.com0bb18bb2012-07-26 15:20:36 +0000404 SkPath path;
405 REPORTER_ASSERT(reporter, path.isFinite());
406
407 path.reset();
408 REPORTER_ASSERT(reporter, path.isFinite());
409
410 path.reset();
411 path.moveTo(SK_Scalar1, 0);
412 REPORTER_ASSERT(reporter, path.isFinite());
413
414 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000415 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000416 REPORTER_ASSERT(reporter, !path.isFinite());
417
418 path.reset();
419 path.moveTo(nan, 0);
420 REPORTER_ASSERT(reporter, !path.isFinite());
421}
422
423static void test_isfinite(skiatest::Reporter* reporter) {
424 test_rect_isfinite(reporter);
425 test_path_isfinite(reporter);
426}
427
reed@google.com744faba2012-05-29 19:54:52 +0000428// assert that we always
429// start with a moveTo
430// only have 1 moveTo
431// only have Lines after that
432// end with a single close
433// only have (at most) 1 close
434//
435static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000436 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000437 SkPath::RawIter iter(path);
438 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000439
440 bool firstTime = true;
441 bool foundClose = false;
442 for (;;) {
443 switch (iter.next(pts)) {
444 case SkPath::kMove_Verb:
445 REPORTER_ASSERT(reporter, firstTime);
446 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
447 srcPts++;
448 firstTime = false;
449 break;
450 case SkPath::kLine_Verb:
451 REPORTER_ASSERT(reporter, !firstTime);
452 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
453 srcPts++;
454 break;
455 case SkPath::kQuad_Verb:
456 REPORTER_ASSERT(reporter, !"unexpected quad verb");
457 break;
458 case SkPath::kCubic_Verb:
459 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
460 break;
461 case SkPath::kClose_Verb:
462 REPORTER_ASSERT(reporter, !firstTime);
463 REPORTER_ASSERT(reporter, !foundClose);
464 REPORTER_ASSERT(reporter, expectClose);
465 foundClose = true;
466 break;
467 case SkPath::kDone_Verb:
468 goto DONE;
469 }
470 }
471DONE:
472 REPORTER_ASSERT(reporter, foundClose == expectClose);
473}
474
475static void test_addPoly(skiatest::Reporter* reporter) {
476 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000477 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000478
reed@google.com744faba2012-05-29 19:54:52 +0000479 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
480 pts[i].fX = rand.nextSScalar1();
481 pts[i].fY = rand.nextSScalar1();
482 }
483
484 for (int doClose = 0; doClose <= 1; ++doClose) {
485 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
486 SkPath path;
487 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000488 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000489 }
490 }
491}
492
reed@google.com8b06f1a2012-05-29 12:03:46 +0000493static void test_strokerec(skiatest::Reporter* reporter) {
494 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
495 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000496
reed@google.com8b06f1a2012-05-29 12:03:46 +0000497 rec.setHairlineStyle();
498 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000499
reed@google.com8b06f1a2012-05-29 12:03:46 +0000500 rec.setStrokeStyle(SK_Scalar1, false);
501 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000502
reed@google.com8b06f1a2012-05-29 12:03:46 +0000503 rec.setStrokeStyle(SK_Scalar1, true);
504 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000505
reed@google.com8b06f1a2012-05-29 12:03:46 +0000506 rec.setStrokeStyle(0, false);
507 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000508
reed@google.com8b06f1a2012-05-29 12:03:46 +0000509 rec.setStrokeStyle(0, true);
510 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
511}
512
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000513// Set this for paths that don't have a consistent direction such as a bowtie.
514// (cheapComputeDirection is not expected to catch these.)
515static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
516
517static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
518 SkPath::Direction expected) {
519 if (expected == kDontCheckDir) {
520 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000521 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000522 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
523
524 SkPath::Direction dir;
525 if (copy.cheapComputeDirection(&dir)) {
526 REPORTER_ASSERT(reporter, dir == expected);
527 } else {
528 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
529 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000530}
531
reed@google.com3e71a882012-01-10 18:44:37 +0000532static void test_direction(skiatest::Reporter* reporter) {
533 size_t i;
534 SkPath path;
535 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
536 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
537 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000538 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000539
540 static const char* gDegen[] = {
541 "M 10 10",
542 "M 10 10 M 20 20",
543 "M 10 10 L 20 20",
544 "M 10 10 L 10 10 L 10 10",
545 "M 10 10 Q 10 10 10 10",
546 "M 10 10 C 10 10 10 10 10 10",
547 };
548 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
549 path.reset();
550 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
551 REPORTER_ASSERT(reporter, valid);
552 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
553 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000554
reed@google.com3e71a882012-01-10 18:44:37 +0000555 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000556 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000557 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000558 "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 +0000559 // rect with top two corners replaced by cubics with identical middle
560 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000561 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
562 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000563 };
564 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
565 path.reset();
566 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
567 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000568 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000569 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000570
reed@google.com3e71a882012-01-10 18:44:37 +0000571 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000572 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000573 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000574 "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 +0000575 // rect with top two corners replaced by cubics with identical middle
576 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000577 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
578 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000579 };
580 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
581 path.reset();
582 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
583 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000584 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000585 }
reed@google.comac8543f2012-01-30 20:51:25 +0000586
587 // Test two donuts, each wound a different direction. Only the outer contour
588 // determines the cheap direction
589 path.reset();
590 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
591 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000592 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000593
reed@google.comac8543f2012-01-30 20:51:25 +0000594 path.reset();
595 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
596 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000597 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000598
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000599#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000600 // triangle with one point really far from the origin.
601 path.reset();
602 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000603 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
604 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
605 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000606 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000607#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000608}
609
reed@google.comffdb0182011-11-14 19:29:14 +0000610static void add_rect(SkPath* path, const SkRect& r) {
611 path->moveTo(r.fLeft, r.fTop);
612 path->lineTo(r.fRight, r.fTop);
613 path->lineTo(r.fRight, r.fBottom);
614 path->lineTo(r.fLeft, r.fBottom);
615 path->close();
616}
617
618static void test_bounds(skiatest::Reporter* reporter) {
619 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000620 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
621 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
622 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
623 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000624 };
625
626 SkPath path0, path1;
627 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
628 path0.addRect(rects[i]);
629 add_rect(&path1, rects[i]);
630 }
631
632 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
633}
634
reed@google.com55b5f4b2011-09-07 12:23:41 +0000635static void stroke_cubic(const SkPoint pts[4]) {
636 SkPath path;
637 path.moveTo(pts[0]);
638 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000639
reed@google.com55b5f4b2011-09-07 12:23:41 +0000640 SkPaint paint;
641 paint.setStyle(SkPaint::kStroke_Style);
642 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000643
reed@google.com55b5f4b2011-09-07 12:23:41 +0000644 SkPath fill;
645 paint.getFillPath(path, &fill);
646}
647
648// just ensure this can run w/o any SkASSERTS firing in the debug build
649// we used to assert due to differences in how we determine a degenerate vector
650// but that was fixed with the introduction of SkPoint::CanNormalize
651static void stroke_tiny_cubic() {
652 SkPoint p0[] = {
653 { 372.0f, 92.0f },
654 { 372.0f, 92.0f },
655 { 372.0f, 92.0f },
656 { 372.0f, 92.0f },
657 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000658
reed@google.com55b5f4b2011-09-07 12:23:41 +0000659 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000660
reed@google.com55b5f4b2011-09-07 12:23:41 +0000661 SkPoint p1[] = {
662 { 372.0f, 92.0f },
663 { 372.0007f, 92.000755f },
664 { 371.99927f, 92.003922f },
665 { 371.99826f, 92.003899f },
666 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000667
reed@google.com55b5f4b2011-09-07 12:23:41 +0000668 stroke_cubic(p1);
669}
670
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000671static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
672 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000673 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000674 SkPoint mv;
675 SkPoint pts[4];
676 SkPath::Verb v;
677 int nMT = 0;
678 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000679 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000680 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
681 switch (v) {
682 case SkPath::kMove_Verb:
683 mv = pts[0];
684 ++nMT;
685 break;
686 case SkPath::kClose_Verb:
687 REPORTER_ASSERT(reporter, mv == pts[0]);
688 ++nCL;
689 break;
690 default:
691 break;
692 }
693 }
694 // if we force a close on the interator we should have a close
695 // for every moveTo
696 REPORTER_ASSERT(reporter, !i || nMT == nCL);
697 }
698}
699
700static void test_close(skiatest::Reporter* reporter) {
701 SkPath closePt;
702 closePt.moveTo(0, 0);
703 closePt.close();
704 check_close(reporter, closePt);
705
706 SkPath openPt;
707 openPt.moveTo(0, 0);
708 check_close(reporter, openPt);
709
710 SkPath empty;
711 check_close(reporter, empty);
712 empty.close();
713 check_close(reporter, empty);
714
715 SkPath rect;
716 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
717 check_close(reporter, rect);
718 rect.close();
719 check_close(reporter, rect);
720
721 SkPath quad;
722 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
723 check_close(reporter, quad);
724 quad.close();
725 check_close(reporter, quad);
726
727 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000728 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000729 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
730 check_close(reporter, cubic);
731 cubic.close();
732 check_close(reporter, cubic);
733
734 SkPath line;
735 line.moveTo(SK_Scalar1, SK_Scalar1);
736 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
737 check_close(reporter, line);
738 line.close();
739 check_close(reporter, line);
740
741 SkPath rect2;
742 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
743 rect2.close();
744 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
745 check_close(reporter, rect2);
746 rect2.close();
747 check_close(reporter, rect2);
748
749 SkPath oval3;
750 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
751 oval3.close();
752 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
753 check_close(reporter, oval3);
754 oval3.close();
755 check_close(reporter, oval3);
756
757 SkPath moves;
758 moves.moveTo(SK_Scalar1, SK_Scalar1);
759 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
760 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
761 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
762 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000763
764 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000765}
766
reed@google.com7c424812011-05-15 04:38:34 +0000767static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
768 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000769 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
770 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000771 REPORTER_ASSERT(reporter, c == expected);
772}
773
774static void test_convexity2(skiatest::Reporter* reporter) {
775 SkPath pt;
776 pt.moveTo(0, 0);
777 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000778 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000779 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000780
reed@google.com7c424812011-05-15 04:38:34 +0000781 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000782 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
783 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000784 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000785 check_convexity(reporter, line, SkPath::kConvex_Convexity);
786 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000787
reed@google.com7c424812011-05-15 04:38:34 +0000788 SkPath triLeft;
789 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000790 triLeft.lineTo(SK_Scalar1, 0);
791 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000792 triLeft.close();
793 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000794 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000795
reed@google.com7c424812011-05-15 04:38:34 +0000796 SkPath triRight;
797 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000798 triRight.lineTo(-SK_Scalar1, 0);
799 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000800 triRight.close();
801 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000802 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000803
reed@google.com7c424812011-05-15 04:38:34 +0000804 SkPath square;
805 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000806 square.lineTo(SK_Scalar1, 0);
807 square.lineTo(SK_Scalar1, SK_Scalar1);
808 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000809 square.close();
810 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000811 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000812
reed@google.com7c424812011-05-15 04:38:34 +0000813 SkPath redundantSquare;
814 redundantSquare.moveTo(0, 0);
815 redundantSquare.lineTo(0, 0);
816 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000817 redundantSquare.lineTo(SK_Scalar1, 0);
818 redundantSquare.lineTo(SK_Scalar1, 0);
819 redundantSquare.lineTo(SK_Scalar1, 0);
820 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
821 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
822 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
823 redundantSquare.lineTo(0, SK_Scalar1);
824 redundantSquare.lineTo(0, SK_Scalar1);
825 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000826 redundantSquare.close();
827 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000828 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000829
reed@google.com7c424812011-05-15 04:38:34 +0000830 SkPath bowTie;
831 bowTie.moveTo(0, 0);
832 bowTie.lineTo(0, 0);
833 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000834 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
835 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
836 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
837 bowTie.lineTo(SK_Scalar1, 0);
838 bowTie.lineTo(SK_Scalar1, 0);
839 bowTie.lineTo(SK_Scalar1, 0);
840 bowTie.lineTo(0, SK_Scalar1);
841 bowTie.lineTo(0, SK_Scalar1);
842 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000843 bowTie.close();
844 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000845 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000846
reed@google.com7c424812011-05-15 04:38:34 +0000847 SkPath spiral;
848 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000849 spiral.lineTo(100*SK_Scalar1, 0);
850 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
851 spiral.lineTo(0, 100*SK_Scalar1);
852 spiral.lineTo(0, 50*SK_Scalar1);
853 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
854 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000855 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000856 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000857 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000858
reed@google.com7c424812011-05-15 04:38:34 +0000859 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000860 dent.moveTo(0, 0);
861 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
862 dent.lineTo(0, 100*SK_Scalar1);
863 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
864 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000865 dent.close();
866 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000867 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000868}
869
reed@android.com6b82d1a2009-06-03 02:35:01 +0000870static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
871 const SkRect& bounds) {
872 REPORTER_ASSERT(reporter, p.isConvex());
873 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000874
reed@android.com6b82d1a2009-06-03 02:35:01 +0000875 SkPath p2(p);
876 REPORTER_ASSERT(reporter, p2.isConvex());
877 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
878
879 SkPath other;
880 other.swap(p2);
881 REPORTER_ASSERT(reporter, other.isConvex());
882 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
883}
884
reed@google.com04863fa2011-05-15 04:08:24 +0000885static void setFromString(SkPath* path, const char str[]) {
886 bool first = true;
887 while (str) {
888 SkScalar x, y;
889 str = SkParse::FindScalar(str, &x);
890 if (NULL == str) {
891 break;
892 }
893 str = SkParse::FindScalar(str, &y);
894 SkASSERT(str);
895 if (first) {
896 path->moveTo(x, y);
897 first = false;
898 } else {
899 path->lineTo(x, y);
900 }
901 }
902}
903
904static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000905 SkPath path;
906
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000907 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000908 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000909 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000910 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000911 check_convexity(reporter, path, SkPath::kConcave_Convexity);
912
reed@google.com04863fa2011-05-15 04:08:24 +0000913 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000914 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000915 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000916 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000917
reed@google.com04863fa2011-05-15 04:08:24 +0000918 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000919 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000920 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000921 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000922
reed@google.com04863fa2011-05-15 04:08:24 +0000923 static const struct {
924 const char* fPathStr;
925 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000926 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000927 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000928 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
929 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
930 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
931 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
932 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
933 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
934 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
935 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000936 };
937
938 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
939 SkPath path;
940 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000941 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
942 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000943 }
944}
945
reed@google.com7e6c4d12012-05-10 14:05:43 +0000946static void test_isLine(skiatest::Reporter* reporter) {
947 SkPath path;
948 SkPoint pts[2];
949 const SkScalar value = SkIntToScalar(5);
950
951 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000952
reed@google.com7e6c4d12012-05-10 14:05:43 +0000953 // set some non-zero values
954 pts[0].set(value, value);
955 pts[1].set(value, value);
956 REPORTER_ASSERT(reporter, !path.isLine(pts));
957 // check that pts was untouched
958 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
959 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
960
961 const SkScalar moveX = SkIntToScalar(1);
962 const SkScalar moveY = SkIntToScalar(2);
963 SkASSERT(value != moveX && value != moveY);
964
965 path.moveTo(moveX, moveY);
966 REPORTER_ASSERT(reporter, !path.isLine(NULL));
967 REPORTER_ASSERT(reporter, !path.isLine(pts));
968 // check that pts was untouched
969 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
970 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
971
972 const SkScalar lineX = SkIntToScalar(2);
973 const SkScalar lineY = SkIntToScalar(2);
974 SkASSERT(value != lineX && value != lineY);
975
976 path.lineTo(lineX, lineY);
977 REPORTER_ASSERT(reporter, path.isLine(NULL));
978
979 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
980 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
981 REPORTER_ASSERT(reporter, path.isLine(pts));
982 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
983 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
984
985 path.lineTo(0, 0); // too many points/verbs
986 REPORTER_ASSERT(reporter, !path.isLine(NULL));
987 REPORTER_ASSERT(reporter, !path.isLine(pts));
988 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
989 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
990}
991
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000992static void test_conservativelyContains(skiatest::Reporter* reporter) {
993 SkPath path;
994
995 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
996 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
997
998 // A circle that bounds kBaseRect (with a significant amount of slop)
999 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1000 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1001 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1002
1003 // round-rect radii
1004 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001005
caryclark@google.com56f233a2012-11-19 13:06:06 +00001006 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001007 SkRect fQueryRect;
1008 bool fInRect;
1009 bool fInCircle;
1010 bool fInRR;
1011 } kQueries[] = {
1012 {kBaseRect, true, true, false},
1013
1014 // rect well inside of kBaseRect
1015 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1016 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1017 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1018 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1019 true, true, true},
1020
1021 // rects with edges off by one from kBaseRect's edges
1022 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1023 kBaseRect.width(), kBaseRect.height() + 1),
1024 false, true, false},
1025 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1026 kBaseRect.width() + 1, kBaseRect.height()),
1027 false, true, false},
1028 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1029 kBaseRect.width() + 1, kBaseRect.height() + 1),
1030 false, true, false},
1031 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1032 kBaseRect.width(), kBaseRect.height()),
1033 false, true, false},
1034 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1035 kBaseRect.width(), kBaseRect.height()),
1036 false, true, false},
1037 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1038 kBaseRect.width() + 2, kBaseRect.height()),
1039 false, true, false},
1040 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1041 kBaseRect.width() + 2, kBaseRect.height()),
1042 false, true, false},
1043
1044 // zero-w/h rects at each corner of kBaseRect
1045 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1046 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1047 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1048 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1049
1050 // far away rect
1051 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1052 SkIntToScalar(10), SkIntToScalar(10)),
1053 false, false, false},
1054
1055 // very large rect containing kBaseRect
1056 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1057 kBaseRect.fTop - 5 * kBaseRect.height(),
1058 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1059 false, false, false},
1060
1061 // skinny rect that spans same y-range as kBaseRect
1062 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1063 SkIntToScalar(1), kBaseRect.height()),
1064 true, true, true},
1065
1066 // short rect that spans same x-range as kBaseRect
1067 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1068 true, true, true},
1069
1070 // skinny rect that spans slightly larger y-range than kBaseRect
1071 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1072 SkIntToScalar(1), kBaseRect.height() + 1),
1073 false, true, false},
1074
1075 // short rect that spans slightly larger x-range than kBaseRect
1076 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1077 kBaseRect.width() + 1, SkScalar(1)),
1078 false, true, false},
1079 };
1080
1081 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001082 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001083 SkRect qRect = kQueries[q].fQueryRect;
1084 if (inv & 0x1) {
1085 SkTSwap(qRect.fLeft, qRect.fRight);
1086 }
1087 if (inv & 0x2) {
1088 SkTSwap(qRect.fTop, qRect.fBottom);
1089 }
1090 for (int d = 0; d < 2; ++d) {
1091 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1092 path.reset();
1093 path.addRect(kBaseRect, dir);
1094 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1095 path.conservativelyContainsRect(qRect));
1096
1097 path.reset();
1098 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1099 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1100 path.conservativelyContainsRect(qRect));
1101
1102 path.reset();
1103 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1104 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1105 path.conservativelyContainsRect(qRect));
1106 }
1107 // Slightly non-convex shape, shouldn't contain any rects.
1108 path.reset();
1109 path.moveTo(0, 0);
1110 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1111 path.lineTo(SkIntToScalar(100), 0);
1112 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1113 path.lineTo(0, SkIntToScalar(100));
1114 path.close();
1115 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1116 }
1117 }
1118
1119 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1120 path.reset();
1121 path.moveTo(0, 0);
1122 path.lineTo(SkIntToScalar(100), 0);
1123 path.lineTo(0, SkIntToScalar(100));
1124
1125 // inside, on along top edge
1126 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1127 SkIntToScalar(10),
1128 SkIntToScalar(10))));
1129 // above
1130 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1131 SkRect::MakeXYWH(SkIntToScalar(50),
1132 SkIntToScalar(-10),
1133 SkIntToScalar(10),
1134 SkIntToScalar(10))));
1135 // to the left
1136 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1137 SkIntToScalar(5),
1138 SkIntToScalar(5),
1139 SkIntToScalar(5))));
1140
1141 // outside the diagonal edge
1142 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1143 SkIntToScalar(200),
1144 SkIntToScalar(20),
1145 SkIntToScalar(5))));
1146}
1147
caryclark@google.comf1316942011-07-26 19:54:45 +00001148// Simple isRect test is inline TestPath, below.
1149// test_isRect provides more extensive testing.
1150static void test_isRect(skiatest::Reporter* reporter) {
1151 // passing tests (all moveTo / lineTo...
1152 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1153 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1154 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1155 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1156 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1157 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1158 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1159 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1160 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1161 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1162 {1, 0}, {.5f, 0}};
1163 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1164 {0, 1}, {0, .5f}};
1165 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1166 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1167 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001168 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001169
caryclark@google.comf1316942011-07-26 19:54:45 +00001170 // failing tests
1171 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1172 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1173 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1174 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1175 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1176 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1177 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1178 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001179 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1180 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1181 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001182
caryclark@google.comf1316942011-07-26 19:54:45 +00001183 // failing, no close
1184 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1185 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1186
1187 size_t testLen[] = {
1188 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1189 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001190 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001191 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001192 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001193 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001194 };
1195 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001196 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1197 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001198 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001199 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001200 SkPoint* lastPass = rf;
1201 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001202 bool fail = false;
1203 bool close = true;
1204 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1205 size_t index;
1206 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1207 SkPath path;
1208 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1209 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1210 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1211 }
1212 if (close) {
1213 path.close();
1214 }
1215 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001216 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1217
caryclark@google.com56f233a2012-11-19 13:06:06 +00001218 if (!fail) {
1219 SkRect computed, expected;
1220 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1221 REPORTER_ASSERT(reporter, path.isRect(&computed));
1222 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001223
caryclark@google.comf68154a2012-11-21 15:18:06 +00001224 bool isClosed;
1225 SkPath::Direction direction, cheapDirection;
1226 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1227 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1228 REPORTER_ASSERT(reporter, isClosed == close);
1229 REPORTER_ASSERT(reporter, direction == cheapDirection);
1230 } else {
1231 SkRect computed;
1232 computed.set(123, 456, 789, 1011);
1233 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1234 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1235 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1236
1237 bool isClosed = (bool) -1;
1238 SkPath::Direction direction = (SkPath::Direction) -1;
1239 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1240 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1241 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001242 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001243
caryclark@google.comf1316942011-07-26 19:54:45 +00001244 if (tests[testIndex] == lastPass) {
1245 fail = true;
1246 }
1247 if (tests[testIndex] == lastClose) {
1248 close = false;
1249 }
1250 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001251
caryclark@google.comf1316942011-07-26 19:54:45 +00001252 // fail, close then line
1253 SkPath path1;
1254 path1.moveTo(r1[0].fX, r1[0].fY);
1255 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1256 path1.lineTo(r1[index].fX, r1[index].fY);
1257 }
1258 path1.close();
1259 path1.lineTo(1, 0);
1260 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001261
caryclark@google.comf1316942011-07-26 19:54:45 +00001262 // fail, move in the middle
1263 path1.reset();
1264 path1.moveTo(r1[0].fX, r1[0].fY);
1265 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1266 if (index == 2) {
1267 path1.moveTo(1, .5f);
1268 }
1269 path1.lineTo(r1[index].fX, r1[index].fY);
1270 }
1271 path1.close();
1272 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1273
1274 // fail, move on the edge
1275 path1.reset();
1276 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1277 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1278 path1.lineTo(r1[index].fX, r1[index].fY);
1279 }
1280 path1.close();
1281 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001282
caryclark@google.comf1316942011-07-26 19:54:45 +00001283 // fail, quad
1284 path1.reset();
1285 path1.moveTo(r1[0].fX, r1[0].fY);
1286 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1287 if (index == 2) {
1288 path1.quadTo(1, .5f, 1, .5f);
1289 }
1290 path1.lineTo(r1[index].fX, r1[index].fY);
1291 }
1292 path1.close();
1293 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001294
caryclark@google.comf1316942011-07-26 19:54:45 +00001295 // fail, cubic
1296 path1.reset();
1297 path1.moveTo(r1[0].fX, r1[0].fY);
1298 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1299 if (index == 2) {
1300 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1301 }
1302 path1.lineTo(r1[index].fX, r1[index].fY);
1303 }
1304 path1.close();
1305 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1306}
1307
caryclark@google.com56f233a2012-11-19 13:06:06 +00001308static void test_isNestedRects(skiatest::Reporter* reporter) {
1309 // passing tests (all moveTo / lineTo...
1310 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1311 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1312 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1313 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1314 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1315 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1316 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1317 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1318 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1319 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1320 {1, 0}, {.5f, 0}};
1321 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1322 {0, 1}, {0, .5f}};
1323 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1324 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1325 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1326
1327 // failing tests
1328 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1329 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1330 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1331 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1332 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1333 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1334 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1335 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1336
1337 // failing, no close
1338 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1339 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1340
1341 size_t testLen[] = {
1342 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1343 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1344 sizeof(rd), sizeof(re),
1345 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1346 sizeof(f7), sizeof(f8),
1347 sizeof(c1), sizeof(c2)
1348 };
1349 SkPoint* tests[] = {
1350 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1351 f1, f2, f3, f4, f5, f6, f7, f8,
1352 c1, c2
1353 };
1354 const SkPoint* lastPass = re;
1355 const SkPoint* lastClose = f8;
1356 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1357 size_t index;
1358 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1359 bool fail = false;
1360 bool close = true;
1361 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1362 SkPath path;
1363 if (rectFirst) {
1364 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1365 }
1366 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1367 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1368 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1369 }
1370 if (close) {
1371 path.close();
1372 }
1373 if (!rectFirst) {
1374 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1375 }
1376 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1377 if (!fail) {
1378 SkRect expected[2], computed[2];
1379 SkRect testBounds;
1380 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1381 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1382 expected[1] = testBounds;
1383 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1384 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1385 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1386 }
1387 if (tests[testIndex] == lastPass) {
1388 fail = true;
1389 }
1390 if (tests[testIndex] == lastClose) {
1391 close = false;
1392 }
1393 }
1394
1395 // fail, close then line
1396 SkPath path1;
1397 if (rectFirst) {
1398 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1399 }
1400 path1.moveTo(r1[0].fX, r1[0].fY);
1401 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1402 path1.lineTo(r1[index].fX, r1[index].fY);
1403 }
1404 path1.close();
1405 path1.lineTo(1, 0);
1406 if (!rectFirst) {
1407 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1408 }
1409 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1410
1411 // fail, move in the middle
1412 path1.reset();
1413 if (rectFirst) {
1414 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1415 }
1416 path1.moveTo(r1[0].fX, r1[0].fY);
1417 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1418 if (index == 2) {
1419 path1.moveTo(1, .5f);
1420 }
1421 path1.lineTo(r1[index].fX, r1[index].fY);
1422 }
1423 path1.close();
1424 if (!rectFirst) {
1425 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1426 }
1427 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1428
1429 // fail, move on the edge
1430 path1.reset();
1431 if (rectFirst) {
1432 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1433 }
1434 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1435 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1436 path1.lineTo(r1[index].fX, r1[index].fY);
1437 }
1438 path1.close();
1439 if (!rectFirst) {
1440 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1441 }
1442 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1443
1444 // fail, quad
1445 path1.reset();
1446 if (rectFirst) {
1447 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1448 }
1449 path1.moveTo(r1[0].fX, r1[0].fY);
1450 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1451 if (index == 2) {
1452 path1.quadTo(1, .5f, 1, .5f);
1453 }
1454 path1.lineTo(r1[index].fX, r1[index].fY);
1455 }
1456 path1.close();
1457 if (!rectFirst) {
1458 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1459 }
1460 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1461
1462 // fail, cubic
1463 path1.reset();
1464 if (rectFirst) {
1465 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1466 }
1467 path1.moveTo(r1[0].fX, r1[0].fY);
1468 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1469 if (index == 2) {
1470 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1471 }
1472 path1.lineTo(r1[index].fX, r1[index].fY);
1473 }
1474 path1.close();
1475 if (!rectFirst) {
1476 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1477 }
1478 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001479
caryclark@google.com56f233a2012-11-19 13:06:06 +00001480 // fail, not nested
1481 path1.reset();
1482 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1483 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1484 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1485 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001486
1487 // pass, stroke rect
1488 SkPath src, dst;
1489 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1490 SkPaint strokePaint;
1491 strokePaint.setStyle(SkPaint::kStroke_Style);
1492 strokePaint.setStrokeWidth(2);
1493 strokePaint.getFillPath(src, &dst);
1494 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001495}
1496
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001497static void write_and_read_back(skiatest::Reporter* reporter,
1498 const SkPath& p) {
1499 SkWriter32 writer(100);
1500 writer.writePath(p);
1501 size_t size = writer.size();
1502 SkAutoMalloc storage(size);
1503 writer.flatten(storage.get());
1504 SkReader32 reader(storage.get(), size);
1505
1506 SkPath readBack;
1507 REPORTER_ASSERT(reporter, readBack != p);
1508 reader.readPath(&readBack);
1509 REPORTER_ASSERT(reporter, readBack == p);
1510
rmistry@google.comd6176b02012-08-23 18:14:13 +00001511 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001512 p.getConvexityOrUnknown());
1513
1514 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1515
1516 const SkRect& origBounds = p.getBounds();
1517 const SkRect& readBackBounds = readBack.getBounds();
1518
1519 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1520}
1521
reed@google.com53effc52011-09-21 19:05:12 +00001522static void test_flattening(skiatest::Reporter* reporter) {
1523 SkPath p;
1524
1525 static const SkPoint pts[] = {
1526 { 0, 0 },
1527 { SkIntToScalar(10), SkIntToScalar(10) },
1528 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1529 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1530 };
1531 p.moveTo(pts[0]);
1532 p.lineTo(pts[1]);
1533 p.quadTo(pts[2], pts[3]);
1534 p.cubicTo(pts[4], pts[5], pts[6]);
1535
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001536 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001537
1538 // create a buffer that should be much larger than the path so we don't
1539 // kill our stack if writer goes too far.
1540 char buffer[1024];
1541 uint32_t size1 = p.writeToMemory(NULL);
1542 uint32_t size2 = p.writeToMemory(buffer);
1543 REPORTER_ASSERT(reporter, size1 == size2);
1544
1545 SkPath p2;
1546 uint32_t size3 = p2.readFromMemory(buffer);
1547 REPORTER_ASSERT(reporter, size1 == size3);
1548 REPORTER_ASSERT(reporter, p == p2);
1549
1550 char buffer2[1024];
1551 size3 = p2.writeToMemory(buffer2);
1552 REPORTER_ASSERT(reporter, size1 == size3);
1553 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001554
1555 // test persistence of the oval flag & convexity
1556 {
1557 SkPath oval;
1558 SkRect rect = SkRect::MakeWH(10, 10);
1559 oval.addOval(rect);
1560
1561 write_and_read_back(reporter, oval);
1562 }
reed@google.com53effc52011-09-21 19:05:12 +00001563}
1564
1565static void test_transform(skiatest::Reporter* reporter) {
1566 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001567
reed@google.com53effc52011-09-21 19:05:12 +00001568 static const SkPoint pts[] = {
1569 { 0, 0 },
1570 { SkIntToScalar(10), SkIntToScalar(10) },
1571 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1572 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1573 };
1574 p.moveTo(pts[0]);
1575 p.lineTo(pts[1]);
1576 p.quadTo(pts[2], pts[3]);
1577 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001578
reed@google.com53effc52011-09-21 19:05:12 +00001579 SkMatrix matrix;
1580 matrix.reset();
1581 p.transform(matrix, &p1);
1582 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001583
reed@google.com53effc52011-09-21 19:05:12 +00001584 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1585 p.transform(matrix, &p1);
1586 SkPoint pts1[7];
1587 int count = p1.getPoints(pts1, 7);
1588 REPORTER_ASSERT(reporter, 7 == count);
1589 for (int i = 0; i < count; ++i) {
1590 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1591 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1592 }
1593}
1594
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001595static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001596 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001597 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001598
caryclark@google.com56f233a2012-11-19 13:06:06 +00001599 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001600 const char* testPath;
1601 const size_t numResultPts;
1602 const SkRect resultBound;
1603 const SkPath::Verb* resultVerbs;
1604 const size_t numResultVerbs;
1605 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001606
schenney@chromium.org7e963602012-06-13 17:05:43 +00001607 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1608 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1609 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1610 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1611 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1612 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1613 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1614 static const SkPath::Verb resultVerbs8[] = {
1615 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1616 };
1617 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1618 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1619 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1620 static const SkPath::Verb resultVerbs12[] = {
1621 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1622 };
1623 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1624 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1625 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1626 static const SkPath::Verb resultVerbs16[] = {
1627 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1628 };
1629 static const struct zeroPathTestData gZeroLengthTests[] = {
1630 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001631 { "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 +00001632 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001633 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1634 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1635 { "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) },
1636 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1637 { "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) },
1638 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1639 { "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) },
1640 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1641 { "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) },
1642 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1643 { "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 +00001644 SK_ARRAY_COUNT(resultVerbs14)
1645 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001646 { "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) },
1647 { "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 +00001648 SK_ARRAY_COUNT(resultVerbs16)
1649 }
1650 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001651
schenney@chromium.org7e963602012-06-13 17:05:43 +00001652 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1653 p.reset();
1654 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1655 REPORTER_ASSERT(reporter, valid);
1656 REPORTER_ASSERT(reporter, !p.isEmpty());
1657 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1658 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1659 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1660 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1661 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1662 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001663 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001664}
1665
1666struct SegmentInfo {
1667 SkPath fPath;
1668 int fPointCount;
1669};
1670
reed@google.com10296cc2011-09-21 12:29:05 +00001671#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1672
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001673static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001674 SkPath p, p2;
1675
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001676 p.moveTo(0, 0);
1677 p.quadTo(100, 100, 200, 200);
1678 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1679 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001680 p2 = p;
1681 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001682 p.cubicTo(100, 100, 200, 200, 300, 300);
1683 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1684 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001685 p2 = p;
1686 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1687
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001688 p.reset();
1689 p.moveTo(0, 0);
1690 p.cubicTo(100, 100, 200, 200, 300, 300);
1691 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001692 p2 = p;
1693 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001694
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001695 REPORTER_ASSERT(reporter, !p.isEmpty());
1696}
1697
1698static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001699 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001700 SkPoint pts[4];
1701
1702 // Test an iterator with no path
1703 SkPath::Iter noPathIter;
1704 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001705
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001706 // Test that setting an empty path works
1707 noPathIter.setPath(p, false);
1708 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001709
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001710 // Test that close path makes no difference for an empty path
1711 noPathIter.setPath(p, true);
1712 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001713
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001714 // Test an iterator with an initial empty path
1715 SkPath::Iter iter(p, false);
1716 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1717
1718 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001719 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001720 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1721
rmistry@google.comd6176b02012-08-23 18:14:13 +00001722
schenney@chromium.org7e963602012-06-13 17:05:43 +00001723 struct iterTestData {
1724 const char* testPath;
1725 const bool forceClose;
1726 const bool consumeDegenerates;
1727 const size_t* numResultPtsPerVerb;
1728 const SkPoint* resultPts;
1729 const SkPath::Verb* resultVerbs;
1730 const size_t numResultVerbs;
1731 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001732
schenney@chromium.org7e963602012-06-13 17:05:43 +00001733 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1734 static const SkPath::Verb resultVerbs2[] = {
1735 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1736 };
1737 static const SkPath::Verb resultVerbs3[] = {
1738 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1739 };
1740 static const SkPath::Verb resultVerbs4[] = {
1741 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1742 };
1743 static const SkPath::Verb resultVerbs5[] = {
1744 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1745 };
1746 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001747 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1748 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1749 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1750 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001751 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001752 static const SkPoint resultPts2[] = {
1753 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1754 };
1755 static const SkPoint resultPts3[] = {
1756 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1757 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1758 };
1759 static const SkPoint resultPts4[] = {
1760 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1761 };
1762 static const SkPoint resultPts5[] = {
1763 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1764 };
1765 static const struct iterTestData gIterTests[] = {
1766 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001767 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1768 { "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 +00001769 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1770 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1771 { "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) },
1772 { "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 +00001773 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1774 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1775 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1776 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1777 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1778 { "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 +00001779 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001780
schenney@chromium.org7e963602012-06-13 17:05:43 +00001781 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1782 p.reset();
1783 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1784 REPORTER_ASSERT(reporter, valid);
1785 iter.setPath(p, gIterTests[i].forceClose);
1786 int j = 0, l = 0;
1787 do {
1788 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1789 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1790 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1791 }
1792 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1793 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1794 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001795
1796 // The GM degeneratesegments.cpp test is more extensive
1797}
1798
1799static void test_raw_iter(skiatest::Reporter* reporter) {
1800 SkPath p;
1801 SkPoint pts[4];
1802
1803 // Test an iterator with no path
1804 SkPath::RawIter noPathIter;
1805 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1806 // Test that setting an empty path works
1807 noPathIter.setPath(p);
1808 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001809
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001810 // Test an iterator with an initial empty path
1811 SkPath::RawIter iter(p);
1812 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1813
1814 // Test that a move-only path returns the move.
1815 p.moveTo(SK_Scalar1, 0);
1816 iter.setPath(p);
1817 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1818 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1819 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1820 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1821
1822 // No matter how many moves we add, we should get them all back
1823 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1824 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1825 iter.setPath(p);
1826 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1827 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1828 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1829 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1830 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1831 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1832 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1833 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1834 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1835 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1836
1837 // Initial close is never ever stored
1838 p.reset();
1839 p.close();
1840 iter.setPath(p);
1841 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1842
1843 // Move/close sequences
1844 p.reset();
1845 p.close(); // Not stored, no purpose
1846 p.moveTo(SK_Scalar1, 0);
1847 p.close();
1848 p.close(); // Not stored, no purpose
1849 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1850 p.close();
1851 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1852 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1853 p.close();
1854 iter.setPath(p);
1855 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1856 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1857 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1859 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1860 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1861 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1862 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1863 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1864 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1865 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1866 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1867 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1868 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1869 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1870 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1871 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1872 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1873 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1874 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1875 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1876 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1877
1878 // Generate random paths and verify
1879 SkPoint randomPts[25];
1880 for (int i = 0; i < 5; ++i) {
1881 for (int j = 0; j < 5; ++j) {
1882 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1883 }
1884 }
1885
1886 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001887 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001888 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001889 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001890 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001891
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001892 for (int i = 0; i < 500; ++i) {
1893 p.reset();
1894 bool lastWasClose = true;
1895 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001896 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001897 int numPoints = 0;
1898 int numVerbs = (rand.nextU() >> 16) % 10;
1899 int numIterVerbs = 0;
1900 for (int j = 0; j < numVerbs; ++j) {
1901 do {
1902 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1903 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001904 switch (nextVerb) {
1905 case SkPath::kMove_Verb:
1906 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1907 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001908 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001909 numPoints += 1;
1910 lastWasClose = false;
1911 haveMoveTo = true;
1912 break;
1913 case SkPath::kLine_Verb:
1914 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001915 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001916 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1917 haveMoveTo = true;
1918 }
1919 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1920 p.lineTo(expectedPts[numPoints]);
1921 numPoints += 1;
1922 lastWasClose = false;
1923 break;
1924 case SkPath::kQuad_Verb:
1925 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001926 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001927 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1928 haveMoveTo = true;
1929 }
1930 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1931 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1932 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1933 numPoints += 2;
1934 lastWasClose = false;
1935 break;
1936 case SkPath::kCubic_Verb:
1937 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001938 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001939 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1940 haveMoveTo = true;
1941 }
1942 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1943 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1944 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1945 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1946 expectedPts[numPoints + 2]);
1947 numPoints += 3;
1948 lastWasClose = false;
1949 break;
1950 case SkPath::kClose_Verb:
1951 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001952 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001953 lastWasClose = true;
1954 break;
1955 default:;
1956 }
1957 expectedVerbs[numIterVerbs++] = nextVerb;
1958 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001959
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001960 iter.setPath(p);
1961 numVerbs = numIterVerbs;
1962 numIterVerbs = 0;
1963 int numIterPts = 0;
1964 SkPoint lastMoveTo;
1965 SkPoint lastPt;
1966 lastMoveTo.set(0, 0);
1967 lastPt.set(0, 0);
1968 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1969 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1970 numIterVerbs++;
1971 switch (nextVerb) {
1972 case SkPath::kMove_Verb:
1973 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1974 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1975 lastPt = lastMoveTo = pts[0];
1976 numIterPts += 1;
1977 break;
1978 case SkPath::kLine_Verb:
1979 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1980 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1981 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1982 lastPt = pts[1];
1983 numIterPts += 1;
1984 break;
1985 case SkPath::kQuad_Verb:
1986 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1987 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1988 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1989 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1990 lastPt = pts[2];
1991 numIterPts += 2;
1992 break;
1993 case SkPath::kCubic_Verb:
1994 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1995 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1996 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1997 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1998 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1999 lastPt = pts[3];
2000 numIterPts += 3;
2001 break;
2002 case SkPath::kClose_Verb:
2003 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2004 lastPt = lastMoveTo;
2005 break;
2006 default:;
2007 }
2008 }
2009 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2010 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2011 }
2012}
2013
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002014static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002015 const SkPath& path,
2016 bool expectedCircle,
2017 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002018 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002019 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2020 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002021
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002022 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002023 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2024 }
2025}
2026
2027static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002028 const SkPath& path,
2029 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002030 SkPath tmp;
2031
2032 SkMatrix m;
2033 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2034 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002035 // this matrix reverses the direction.
2036 if (SkPath::kCCW_Direction == dir) {
2037 dir = SkPath::kCW_Direction;
2038 } else {
2039 SkASSERT(SkPath::kCW_Direction == dir);
2040 dir = SkPath::kCCW_Direction;
2041 }
2042 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002043}
2044
2045static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002046 const SkPath& path,
2047 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002048 SkPath tmp;
2049
2050 // translate at small offset
2051 SkMatrix m;
2052 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2053 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002054 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002055
2056 tmp.reset();
2057 m.reset();
2058
2059 // translate at a relatively big offset
2060 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2061 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002062 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002063}
2064
2065static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002066 const SkPath& path,
2067 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002068 for (int angle = 0; angle < 360; ++angle) {
2069 SkPath tmp;
2070 SkMatrix m;
2071 m.setRotate(SkIntToScalar(angle));
2072 path.transform(m, &tmp);
2073
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002074 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002075 // degrees is not an oval anymore, this can be improved. we made this
2076 // for the simplicity of our implementation.
2077 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002078 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002079 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002080 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002081 }
2082 }
2083}
2084
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002085static void test_circle_mirror_x(skiatest::Reporter* reporter,
2086 const SkPath& path,
2087 SkPath::Direction dir) {
2088 SkPath tmp;
2089 SkMatrix m;
2090 m.reset();
2091 m.setScaleX(-SK_Scalar1);
2092 path.transform(m, &tmp);
2093
2094 if (SkPath::kCW_Direction == dir) {
2095 dir = SkPath::kCCW_Direction;
2096 } else {
2097 SkASSERT(SkPath::kCCW_Direction == dir);
2098 dir = SkPath::kCW_Direction;
2099 }
2100
2101 check_for_circle(reporter, tmp, true, dir);
2102}
2103
2104static void test_circle_mirror_y(skiatest::Reporter* reporter,
2105 const SkPath& path,
2106 SkPath::Direction dir) {
2107 SkPath tmp;
2108 SkMatrix m;
2109 m.reset();
2110 m.setScaleY(-SK_Scalar1);
2111 path.transform(m, &tmp);
2112
2113 if (SkPath::kCW_Direction == dir) {
2114 dir = SkPath::kCCW_Direction;
2115 } else {
2116 SkASSERT(SkPath::kCCW_Direction == dir);
2117 dir = SkPath::kCW_Direction;
2118 }
2119
2120 check_for_circle(reporter, tmp, true, dir);
2121}
2122
2123static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2124 const SkPath& path,
2125 SkPath::Direction dir) {
2126 SkPath tmp;
2127 SkMatrix m;
2128 m.reset();
2129 m.setScaleX(-SK_Scalar1);
2130 m.setScaleY(-SK_Scalar1);
2131 path.transform(m, &tmp);
2132
2133 check_for_circle(reporter, tmp, true, dir);
2134}
2135
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002136static void test_circle_with_direction(skiatest::Reporter* reporter,
2137 SkPath::Direction dir) {
2138 SkPath path;
2139
2140 // circle at origin
2141 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002142 check_for_circle(reporter, path, true, dir);
2143 test_circle_rotate(reporter, path, dir);
2144 test_circle_translate(reporter, path, dir);
2145 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002146
2147 // circle at an offset at (10, 10)
2148 path.reset();
2149 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2150 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002151 check_for_circle(reporter, path, true, dir);
2152 test_circle_rotate(reporter, path, dir);
2153 test_circle_translate(reporter, path, dir);
2154 test_circle_skew(reporter, path, dir);
2155 test_circle_mirror_x(reporter, path, dir);
2156 test_circle_mirror_y(reporter, path, dir);
2157 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002158}
2159
2160static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2161 SkPath path;
2162 SkPath circle;
2163 SkPath rect;
2164 SkPath empty;
2165
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002166 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2167 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2168
2169 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002170 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2171 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2172
2173 SkMatrix translate;
2174 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2175
2176 // For simplicity, all the path concatenation related operations
2177 // would mark it non-circle, though in theory it's still a circle.
2178
2179 // empty + circle (translate)
2180 path = empty;
2181 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002182 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002183
2184 // circle + empty (translate)
2185 path = circle;
2186 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002187 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002188
2189 // test reverseAddPath
2190 path = circle;
2191 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002192 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002193}
2194
2195static void test_circle(skiatest::Reporter* reporter) {
2196 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2197 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2198
2199 // multiple addCircle()
2200 SkPath path;
2201 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2202 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002203 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002204
2205 // some extra lineTo() would make isOval() fail
2206 path.reset();
2207 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2208 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002209 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002210
2211 // not back to the original point
2212 path.reset();
2213 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2214 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002215 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002216
2217 test_circle_with_add_paths(reporter);
2218}
2219
2220static void test_oval(skiatest::Reporter* reporter) {
2221 SkRect rect;
2222 SkMatrix m;
2223 SkPath path;
2224
2225 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2226 path.addOval(rect);
2227
2228 REPORTER_ASSERT(reporter, path.isOval(NULL));
2229
2230 m.setRotate(SkIntToScalar(90));
2231 SkPath tmp;
2232 path.transform(m, &tmp);
2233 // an oval rotated 90 degrees is still an oval.
2234 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2235
2236 m.reset();
2237 m.setRotate(SkIntToScalar(30));
2238 tmp.reset();
2239 path.transform(m, &tmp);
2240 // an oval rotated 30 degrees is not an oval anymore.
2241 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2242
2243 // since empty path being transformed.
2244 path.reset();
2245 tmp.reset();
2246 m.reset();
2247 path.transform(m, &tmp);
2248 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2249
2250 // empty path is not an oval
2251 tmp.reset();
2252 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2253
2254 // only has moveTo()s
2255 tmp.reset();
2256 tmp.moveTo(0, 0);
2257 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2258 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2259
2260 // mimic WebKit's calling convention,
2261 // call moveTo() first and then call addOval()
2262 path.reset();
2263 path.moveTo(0, 0);
2264 path.addOval(rect);
2265 REPORTER_ASSERT(reporter, path.isOval(NULL));
2266
2267 // copy path
2268 path.reset();
2269 tmp.reset();
2270 tmp.addOval(rect);
2271 path = tmp;
2272 REPORTER_ASSERT(reporter, path.isOval(NULL));
2273}
2274
caryclark@google.com42639cd2012-06-06 12:03:39 +00002275static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002276 SkTSize<SkScalar>::Make(3,4);
2277
reed@android.com3abec1d2009-03-02 05:36:20 +00002278 SkPath p, p2;
2279 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002280
reed@android.com3abec1d2009-03-02 05:36:20 +00002281 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002282 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002283 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002284 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002285 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002286 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2287 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2288 REPORTER_ASSERT(reporter, p == p2);
2289 REPORTER_ASSERT(reporter, !(p != p2));
2290
reed@android.comd252db02009-04-01 18:31:44 +00002291 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002292
reed@android.com3abec1d2009-03-02 05:36:20 +00002293 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002294
reed@android.com6b82d1a2009-06-03 02:35:01 +00002295 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2296 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002297 // we have quads or cubics
2298 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002299 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002300
reed@android.com6b82d1a2009-06-03 02:35:01 +00002301 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002302 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002303 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002304
reed@android.com6b82d1a2009-06-03 02:35:01 +00002305 p.addOval(bounds);
2306 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002307 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002308
reed@android.com6b82d1a2009-06-03 02:35:01 +00002309 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002310 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002311 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002312 // we have only lines
2313 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002314 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002315
2316 REPORTER_ASSERT(reporter, p != p2);
2317 REPORTER_ASSERT(reporter, !(p == p2));
2318
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002319 // do getPoints and getVerbs return the right result
2320 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2321 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002322 SkPoint pts[4];
2323 int count = p.getPoints(pts, 4);
2324 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002325 uint8_t verbs[6];
2326 verbs[5] = 0xff;
2327 p.getVerbs(verbs, 5);
2328 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2329 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2330 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2331 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2332 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2333 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002334 bounds2.set(pts, 4);
2335 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002336
reed@android.com3abec1d2009-03-02 05:36:20 +00002337 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2338 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002339 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002340
reed@android.com3abec1d2009-03-02 05:36:20 +00002341 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002342 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002343 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2344 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002345
reed@android.com3abec1d2009-03-02 05:36:20 +00002346 // now force p to not be a rect
2347 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2348 p.addRect(bounds);
2349 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002350
reed@google.com7e6c4d12012-05-10 14:05:43 +00002351 test_isLine(reporter);
2352 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002353 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002354 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002355 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002356 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002357 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002358 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002359 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002360 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002361 test_flattening(reporter);
2362 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002363 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002364 test_iter(reporter);
2365 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002366 test_circle(reporter);
2367 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002368 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002369 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002370 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002371 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002372 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002373 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002374 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002375 test_tricky_cubic();
2376 test_clipped_cubic();
2377 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002378 test_bad_cubic_crbug229478();
reed@android.com3abec1d2009-03-02 05:36:20 +00002379}
2380
2381#include "TestClassDef.h"
2382DEFINE_TESTCLASS("Path", PathTestClass, TestPath)