blob: d8da95b23d11a36f9c778ce65039f0708d065208 [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.com64d62952013-01-18 17:49:28 +000034static void build_path_170666(SkPath& path) {
35 path.moveTo(17.9459f, 21.6344f);
36 path.lineTo(139.545f, -47.8105f);
37 path.lineTo(139.545f, -47.8105f);
38 path.lineTo(131.07f, -47.3888f);
39 path.lineTo(131.07f, -47.3888f);
40 path.lineTo(122.586f, -46.9532f);
41 path.lineTo(122.586f, -46.9532f);
42 path.lineTo(18076.6f, 31390.9f);
43 path.lineTo(18076.6f, 31390.9f);
44 path.lineTo(18085.1f, 31390.5f);
45 path.lineTo(18085.1f, 31390.5f);
46 path.lineTo(18076.6f, 31390.9f);
47 path.lineTo(18076.6f, 31390.9f);
48 path.lineTo(17955, 31460.3f);
49 path.lineTo(17955, 31460.3f);
50 path.lineTo(17963.5f, 31459.9f);
51 path.lineTo(17963.5f, 31459.9f);
52 path.lineTo(17971.9f, 31459.5f);
53 path.lineTo(17971.9f, 31459.5f);
54 path.lineTo(17.9551f, 21.6205f);
55 path.lineTo(17.9551f, 21.6205f);
56 path.lineTo(9.47091f, 22.0561f);
57 path.lineTo(9.47091f, 22.0561f);
58 path.lineTo(17.9459f, 21.6344f);
59 path.lineTo(17.9459f, 21.6344f);
60 path.close();path.moveTo(0.995934f, 22.4779f);
61 path.lineTo(0.986725f, 22.4918f);
62 path.lineTo(0.986725f, 22.4918f);
63 path.lineTo(17955, 31460.4f);
64 path.lineTo(17955, 31460.4f);
65 path.lineTo(17971.9f, 31459.5f);
66 path.lineTo(17971.9f, 31459.5f);
67 path.lineTo(18093.6f, 31390.1f);
68 path.lineTo(18093.6f, 31390.1f);
69 path.lineTo(18093.6f, 31390);
70 path.lineTo(18093.6f, 31390);
71 path.lineTo(139.555f, -47.8244f);
72 path.lineTo(139.555f, -47.8244f);
73 path.lineTo(122.595f, -46.9671f);
74 path.lineTo(122.595f, -46.9671f);
75 path.lineTo(0.995934f, 22.4779f);
76 path.lineTo(0.995934f, 22.4779f);
77 path.close();
78 path.moveTo(5.43941f, 25.5223f);
79 path.lineTo(798267, -28871.1f);
80 path.lineTo(798267, -28871.1f);
81 path.lineTo(3.12512e+06f, -113102);
82 path.lineTo(3.12512e+06f, -113102);
83 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
84 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
85 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
86 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
87 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
88 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
89 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
90 path.lineTo(2.78271e+08f, -1.00733e+07f);
91 path.lineTo(2.78271e+08f, -1.00733e+07f);
92 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
93 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
94 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
95 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
96 path.lineTo(2.77473e+08f, -1.00444e+07f);
97 path.lineTo(2.77473e+08f, -1.00444e+07f);
98 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
99 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
100 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
101 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
102 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
103 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
104 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
105 path.lineTo(798284, -28872);
106 path.lineTo(798284, -28872);
107 path.lineTo(22.4044f, 24.6677f);
108 path.lineTo(22.4044f, 24.6677f);
109 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
110 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
111 path.close();
112}
113
114static void build_path_simple_170666(SkPath& path) {
115 path.moveTo(126.677f, 24.1591f);
116 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
117}
118
119// This used to assert in the SK_DEBUG build, as the clip step would fail with
120// too-few interations in our cubic-line intersection code. That code now runs
121// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000122static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000123 SkPath path;
124 SkPaint paint;
125 paint.setAntiAlias(true);
126
127 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000128
reed@google.com64d62952013-01-18 17:49:28 +0000129 build_path_simple_170666(path);
130 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000131
reed@google.com64d62952013-01-18 17:49:28 +0000132 build_path_170666(path);
133 surface->getCanvas()->drawPath(path, paint);
134}
135
reed@google.coma8790de2012-10-24 21:04:04 +0000136// Make sure we stay non-finite once we get there (unless we reset or rewind).
137static void test_addrect_isfinite(skiatest::Reporter* reporter) {
138 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000139
reed@google.coma8790de2012-10-24 21:04:04 +0000140 path.addRect(SkRect::MakeWH(50, 100));
141 REPORTER_ASSERT(reporter, path.isFinite());
142
143 path.moveTo(0, 0);
144 path.lineTo(SK_ScalarInfinity, 42);
145 REPORTER_ASSERT(reporter, !path.isFinite());
146
147 path.addRect(SkRect::MakeWH(50, 100));
148 REPORTER_ASSERT(reporter, !path.isFinite());
149
150 path.reset();
151 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000152
reed@google.coma8790de2012-10-24 21:04:04 +0000153 path.addRect(SkRect::MakeWH(50, 100));
154 REPORTER_ASSERT(reporter, path.isFinite());
155}
156
reed@google.com848148e2013-01-15 15:51:59 +0000157static void build_big_path(SkPath* path, bool reducedCase) {
158 if (reducedCase) {
159 path->moveTo(577330, 1971.72f);
160 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
161 } else {
162 path->moveTo(60.1631f, 7.70567f);
163 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
164 path->lineTo(577379, 1977.77f);
165 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
166 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
167 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
168 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
169 path->quadTo(577198, 1972, 577238, 1971.31f);
170 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
171 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
172 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
173 path->moveTo(306.718f, -32.912f);
174 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
175 }
176}
177
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000178static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000179 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
180
181 // This path used to assert, because our cubic-chopping code incorrectly
182 // moved control points after the chop. This test should be run in SK_DEBUG
183 // mode to ensure that we no long assert.
184 SkPath path;
185 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
186 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000187
reed@google.com848148e2013-01-15 15:51:59 +0000188 SkPaint paint;
189 for (int doAA = 0; doAA <= 1; ++doAA) {
190 paint.setAntiAlias(SkToBool(doAA));
191 surface->getCanvas()->drawPath(path, paint);
192 }
193 }
194}
195
reed@google.com8cae8352012-09-14 15:18:41 +0000196// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
197// which triggered an assert, from a tricky cubic. This test replicates that
198// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
199// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000200static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000201 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000202 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
203 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
204 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
205 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000206 };
207
208 SkPath path;
209 path.moveTo(pts[0]);
210 path.cubicTo(pts[1], pts[2], pts[3]);
211
212 SkPaint paint;
213 paint.setAntiAlias(true);
214
215 SkSurface* surface = new_surface(19, 130);
216 surface->getCanvas()->drawPath(path, paint);
217 surface->unref();
218}
reed@android.com3abec1d2009-03-02 05:36:20 +0000219
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000220// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
221//
222static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
223 SkPath path;
224 path.quadTo(157, 366, 286, 208);
225 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000226
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000227 SkMatrix matrix;
228 matrix.setScale(1000*1000, 1000*1000);
229
230 // Be sure that path::transform correctly updates isFinite and the bounds
231 // if the transformation overflows. The previous bug was that isFinite was
232 // set to true in this case, but the bounds were not set to empty (which
233 // they should be).
234 while (path.isFinite()) {
235 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
236 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
237 path.transform(matrix);
238 }
239 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
240
241 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
242 path.transform(matrix);
243 // we need to still be non-finite
244 REPORTER_ASSERT(reporter, !path.isFinite());
245 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
246}
247
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000248static void add_corner_arc(SkPath* path, const SkRect& rect,
249 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000250 int startAngle)
251{
252
253 SkScalar rx = SkMinScalar(rect.width(), xIn);
254 SkScalar ry = SkMinScalar(rect.height(), yIn);
255
256 SkRect arcRect;
257 arcRect.set(-rx, -ry, rx, ry);
258 switch (startAngle) {
259 case 0:
260 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
261 break;
262 case 90:
263 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
264 break;
265 case 180:
266 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
267 break;
268 case 270:
269 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
270 break;
271 default:
272 break;
273 }
274
275 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
276}
277
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000278static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000279 SkScalar xCorner, SkScalar yCorner) {
280 // we are lazy here and use the same x & y for each corner
281 add_corner_arc(path, r, xCorner, yCorner, 270);
282 add_corner_arc(path, r, xCorner, yCorner, 0);
283 add_corner_arc(path, r, xCorner, yCorner, 90);
284 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000285 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000286}
287
288// Chrome creates its own round rects with each corner possibly being different.
289// Performance will suffer if they are not convex.
290// Note: PathBench::ArbRoundRectBench performs almost exactly
291// the same test (but with drawing)
292static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000293 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000294 SkRect r;
295
296 for (int i = 0; i < 5000; ++i) {
297
robertphillips@google.com158618e2012-10-23 16:56:56 +0000298 SkScalar size = rand.nextUScalar1() * 30;
299 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000300 continue;
301 }
302 r.fLeft = rand.nextUScalar1() * 300;
303 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000304 r.fRight = r.fLeft + 2 * size;
305 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000306
307 SkPath temp;
308
309 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
310
311 REPORTER_ASSERT(reporter, temp.isConvex());
312 }
313}
314
robertphillips@google.com158618e2012-10-23 16:56:56 +0000315// Chrome will sometimes create a 0 radius round rect. The degenerate
316// quads prevent the path from being converted to a rect
317// Note: PathBench::ArbRoundRectBench performs almost exactly
318// the same test (but with drawing)
319static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000320 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000321 SkRect r;
322
323 for (int i = 0; i < 5000; ++i) {
324
325 SkScalar size = rand.nextUScalar1() * 30;
326 if (size < SK_Scalar1) {
327 continue;
328 }
329 r.fLeft = rand.nextUScalar1() * 300;
330 r.fTop = rand.nextUScalar1() * 300;
331 r.fRight = r.fLeft + 2 * size;
332 r.fBottom = r.fTop + 2 * size;
333
334 SkPath temp;
335
336 make_arb_round_rect(&temp, r, 0, 0);
337
robertphillips@google.com158618e2012-10-23 16:56:56 +0000338 SkRect result;
339 REPORTER_ASSERT(reporter, temp.isRect(&result));
340 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000341 }
342}
343
reed@google.com0bb18bb2012-07-26 15:20:36 +0000344static void test_rect_isfinite(skiatest::Reporter* reporter) {
345 const SkScalar inf = SK_ScalarInfinity;
346 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000347
reed@google.com0bb18bb2012-07-26 15:20:36 +0000348 SkRect r;
349 r.setEmpty();
350 REPORTER_ASSERT(reporter, r.isFinite());
351 r.set(0, 0, inf, -inf);
352 REPORTER_ASSERT(reporter, !r.isFinite());
353 r.set(0, 0, nan, 0);
354 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000355
reed@google.com0bb18bb2012-07-26 15:20:36 +0000356 SkPoint pts[] = {
357 { 0, 0 },
358 { SK_Scalar1, 0 },
359 { 0, SK_Scalar1 },
360 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000361
reed@google.com0bb18bb2012-07-26 15:20:36 +0000362 bool isFine = r.setBoundsCheck(pts, 3);
363 REPORTER_ASSERT(reporter, isFine);
364 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000365
reed@google.com0bb18bb2012-07-26 15:20:36 +0000366 pts[1].set(inf, 0);
367 isFine = r.setBoundsCheck(pts, 3);
368 REPORTER_ASSERT(reporter, !isFine);
369 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000370
reed@google.com0bb18bb2012-07-26 15:20:36 +0000371 pts[1].set(nan, 0);
372 isFine = r.setBoundsCheck(pts, 3);
373 REPORTER_ASSERT(reporter, !isFine);
374 REPORTER_ASSERT(reporter, r.isEmpty());
375}
376
377static void test_path_isfinite(skiatest::Reporter* reporter) {
378 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000379 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000380 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000381
reed@google.com0bb18bb2012-07-26 15:20:36 +0000382 SkPath path;
383 REPORTER_ASSERT(reporter, path.isFinite());
384
385 path.reset();
386 REPORTER_ASSERT(reporter, path.isFinite());
387
388 path.reset();
389 path.moveTo(SK_Scalar1, 0);
390 REPORTER_ASSERT(reporter, path.isFinite());
391
392 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000393 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000394 REPORTER_ASSERT(reporter, !path.isFinite());
395
396 path.reset();
397 path.moveTo(nan, 0);
398 REPORTER_ASSERT(reporter, !path.isFinite());
399}
400
401static void test_isfinite(skiatest::Reporter* reporter) {
402 test_rect_isfinite(reporter);
403 test_path_isfinite(reporter);
404}
405
reed@google.com744faba2012-05-29 19:54:52 +0000406// assert that we always
407// start with a moveTo
408// only have 1 moveTo
409// only have Lines after that
410// end with a single close
411// only have (at most) 1 close
412//
413static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000414 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000415 SkPath::RawIter iter(path);
416 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000417
418 bool firstTime = true;
419 bool foundClose = false;
420 for (;;) {
421 switch (iter.next(pts)) {
422 case SkPath::kMove_Verb:
423 REPORTER_ASSERT(reporter, firstTime);
424 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
425 srcPts++;
426 firstTime = false;
427 break;
428 case SkPath::kLine_Verb:
429 REPORTER_ASSERT(reporter, !firstTime);
430 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
431 srcPts++;
432 break;
433 case SkPath::kQuad_Verb:
434 REPORTER_ASSERT(reporter, !"unexpected quad verb");
435 break;
436 case SkPath::kCubic_Verb:
437 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
438 break;
439 case SkPath::kClose_Verb:
440 REPORTER_ASSERT(reporter, !firstTime);
441 REPORTER_ASSERT(reporter, !foundClose);
442 REPORTER_ASSERT(reporter, expectClose);
443 foundClose = true;
444 break;
445 case SkPath::kDone_Verb:
446 goto DONE;
447 }
448 }
449DONE:
450 REPORTER_ASSERT(reporter, foundClose == expectClose);
451}
452
453static void test_addPoly(skiatest::Reporter* reporter) {
454 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000455 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000456
reed@google.com744faba2012-05-29 19:54:52 +0000457 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
458 pts[i].fX = rand.nextSScalar1();
459 pts[i].fY = rand.nextSScalar1();
460 }
461
462 for (int doClose = 0; doClose <= 1; ++doClose) {
463 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
464 SkPath path;
465 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000466 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000467 }
468 }
469}
470
reed@google.com8b06f1a2012-05-29 12:03:46 +0000471static void test_strokerec(skiatest::Reporter* reporter) {
472 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
473 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000474
reed@google.com8b06f1a2012-05-29 12:03:46 +0000475 rec.setHairlineStyle();
476 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000477
reed@google.com8b06f1a2012-05-29 12:03:46 +0000478 rec.setStrokeStyle(SK_Scalar1, false);
479 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000480
reed@google.com8b06f1a2012-05-29 12:03:46 +0000481 rec.setStrokeStyle(SK_Scalar1, true);
482 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000483
reed@google.com8b06f1a2012-05-29 12:03:46 +0000484 rec.setStrokeStyle(0, false);
485 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000486
reed@google.com8b06f1a2012-05-29 12:03:46 +0000487 rec.setStrokeStyle(0, true);
488 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
489}
490
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000491// Set this for paths that don't have a consistent direction such as a bowtie.
492// (cheapComputeDirection is not expected to catch these.)
493static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
494
495static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
496 SkPath::Direction expected) {
497 if (expected == kDontCheckDir) {
498 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000499 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000500 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
501
502 SkPath::Direction dir;
503 if (copy.cheapComputeDirection(&dir)) {
504 REPORTER_ASSERT(reporter, dir == expected);
505 } else {
506 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
507 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000508}
509
reed@google.com3e71a882012-01-10 18:44:37 +0000510static void test_direction(skiatest::Reporter* reporter) {
511 size_t i;
512 SkPath path;
513 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
514 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
515 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000516 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000517
518 static const char* gDegen[] = {
519 "M 10 10",
520 "M 10 10 M 20 20",
521 "M 10 10 L 20 20",
522 "M 10 10 L 10 10 L 10 10",
523 "M 10 10 Q 10 10 10 10",
524 "M 10 10 C 10 10 10 10 10 10",
525 };
526 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
527 path.reset();
528 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
529 REPORTER_ASSERT(reporter, valid);
530 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
531 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000532
reed@google.com3e71a882012-01-10 18:44:37 +0000533 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000534 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000535 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000536 "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 +0000537 // rect with top two corners replaced by cubics with identical middle
538 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000539 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
540 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000541 };
542 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
543 path.reset();
544 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
545 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000546 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000547 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000548
reed@google.com3e71a882012-01-10 18:44:37 +0000549 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000550 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000551 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000552 "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 +0000553 // rect with top two corners replaced by cubics with identical middle
554 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000555 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
556 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000557 };
558 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
559 path.reset();
560 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
561 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000562 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000563 }
reed@google.comac8543f2012-01-30 20:51:25 +0000564
565 // Test two donuts, each wound a different direction. Only the outer contour
566 // determines the cheap direction
567 path.reset();
568 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
569 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000570 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000571
reed@google.comac8543f2012-01-30 20:51:25 +0000572 path.reset();
573 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
574 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000575 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000576
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000577#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000578 // triangle with one point really far from the origin.
579 path.reset();
580 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000581 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
582 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
583 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000584 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000585#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000586}
587
reed@google.comffdb0182011-11-14 19:29:14 +0000588static void add_rect(SkPath* path, const SkRect& r) {
589 path->moveTo(r.fLeft, r.fTop);
590 path->lineTo(r.fRight, r.fTop);
591 path->lineTo(r.fRight, r.fBottom);
592 path->lineTo(r.fLeft, r.fBottom);
593 path->close();
594}
595
596static void test_bounds(skiatest::Reporter* reporter) {
597 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000598 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
599 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
600 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
601 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000602 };
603
604 SkPath path0, path1;
605 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
606 path0.addRect(rects[i]);
607 add_rect(&path1, rects[i]);
608 }
609
610 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
611}
612
reed@google.com55b5f4b2011-09-07 12:23:41 +0000613static void stroke_cubic(const SkPoint pts[4]) {
614 SkPath path;
615 path.moveTo(pts[0]);
616 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000617
reed@google.com55b5f4b2011-09-07 12:23:41 +0000618 SkPaint paint;
619 paint.setStyle(SkPaint::kStroke_Style);
620 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000621
reed@google.com55b5f4b2011-09-07 12:23:41 +0000622 SkPath fill;
623 paint.getFillPath(path, &fill);
624}
625
626// just ensure this can run w/o any SkASSERTS firing in the debug build
627// we used to assert due to differences in how we determine a degenerate vector
628// but that was fixed with the introduction of SkPoint::CanNormalize
629static void stroke_tiny_cubic() {
630 SkPoint p0[] = {
631 { 372.0f, 92.0f },
632 { 372.0f, 92.0f },
633 { 372.0f, 92.0f },
634 { 372.0f, 92.0f },
635 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000636
reed@google.com55b5f4b2011-09-07 12:23:41 +0000637 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000638
reed@google.com55b5f4b2011-09-07 12:23:41 +0000639 SkPoint p1[] = {
640 { 372.0f, 92.0f },
641 { 372.0007f, 92.000755f },
642 { 371.99927f, 92.003922f },
643 { 371.99826f, 92.003899f },
644 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000645
reed@google.com55b5f4b2011-09-07 12:23:41 +0000646 stroke_cubic(p1);
647}
648
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000649static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
650 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000651 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000652 SkPoint mv;
653 SkPoint pts[4];
654 SkPath::Verb v;
655 int nMT = 0;
656 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000657 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000658 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
659 switch (v) {
660 case SkPath::kMove_Verb:
661 mv = pts[0];
662 ++nMT;
663 break;
664 case SkPath::kClose_Verb:
665 REPORTER_ASSERT(reporter, mv == pts[0]);
666 ++nCL;
667 break;
668 default:
669 break;
670 }
671 }
672 // if we force a close on the interator we should have a close
673 // for every moveTo
674 REPORTER_ASSERT(reporter, !i || nMT == nCL);
675 }
676}
677
678static void test_close(skiatest::Reporter* reporter) {
679 SkPath closePt;
680 closePt.moveTo(0, 0);
681 closePt.close();
682 check_close(reporter, closePt);
683
684 SkPath openPt;
685 openPt.moveTo(0, 0);
686 check_close(reporter, openPt);
687
688 SkPath empty;
689 check_close(reporter, empty);
690 empty.close();
691 check_close(reporter, empty);
692
693 SkPath rect;
694 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
695 check_close(reporter, rect);
696 rect.close();
697 check_close(reporter, rect);
698
699 SkPath quad;
700 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
701 check_close(reporter, quad);
702 quad.close();
703 check_close(reporter, quad);
704
705 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000706 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000707 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
708 check_close(reporter, cubic);
709 cubic.close();
710 check_close(reporter, cubic);
711
712 SkPath line;
713 line.moveTo(SK_Scalar1, SK_Scalar1);
714 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
715 check_close(reporter, line);
716 line.close();
717 check_close(reporter, line);
718
719 SkPath rect2;
720 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
721 rect2.close();
722 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
723 check_close(reporter, rect2);
724 rect2.close();
725 check_close(reporter, rect2);
726
727 SkPath oval3;
728 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
729 oval3.close();
730 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
731 check_close(reporter, oval3);
732 oval3.close();
733 check_close(reporter, oval3);
734
735 SkPath moves;
736 moves.moveTo(SK_Scalar1, SK_Scalar1);
737 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
738 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
739 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
740 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000741
742 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000743}
744
reed@google.com7c424812011-05-15 04:38:34 +0000745static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
746 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000747 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
748 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000749 REPORTER_ASSERT(reporter, c == expected);
750}
751
752static void test_convexity2(skiatest::Reporter* reporter) {
753 SkPath pt;
754 pt.moveTo(0, 0);
755 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000756 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000757 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000758
reed@google.com7c424812011-05-15 04:38:34 +0000759 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000760 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
761 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000762 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000763 check_convexity(reporter, line, SkPath::kConvex_Convexity);
764 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000765
reed@google.com7c424812011-05-15 04:38:34 +0000766 SkPath triLeft;
767 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000768 triLeft.lineTo(SK_Scalar1, 0);
769 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000770 triLeft.close();
771 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000772 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000773
reed@google.com7c424812011-05-15 04:38:34 +0000774 SkPath triRight;
775 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000776 triRight.lineTo(-SK_Scalar1, 0);
777 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000778 triRight.close();
779 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000780 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000781
reed@google.com7c424812011-05-15 04:38:34 +0000782 SkPath square;
783 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000784 square.lineTo(SK_Scalar1, 0);
785 square.lineTo(SK_Scalar1, SK_Scalar1);
786 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000787 square.close();
788 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000789 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000790
reed@google.com7c424812011-05-15 04:38:34 +0000791 SkPath redundantSquare;
792 redundantSquare.moveTo(0, 0);
793 redundantSquare.lineTo(0, 0);
794 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000795 redundantSquare.lineTo(SK_Scalar1, 0);
796 redundantSquare.lineTo(SK_Scalar1, 0);
797 redundantSquare.lineTo(SK_Scalar1, 0);
798 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
799 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
800 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
801 redundantSquare.lineTo(0, SK_Scalar1);
802 redundantSquare.lineTo(0, SK_Scalar1);
803 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000804 redundantSquare.close();
805 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000806 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000807
reed@google.com7c424812011-05-15 04:38:34 +0000808 SkPath bowTie;
809 bowTie.moveTo(0, 0);
810 bowTie.lineTo(0, 0);
811 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000812 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
813 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
814 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
815 bowTie.lineTo(SK_Scalar1, 0);
816 bowTie.lineTo(SK_Scalar1, 0);
817 bowTie.lineTo(SK_Scalar1, 0);
818 bowTie.lineTo(0, SK_Scalar1);
819 bowTie.lineTo(0, SK_Scalar1);
820 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000821 bowTie.close();
822 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000823 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000824
reed@google.com7c424812011-05-15 04:38:34 +0000825 SkPath spiral;
826 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000827 spiral.lineTo(100*SK_Scalar1, 0);
828 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
829 spiral.lineTo(0, 100*SK_Scalar1);
830 spiral.lineTo(0, 50*SK_Scalar1);
831 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
832 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000833 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000834 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000835 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000836
reed@google.com7c424812011-05-15 04:38:34 +0000837 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000838 dent.moveTo(0, 0);
839 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
840 dent.lineTo(0, 100*SK_Scalar1);
841 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
842 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000843 dent.close();
844 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000845 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000846}
847
reed@android.com6b82d1a2009-06-03 02:35:01 +0000848static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
849 const SkRect& bounds) {
850 REPORTER_ASSERT(reporter, p.isConvex());
851 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000852
reed@android.com6b82d1a2009-06-03 02:35:01 +0000853 SkPath p2(p);
854 REPORTER_ASSERT(reporter, p2.isConvex());
855 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
856
857 SkPath other;
858 other.swap(p2);
859 REPORTER_ASSERT(reporter, other.isConvex());
860 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
861}
862
reed@google.com04863fa2011-05-15 04:08:24 +0000863static void setFromString(SkPath* path, const char str[]) {
864 bool first = true;
865 while (str) {
866 SkScalar x, y;
867 str = SkParse::FindScalar(str, &x);
868 if (NULL == str) {
869 break;
870 }
871 str = SkParse::FindScalar(str, &y);
872 SkASSERT(str);
873 if (first) {
874 path->moveTo(x, y);
875 first = false;
876 } else {
877 path->lineTo(x, y);
878 }
879 }
880}
881
882static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000883 SkPath path;
884
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000885 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000886 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000887 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000888 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000889 check_convexity(reporter, path, SkPath::kConcave_Convexity);
890
reed@google.com04863fa2011-05-15 04:08:24 +0000891 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000892 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000893 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000894 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000895
reed@google.com04863fa2011-05-15 04:08:24 +0000896 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000897 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000898 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000899 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000900
reed@google.com04863fa2011-05-15 04:08:24 +0000901 static const struct {
902 const char* fPathStr;
903 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000904 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000905 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000906 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
907 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
908 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
909 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
910 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
911 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
912 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
913 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000914 };
915
916 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
917 SkPath path;
918 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000919 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
920 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000921 }
922}
923
reed@google.com7e6c4d12012-05-10 14:05:43 +0000924static void test_isLine(skiatest::Reporter* reporter) {
925 SkPath path;
926 SkPoint pts[2];
927 const SkScalar value = SkIntToScalar(5);
928
929 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000930
reed@google.com7e6c4d12012-05-10 14:05:43 +0000931 // set some non-zero values
932 pts[0].set(value, value);
933 pts[1].set(value, value);
934 REPORTER_ASSERT(reporter, !path.isLine(pts));
935 // check that pts was untouched
936 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
937 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
938
939 const SkScalar moveX = SkIntToScalar(1);
940 const SkScalar moveY = SkIntToScalar(2);
941 SkASSERT(value != moveX && value != moveY);
942
943 path.moveTo(moveX, moveY);
944 REPORTER_ASSERT(reporter, !path.isLine(NULL));
945 REPORTER_ASSERT(reporter, !path.isLine(pts));
946 // check that pts was untouched
947 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
948 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
949
950 const SkScalar lineX = SkIntToScalar(2);
951 const SkScalar lineY = SkIntToScalar(2);
952 SkASSERT(value != lineX && value != lineY);
953
954 path.lineTo(lineX, lineY);
955 REPORTER_ASSERT(reporter, path.isLine(NULL));
956
957 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
958 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
959 REPORTER_ASSERT(reporter, path.isLine(pts));
960 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
961 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
962
963 path.lineTo(0, 0); // too many points/verbs
964 REPORTER_ASSERT(reporter, !path.isLine(NULL));
965 REPORTER_ASSERT(reporter, !path.isLine(pts));
966 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
967 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
968}
969
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000970static void test_conservativelyContains(skiatest::Reporter* reporter) {
971 SkPath path;
972
973 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
974 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
975
976 // A circle that bounds kBaseRect (with a significant amount of slop)
977 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
978 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
979 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
980
981 // round-rect radii
982 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +0000983
caryclark@google.com56f233a2012-11-19 13:06:06 +0000984 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000985 SkRect fQueryRect;
986 bool fInRect;
987 bool fInCircle;
988 bool fInRR;
989 } kQueries[] = {
990 {kBaseRect, true, true, false},
991
992 // rect well inside of kBaseRect
993 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
994 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
995 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
996 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
997 true, true, true},
998
999 // rects with edges off by one from kBaseRect's edges
1000 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1001 kBaseRect.width(), kBaseRect.height() + 1),
1002 false, true, false},
1003 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1004 kBaseRect.width() + 1, kBaseRect.height()),
1005 false, true, false},
1006 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1007 kBaseRect.width() + 1, kBaseRect.height() + 1),
1008 false, true, false},
1009 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1010 kBaseRect.width(), kBaseRect.height()),
1011 false, true, false},
1012 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1013 kBaseRect.width(), kBaseRect.height()),
1014 false, true, false},
1015 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1016 kBaseRect.width() + 2, kBaseRect.height()),
1017 false, true, false},
1018 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1019 kBaseRect.width() + 2, kBaseRect.height()),
1020 false, true, false},
1021
1022 // zero-w/h rects at each corner of kBaseRect
1023 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1024 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1025 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1026 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1027
1028 // far away rect
1029 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1030 SkIntToScalar(10), SkIntToScalar(10)),
1031 false, false, false},
1032
1033 // very large rect containing kBaseRect
1034 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1035 kBaseRect.fTop - 5 * kBaseRect.height(),
1036 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1037 false, false, false},
1038
1039 // skinny rect that spans same y-range as kBaseRect
1040 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1041 SkIntToScalar(1), kBaseRect.height()),
1042 true, true, true},
1043
1044 // short rect that spans same x-range as kBaseRect
1045 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1046 true, true, true},
1047
1048 // skinny rect that spans slightly larger y-range than kBaseRect
1049 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1050 SkIntToScalar(1), kBaseRect.height() + 1),
1051 false, true, false},
1052
1053 // short rect that spans slightly larger x-range than kBaseRect
1054 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1055 kBaseRect.width() + 1, SkScalar(1)),
1056 false, true, false},
1057 };
1058
1059 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001060 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001061 SkRect qRect = kQueries[q].fQueryRect;
1062 if (inv & 0x1) {
1063 SkTSwap(qRect.fLeft, qRect.fRight);
1064 }
1065 if (inv & 0x2) {
1066 SkTSwap(qRect.fTop, qRect.fBottom);
1067 }
1068 for (int d = 0; d < 2; ++d) {
1069 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1070 path.reset();
1071 path.addRect(kBaseRect, dir);
1072 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1073 path.conservativelyContainsRect(qRect));
1074
1075 path.reset();
1076 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1077 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1078 path.conservativelyContainsRect(qRect));
1079
1080 path.reset();
1081 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1082 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1083 path.conservativelyContainsRect(qRect));
1084 }
1085 // Slightly non-convex shape, shouldn't contain any rects.
1086 path.reset();
1087 path.moveTo(0, 0);
1088 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1089 path.lineTo(SkIntToScalar(100), 0);
1090 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1091 path.lineTo(0, SkIntToScalar(100));
1092 path.close();
1093 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1094 }
1095 }
1096
1097 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1098 path.reset();
1099 path.moveTo(0, 0);
1100 path.lineTo(SkIntToScalar(100), 0);
1101 path.lineTo(0, SkIntToScalar(100));
1102
1103 // inside, on along top edge
1104 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1105 SkIntToScalar(10),
1106 SkIntToScalar(10))));
1107 // above
1108 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1109 SkRect::MakeXYWH(SkIntToScalar(50),
1110 SkIntToScalar(-10),
1111 SkIntToScalar(10),
1112 SkIntToScalar(10))));
1113 // to the left
1114 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1115 SkIntToScalar(5),
1116 SkIntToScalar(5),
1117 SkIntToScalar(5))));
1118
1119 // outside the diagonal edge
1120 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1121 SkIntToScalar(200),
1122 SkIntToScalar(20),
1123 SkIntToScalar(5))));
1124}
1125
caryclark@google.comf1316942011-07-26 19:54:45 +00001126// Simple isRect test is inline TestPath, below.
1127// test_isRect provides more extensive testing.
1128static void test_isRect(skiatest::Reporter* reporter) {
1129 // passing tests (all moveTo / lineTo...
1130 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1131 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1132 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1133 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1134 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1135 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1136 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1137 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1138 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1139 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1140 {1, 0}, {.5f, 0}};
1141 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1142 {0, 1}, {0, .5f}};
1143 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1144 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1145 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001146 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001147
caryclark@google.comf1316942011-07-26 19:54:45 +00001148 // failing tests
1149 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1150 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1151 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1152 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1153 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1154 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1155 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1156 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001157 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1158 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1159 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001160
caryclark@google.comf1316942011-07-26 19:54:45 +00001161 // failing, no close
1162 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1163 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1164
1165 size_t testLen[] = {
1166 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1167 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001168 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001169 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001170 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001171 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001172 };
1173 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001174 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1175 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001176 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001177 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001178 SkPoint* lastPass = rf;
1179 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001180 bool fail = false;
1181 bool close = true;
1182 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1183 size_t index;
1184 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1185 SkPath path;
1186 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1187 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1188 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1189 }
1190 if (close) {
1191 path.close();
1192 }
1193 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001194 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1195
caryclark@google.com56f233a2012-11-19 13:06:06 +00001196 if (!fail) {
1197 SkRect computed, expected;
1198 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1199 REPORTER_ASSERT(reporter, path.isRect(&computed));
1200 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001201
caryclark@google.comf68154a2012-11-21 15:18:06 +00001202 bool isClosed;
1203 SkPath::Direction direction, cheapDirection;
1204 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1205 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1206 REPORTER_ASSERT(reporter, isClosed == close);
1207 REPORTER_ASSERT(reporter, direction == cheapDirection);
1208 } else {
1209 SkRect computed;
1210 computed.set(123, 456, 789, 1011);
1211 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1212 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1213 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1214
1215 bool isClosed = (bool) -1;
1216 SkPath::Direction direction = (SkPath::Direction) -1;
1217 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1218 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1219 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001220 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001221
caryclark@google.comf1316942011-07-26 19:54:45 +00001222 if (tests[testIndex] == lastPass) {
1223 fail = true;
1224 }
1225 if (tests[testIndex] == lastClose) {
1226 close = false;
1227 }
1228 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001229
caryclark@google.comf1316942011-07-26 19:54:45 +00001230 // fail, close then line
1231 SkPath path1;
1232 path1.moveTo(r1[0].fX, r1[0].fY);
1233 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1234 path1.lineTo(r1[index].fX, r1[index].fY);
1235 }
1236 path1.close();
1237 path1.lineTo(1, 0);
1238 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001239
caryclark@google.comf1316942011-07-26 19:54:45 +00001240 // fail, move in the middle
1241 path1.reset();
1242 path1.moveTo(r1[0].fX, r1[0].fY);
1243 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1244 if (index == 2) {
1245 path1.moveTo(1, .5f);
1246 }
1247 path1.lineTo(r1[index].fX, r1[index].fY);
1248 }
1249 path1.close();
1250 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1251
1252 // fail, move on the edge
1253 path1.reset();
1254 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1255 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1256 path1.lineTo(r1[index].fX, r1[index].fY);
1257 }
1258 path1.close();
1259 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001260
caryclark@google.comf1316942011-07-26 19:54:45 +00001261 // fail, quad
1262 path1.reset();
1263 path1.moveTo(r1[0].fX, r1[0].fY);
1264 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1265 if (index == 2) {
1266 path1.quadTo(1, .5f, 1, .5f);
1267 }
1268 path1.lineTo(r1[index].fX, r1[index].fY);
1269 }
1270 path1.close();
1271 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001272
caryclark@google.comf1316942011-07-26 19:54:45 +00001273 // fail, cubic
1274 path1.reset();
1275 path1.moveTo(r1[0].fX, r1[0].fY);
1276 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1277 if (index == 2) {
1278 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1279 }
1280 path1.lineTo(r1[index].fX, r1[index].fY);
1281 }
1282 path1.close();
1283 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1284}
1285
caryclark@google.com56f233a2012-11-19 13:06:06 +00001286static void test_isNestedRects(skiatest::Reporter* reporter) {
1287 // passing tests (all moveTo / lineTo...
1288 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1289 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1290 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1291 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1292 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1293 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1294 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1295 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1296 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1297 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1298 {1, 0}, {.5f, 0}};
1299 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1300 {0, 1}, {0, .5f}};
1301 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1302 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1303 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1304
1305 // failing tests
1306 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1307 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1308 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1309 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1310 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1311 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1312 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1313 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1314
1315 // failing, no close
1316 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1317 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1318
1319 size_t testLen[] = {
1320 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1321 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1322 sizeof(rd), sizeof(re),
1323 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1324 sizeof(f7), sizeof(f8),
1325 sizeof(c1), sizeof(c2)
1326 };
1327 SkPoint* tests[] = {
1328 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1329 f1, f2, f3, f4, f5, f6, f7, f8,
1330 c1, c2
1331 };
1332 const SkPoint* lastPass = re;
1333 const SkPoint* lastClose = f8;
1334 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1335 size_t index;
1336 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1337 bool fail = false;
1338 bool close = true;
1339 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1340 SkPath path;
1341 if (rectFirst) {
1342 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1343 }
1344 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1345 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1346 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1347 }
1348 if (close) {
1349 path.close();
1350 }
1351 if (!rectFirst) {
1352 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1353 }
1354 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1355 if (!fail) {
1356 SkRect expected[2], computed[2];
1357 SkRect testBounds;
1358 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1359 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1360 expected[1] = testBounds;
1361 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1362 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1363 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1364 }
1365 if (tests[testIndex] == lastPass) {
1366 fail = true;
1367 }
1368 if (tests[testIndex] == lastClose) {
1369 close = false;
1370 }
1371 }
1372
1373 // fail, close then line
1374 SkPath path1;
1375 if (rectFirst) {
1376 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1377 }
1378 path1.moveTo(r1[0].fX, r1[0].fY);
1379 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1380 path1.lineTo(r1[index].fX, r1[index].fY);
1381 }
1382 path1.close();
1383 path1.lineTo(1, 0);
1384 if (!rectFirst) {
1385 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1386 }
1387 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1388
1389 // fail, move in the middle
1390 path1.reset();
1391 if (rectFirst) {
1392 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1393 }
1394 path1.moveTo(r1[0].fX, r1[0].fY);
1395 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1396 if (index == 2) {
1397 path1.moveTo(1, .5f);
1398 }
1399 path1.lineTo(r1[index].fX, r1[index].fY);
1400 }
1401 path1.close();
1402 if (!rectFirst) {
1403 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1404 }
1405 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1406
1407 // fail, move on the edge
1408 path1.reset();
1409 if (rectFirst) {
1410 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1411 }
1412 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1413 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1414 path1.lineTo(r1[index].fX, r1[index].fY);
1415 }
1416 path1.close();
1417 if (!rectFirst) {
1418 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1419 }
1420 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1421
1422 // fail, quad
1423 path1.reset();
1424 if (rectFirst) {
1425 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1426 }
1427 path1.moveTo(r1[0].fX, r1[0].fY);
1428 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1429 if (index == 2) {
1430 path1.quadTo(1, .5f, 1, .5f);
1431 }
1432 path1.lineTo(r1[index].fX, r1[index].fY);
1433 }
1434 path1.close();
1435 if (!rectFirst) {
1436 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1437 }
1438 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1439
1440 // fail, cubic
1441 path1.reset();
1442 if (rectFirst) {
1443 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1444 }
1445 path1.moveTo(r1[0].fX, r1[0].fY);
1446 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1447 if (index == 2) {
1448 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1449 }
1450 path1.lineTo(r1[index].fX, r1[index].fY);
1451 }
1452 path1.close();
1453 if (!rectFirst) {
1454 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1455 }
1456 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001457
caryclark@google.com56f233a2012-11-19 13:06:06 +00001458 // fail, not nested
1459 path1.reset();
1460 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1461 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1462 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1463 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001464
1465 // pass, stroke rect
1466 SkPath src, dst;
1467 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1468 SkPaint strokePaint;
1469 strokePaint.setStyle(SkPaint::kStroke_Style);
1470 strokePaint.setStrokeWidth(2);
1471 strokePaint.getFillPath(src, &dst);
1472 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001473}
1474
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001475static void write_and_read_back(skiatest::Reporter* reporter,
1476 const SkPath& p) {
1477 SkWriter32 writer(100);
1478 writer.writePath(p);
1479 size_t size = writer.size();
1480 SkAutoMalloc storage(size);
1481 writer.flatten(storage.get());
1482 SkReader32 reader(storage.get(), size);
1483
1484 SkPath readBack;
1485 REPORTER_ASSERT(reporter, readBack != p);
1486 reader.readPath(&readBack);
1487 REPORTER_ASSERT(reporter, readBack == p);
1488
rmistry@google.comd6176b02012-08-23 18:14:13 +00001489 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001490 p.getConvexityOrUnknown());
1491
1492 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1493
1494 const SkRect& origBounds = p.getBounds();
1495 const SkRect& readBackBounds = readBack.getBounds();
1496
1497 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1498}
1499
reed@google.com53effc52011-09-21 19:05:12 +00001500static void test_flattening(skiatest::Reporter* reporter) {
1501 SkPath p;
1502
1503 static const SkPoint pts[] = {
1504 { 0, 0 },
1505 { SkIntToScalar(10), SkIntToScalar(10) },
1506 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1507 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1508 };
1509 p.moveTo(pts[0]);
1510 p.lineTo(pts[1]);
1511 p.quadTo(pts[2], pts[3]);
1512 p.cubicTo(pts[4], pts[5], pts[6]);
1513
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001514 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001515
1516 // create a buffer that should be much larger than the path so we don't
1517 // kill our stack if writer goes too far.
1518 char buffer[1024];
1519 uint32_t size1 = p.writeToMemory(NULL);
1520 uint32_t size2 = p.writeToMemory(buffer);
1521 REPORTER_ASSERT(reporter, size1 == size2);
1522
1523 SkPath p2;
1524 uint32_t size3 = p2.readFromMemory(buffer);
1525 REPORTER_ASSERT(reporter, size1 == size3);
1526 REPORTER_ASSERT(reporter, p == p2);
1527
1528 char buffer2[1024];
1529 size3 = p2.writeToMemory(buffer2);
1530 REPORTER_ASSERT(reporter, size1 == size3);
1531 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001532
1533 // test persistence of the oval flag & convexity
1534 {
1535 SkPath oval;
1536 SkRect rect = SkRect::MakeWH(10, 10);
1537 oval.addOval(rect);
1538
1539 write_and_read_back(reporter, oval);
1540 }
reed@google.com53effc52011-09-21 19:05:12 +00001541}
1542
1543static void test_transform(skiatest::Reporter* reporter) {
1544 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001545
reed@google.com53effc52011-09-21 19:05:12 +00001546 static const SkPoint pts[] = {
1547 { 0, 0 },
1548 { SkIntToScalar(10), SkIntToScalar(10) },
1549 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1550 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1551 };
1552 p.moveTo(pts[0]);
1553 p.lineTo(pts[1]);
1554 p.quadTo(pts[2], pts[3]);
1555 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001556
reed@google.com53effc52011-09-21 19:05:12 +00001557 SkMatrix matrix;
1558 matrix.reset();
1559 p.transform(matrix, &p1);
1560 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001561
reed@google.com53effc52011-09-21 19:05:12 +00001562 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1563 p.transform(matrix, &p1);
1564 SkPoint pts1[7];
1565 int count = p1.getPoints(pts1, 7);
1566 REPORTER_ASSERT(reporter, 7 == count);
1567 for (int i = 0; i < count; ++i) {
1568 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1569 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1570 }
1571}
1572
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001573static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001574 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001575 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001576
caryclark@google.com56f233a2012-11-19 13:06:06 +00001577 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001578 const char* testPath;
1579 const size_t numResultPts;
1580 const SkRect resultBound;
1581 const SkPath::Verb* resultVerbs;
1582 const size_t numResultVerbs;
1583 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001584
schenney@chromium.org7e963602012-06-13 17:05:43 +00001585 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1586 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1587 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1588 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1589 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1590 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1591 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1592 static const SkPath::Verb resultVerbs8[] = {
1593 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1594 };
1595 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1596 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1597 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1598 static const SkPath::Verb resultVerbs12[] = {
1599 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1600 };
1601 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1602 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1603 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1604 static const SkPath::Verb resultVerbs16[] = {
1605 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1606 };
1607 static const struct zeroPathTestData gZeroLengthTests[] = {
1608 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001609 { "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 +00001610 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001611 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1612 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1613 { "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) },
1614 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1615 { "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) },
1616 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1617 { "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) },
1618 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1619 { "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) },
1620 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1621 { "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 +00001622 SK_ARRAY_COUNT(resultVerbs14)
1623 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001624 { "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) },
1625 { "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 +00001626 SK_ARRAY_COUNT(resultVerbs16)
1627 }
1628 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001629
schenney@chromium.org7e963602012-06-13 17:05:43 +00001630 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1631 p.reset();
1632 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1633 REPORTER_ASSERT(reporter, valid);
1634 REPORTER_ASSERT(reporter, !p.isEmpty());
1635 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1636 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1637 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1638 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1639 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1640 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001641 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001642}
1643
1644struct SegmentInfo {
1645 SkPath fPath;
1646 int fPointCount;
1647};
1648
reed@google.com10296cc2011-09-21 12:29:05 +00001649#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1650
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001651static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001652 SkPath p, p2;
1653
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001654 p.moveTo(0, 0);
1655 p.quadTo(100, 100, 200, 200);
1656 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1657 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001658 p2 = p;
1659 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001660 p.cubicTo(100, 100, 200, 200, 300, 300);
1661 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1662 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001663 p2 = p;
1664 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1665
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001666 p.reset();
1667 p.moveTo(0, 0);
1668 p.cubicTo(100, 100, 200, 200, 300, 300);
1669 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001670 p2 = p;
1671 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001672
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001673 REPORTER_ASSERT(reporter, !p.isEmpty());
1674}
1675
1676static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001677 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001678 SkPoint pts[4];
1679
1680 // Test an iterator with no path
1681 SkPath::Iter noPathIter;
1682 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001683
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001684 // Test that setting an empty path works
1685 noPathIter.setPath(p, false);
1686 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001687
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001688 // Test that close path makes no difference for an empty path
1689 noPathIter.setPath(p, true);
1690 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001691
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001692 // Test an iterator with an initial empty path
1693 SkPath::Iter iter(p, false);
1694 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1695
1696 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001697 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001698 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1699
rmistry@google.comd6176b02012-08-23 18:14:13 +00001700
schenney@chromium.org7e963602012-06-13 17:05:43 +00001701 struct iterTestData {
1702 const char* testPath;
1703 const bool forceClose;
1704 const bool consumeDegenerates;
1705 const size_t* numResultPtsPerVerb;
1706 const SkPoint* resultPts;
1707 const SkPath::Verb* resultVerbs;
1708 const size_t numResultVerbs;
1709 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001710
schenney@chromium.org7e963602012-06-13 17:05:43 +00001711 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1712 static const SkPath::Verb resultVerbs2[] = {
1713 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1714 };
1715 static const SkPath::Verb resultVerbs3[] = {
1716 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1717 };
1718 static const SkPath::Verb resultVerbs4[] = {
1719 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1720 };
1721 static const SkPath::Verb resultVerbs5[] = {
1722 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1723 };
1724 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001725 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1726 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1727 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1728 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001729 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001730 static const SkPoint resultPts2[] = {
1731 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1732 };
1733 static const SkPoint resultPts3[] = {
1734 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1735 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1736 };
1737 static const SkPoint resultPts4[] = {
1738 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1739 };
1740 static const SkPoint resultPts5[] = {
1741 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1742 };
1743 static const struct iterTestData gIterTests[] = {
1744 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001745 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1746 { "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 +00001747 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1748 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1749 { "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) },
1750 { "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 +00001751 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1752 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1753 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1754 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1755 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1756 { "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 +00001757 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001758
schenney@chromium.org7e963602012-06-13 17:05:43 +00001759 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1760 p.reset();
1761 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1762 REPORTER_ASSERT(reporter, valid);
1763 iter.setPath(p, gIterTests[i].forceClose);
1764 int j = 0, l = 0;
1765 do {
1766 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1767 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1768 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1769 }
1770 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1771 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1772 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001773
1774 // The GM degeneratesegments.cpp test is more extensive
1775}
1776
1777static void test_raw_iter(skiatest::Reporter* reporter) {
1778 SkPath p;
1779 SkPoint pts[4];
1780
1781 // Test an iterator with no path
1782 SkPath::RawIter noPathIter;
1783 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1784 // Test that setting an empty path works
1785 noPathIter.setPath(p);
1786 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001787
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001788 // Test an iterator with an initial empty path
1789 SkPath::RawIter iter(p);
1790 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1791
1792 // Test that a move-only path returns the move.
1793 p.moveTo(SK_Scalar1, 0);
1794 iter.setPath(p);
1795 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1796 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1797 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1798 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1799
1800 // No matter how many moves we add, we should get them all back
1801 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1802 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1803 iter.setPath(p);
1804 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1805 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1806 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1807 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1808 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1809 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1810 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1811 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1812 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1813 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1814
1815 // Initial close is never ever stored
1816 p.reset();
1817 p.close();
1818 iter.setPath(p);
1819 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1820
1821 // Move/close sequences
1822 p.reset();
1823 p.close(); // Not stored, no purpose
1824 p.moveTo(SK_Scalar1, 0);
1825 p.close();
1826 p.close(); // Not stored, no purpose
1827 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1828 p.close();
1829 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1830 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1831 p.close();
1832 iter.setPath(p);
1833 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1834 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1835 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1836 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1837 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1838 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1839 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1840 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1841 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1842 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1843 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1844 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1845 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1846 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1847 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1848 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1849 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1850 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1851 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1852 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1853 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1854 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1855
1856 // Generate random paths and verify
1857 SkPoint randomPts[25];
1858 for (int i = 0; i < 5; ++i) {
1859 for (int j = 0; j < 5; ++j) {
1860 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1861 }
1862 }
1863
1864 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001865 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001866 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001867 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001868 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001869
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001870 for (int i = 0; i < 500; ++i) {
1871 p.reset();
1872 bool lastWasClose = true;
1873 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001874 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001875 int numPoints = 0;
1876 int numVerbs = (rand.nextU() >> 16) % 10;
1877 int numIterVerbs = 0;
1878 for (int j = 0; j < numVerbs; ++j) {
1879 do {
1880 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1881 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001882 switch (nextVerb) {
1883 case SkPath::kMove_Verb:
1884 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1885 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001886 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001887 numPoints += 1;
1888 lastWasClose = false;
1889 haveMoveTo = true;
1890 break;
1891 case SkPath::kLine_Verb:
1892 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001893 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001894 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1895 haveMoveTo = true;
1896 }
1897 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1898 p.lineTo(expectedPts[numPoints]);
1899 numPoints += 1;
1900 lastWasClose = false;
1901 break;
1902 case SkPath::kQuad_Verb:
1903 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001904 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001905 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1906 haveMoveTo = true;
1907 }
1908 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1909 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1910 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1911 numPoints += 2;
1912 lastWasClose = false;
1913 break;
1914 case SkPath::kCubic_Verb:
1915 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001916 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001917 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1918 haveMoveTo = true;
1919 }
1920 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1921 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1922 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1923 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1924 expectedPts[numPoints + 2]);
1925 numPoints += 3;
1926 lastWasClose = false;
1927 break;
1928 case SkPath::kClose_Verb:
1929 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001930 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001931 lastWasClose = true;
1932 break;
1933 default:;
1934 }
1935 expectedVerbs[numIterVerbs++] = nextVerb;
1936 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001937
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001938 iter.setPath(p);
1939 numVerbs = numIterVerbs;
1940 numIterVerbs = 0;
1941 int numIterPts = 0;
1942 SkPoint lastMoveTo;
1943 SkPoint lastPt;
1944 lastMoveTo.set(0, 0);
1945 lastPt.set(0, 0);
1946 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1947 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1948 numIterVerbs++;
1949 switch (nextVerb) {
1950 case SkPath::kMove_Verb:
1951 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1952 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1953 lastPt = lastMoveTo = pts[0];
1954 numIterPts += 1;
1955 break;
1956 case SkPath::kLine_Verb:
1957 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1958 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1959 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1960 lastPt = pts[1];
1961 numIterPts += 1;
1962 break;
1963 case SkPath::kQuad_Verb:
1964 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1965 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1966 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1967 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1968 lastPt = pts[2];
1969 numIterPts += 2;
1970 break;
1971 case SkPath::kCubic_Verb:
1972 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1973 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1974 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1975 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1976 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1977 lastPt = pts[3];
1978 numIterPts += 3;
1979 break;
1980 case SkPath::kClose_Verb:
1981 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1982 lastPt = lastMoveTo;
1983 break;
1984 default:;
1985 }
1986 }
1987 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1988 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1989 }
1990}
1991
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001992static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001993 const SkPath& path,
1994 bool expectedCircle,
1995 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001996 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001997 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
1998 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00001999
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002000 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002001 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2002 }
2003}
2004
2005static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002006 const SkPath& path,
2007 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002008 SkPath tmp;
2009
2010 SkMatrix m;
2011 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2012 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002013 // this matrix reverses the direction.
2014 if (SkPath::kCCW_Direction == dir) {
2015 dir = SkPath::kCW_Direction;
2016 } else {
2017 SkASSERT(SkPath::kCW_Direction == dir);
2018 dir = SkPath::kCCW_Direction;
2019 }
2020 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002021}
2022
2023static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002024 const SkPath& path,
2025 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002026 SkPath tmp;
2027
2028 // translate at small offset
2029 SkMatrix m;
2030 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2031 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002032 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002033
2034 tmp.reset();
2035 m.reset();
2036
2037 // translate at a relatively big offset
2038 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2039 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002040 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002041}
2042
2043static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002044 const SkPath& path,
2045 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002046 for (int angle = 0; angle < 360; ++angle) {
2047 SkPath tmp;
2048 SkMatrix m;
2049 m.setRotate(SkIntToScalar(angle));
2050 path.transform(m, &tmp);
2051
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002052 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002053 // degrees is not an oval anymore, this can be improved. we made this
2054 // for the simplicity of our implementation.
2055 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002056 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002057 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002058 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002059 }
2060 }
2061}
2062
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002063static void test_circle_mirror_x(skiatest::Reporter* reporter,
2064 const SkPath& path,
2065 SkPath::Direction dir) {
2066 SkPath tmp;
2067 SkMatrix m;
2068 m.reset();
2069 m.setScaleX(-SK_Scalar1);
2070 path.transform(m, &tmp);
2071
2072 if (SkPath::kCW_Direction == dir) {
2073 dir = SkPath::kCCW_Direction;
2074 } else {
2075 SkASSERT(SkPath::kCCW_Direction == dir);
2076 dir = SkPath::kCW_Direction;
2077 }
2078
2079 check_for_circle(reporter, tmp, true, dir);
2080}
2081
2082static void test_circle_mirror_y(skiatest::Reporter* reporter,
2083 const SkPath& path,
2084 SkPath::Direction dir) {
2085 SkPath tmp;
2086 SkMatrix m;
2087 m.reset();
2088 m.setScaleY(-SK_Scalar1);
2089 path.transform(m, &tmp);
2090
2091 if (SkPath::kCW_Direction == dir) {
2092 dir = SkPath::kCCW_Direction;
2093 } else {
2094 SkASSERT(SkPath::kCCW_Direction == dir);
2095 dir = SkPath::kCW_Direction;
2096 }
2097
2098 check_for_circle(reporter, tmp, true, dir);
2099}
2100
2101static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2102 const SkPath& path,
2103 SkPath::Direction dir) {
2104 SkPath tmp;
2105 SkMatrix m;
2106 m.reset();
2107 m.setScaleX(-SK_Scalar1);
2108 m.setScaleY(-SK_Scalar1);
2109 path.transform(m, &tmp);
2110
2111 check_for_circle(reporter, tmp, true, dir);
2112}
2113
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002114static void test_circle_with_direction(skiatest::Reporter* reporter,
2115 SkPath::Direction dir) {
2116 SkPath path;
2117
2118 // circle at origin
2119 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002120 check_for_circle(reporter, path, true, dir);
2121 test_circle_rotate(reporter, path, dir);
2122 test_circle_translate(reporter, path, dir);
2123 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002124
2125 // circle at an offset at (10, 10)
2126 path.reset();
2127 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2128 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002129 check_for_circle(reporter, path, true, dir);
2130 test_circle_rotate(reporter, path, dir);
2131 test_circle_translate(reporter, path, dir);
2132 test_circle_skew(reporter, path, dir);
2133 test_circle_mirror_x(reporter, path, dir);
2134 test_circle_mirror_y(reporter, path, dir);
2135 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002136}
2137
2138static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2139 SkPath path;
2140 SkPath circle;
2141 SkPath rect;
2142 SkPath empty;
2143
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002144 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2145 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2146
2147 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002148 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2149 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2150
2151 SkMatrix translate;
2152 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2153
2154 // For simplicity, all the path concatenation related operations
2155 // would mark it non-circle, though in theory it's still a circle.
2156
2157 // empty + circle (translate)
2158 path = empty;
2159 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002160 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002161
2162 // circle + empty (translate)
2163 path = circle;
2164 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002165 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002166
2167 // test reverseAddPath
2168 path = circle;
2169 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002170 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002171}
2172
2173static void test_circle(skiatest::Reporter* reporter) {
2174 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2175 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2176
2177 // multiple addCircle()
2178 SkPath path;
2179 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2180 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002181 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002182
2183 // some extra lineTo() would make isOval() fail
2184 path.reset();
2185 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2186 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002187 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002188
2189 // not back to the original point
2190 path.reset();
2191 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2192 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002193 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002194
2195 test_circle_with_add_paths(reporter);
2196}
2197
2198static void test_oval(skiatest::Reporter* reporter) {
2199 SkRect rect;
2200 SkMatrix m;
2201 SkPath path;
2202
2203 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2204 path.addOval(rect);
2205
2206 REPORTER_ASSERT(reporter, path.isOval(NULL));
2207
2208 m.setRotate(SkIntToScalar(90));
2209 SkPath tmp;
2210 path.transform(m, &tmp);
2211 // an oval rotated 90 degrees is still an oval.
2212 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2213
2214 m.reset();
2215 m.setRotate(SkIntToScalar(30));
2216 tmp.reset();
2217 path.transform(m, &tmp);
2218 // an oval rotated 30 degrees is not an oval anymore.
2219 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2220
2221 // since empty path being transformed.
2222 path.reset();
2223 tmp.reset();
2224 m.reset();
2225 path.transform(m, &tmp);
2226 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2227
2228 // empty path is not an oval
2229 tmp.reset();
2230 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2231
2232 // only has moveTo()s
2233 tmp.reset();
2234 tmp.moveTo(0, 0);
2235 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2236 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2237
2238 // mimic WebKit's calling convention,
2239 // call moveTo() first and then call addOval()
2240 path.reset();
2241 path.moveTo(0, 0);
2242 path.addOval(rect);
2243 REPORTER_ASSERT(reporter, path.isOval(NULL));
2244
2245 // copy path
2246 path.reset();
2247 tmp.reset();
2248 tmp.addOval(rect);
2249 path = tmp;
2250 REPORTER_ASSERT(reporter, path.isOval(NULL));
2251}
2252
caryclark@google.com42639cd2012-06-06 12:03:39 +00002253static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002254 SkTSize<SkScalar>::Make(3,4);
2255
reed@android.com3abec1d2009-03-02 05:36:20 +00002256 SkPath p, p2;
2257 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002258
reed@android.com3abec1d2009-03-02 05:36:20 +00002259 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002260 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002261 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002262 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002263 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002264 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2265 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2266 REPORTER_ASSERT(reporter, p == p2);
2267 REPORTER_ASSERT(reporter, !(p != p2));
2268
reed@android.comd252db02009-04-01 18:31:44 +00002269 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002270
reed@android.com3abec1d2009-03-02 05:36:20 +00002271 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002272
reed@android.com6b82d1a2009-06-03 02:35:01 +00002273 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2274 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002275 // we have quads or cubics
2276 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002277 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002278
reed@android.com6b82d1a2009-06-03 02:35:01 +00002279 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002280 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002281 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002282
reed@android.com6b82d1a2009-06-03 02:35:01 +00002283 p.addOval(bounds);
2284 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002285 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002286
reed@android.com6b82d1a2009-06-03 02:35:01 +00002287 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002288 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002289 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002290 // we have only lines
2291 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002292 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002293
2294 REPORTER_ASSERT(reporter, p != p2);
2295 REPORTER_ASSERT(reporter, !(p == p2));
2296
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002297 // do getPoints and getVerbs return the right result
2298 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2299 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002300 SkPoint pts[4];
2301 int count = p.getPoints(pts, 4);
2302 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002303 uint8_t verbs[6];
2304 verbs[5] = 0xff;
2305 p.getVerbs(verbs, 5);
2306 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2307 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2308 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2309 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2310 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2311 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002312 bounds2.set(pts, 4);
2313 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002314
reed@android.com3abec1d2009-03-02 05:36:20 +00002315 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2316 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002317 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002318
reed@android.com3abec1d2009-03-02 05:36:20 +00002319 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002320 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002321 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2322 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002323
reed@android.com3abec1d2009-03-02 05:36:20 +00002324 // now force p to not be a rect
2325 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2326 p.addRect(bounds);
2327 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002328
reed@google.com7e6c4d12012-05-10 14:05:43 +00002329 test_isLine(reporter);
2330 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002331 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002332 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002333 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002334 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002335 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002336 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002337 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002338 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002339 test_flattening(reporter);
2340 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002341 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002342 test_iter(reporter);
2343 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002344 test_circle(reporter);
2345 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002346 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002347 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002348 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002349 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002350 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002351 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002352 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002353 test_tricky_cubic();
2354 test_clipped_cubic();
2355 test_crbug_170666();
reed@android.com3abec1d2009-03-02 05:36:20 +00002356}
2357
2358#include "TestClassDef.h"
2359DEFINE_TESTCLASS("Path", PathTestClass, TestPath)