blob: d4442cb59424338d19f507d0120a93d47914e780 [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).
122static void test_crbug_170666(skiatest::Reporter* reporter) {
123 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
178static void test_clipped_cubic(skiatest::Reporter* reporter) {
179 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.
200static void test_tricky_cubic(skiatest::Reporter* reporter) {
201 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) {
293 SkRandom rand;
294 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
robertphillips@google.com194bf822013-01-23 20:45:26 +0000311#ifndef SK_IGNORE_CONVEX_QUAD_OPT
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000312 REPORTER_ASSERT(reporter, temp.isConvex());
robertphillips@google.comc7a37c72012-10-19 01:26:18 +0000313#endif
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000314 }
315}
316
robertphillips@google.com158618e2012-10-23 16:56:56 +0000317// Chrome will sometimes create a 0 radius round rect. The degenerate
318// quads prevent the path from being converted to a rect
319// Note: PathBench::ArbRoundRectBench performs almost exactly
320// the same test (but with drawing)
321static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
322 SkRandom rand;
323 SkRect r;
324
325 for (int i = 0; i < 5000; ++i) {
326
327 SkScalar size = rand.nextUScalar1() * 30;
328 if (size < SK_Scalar1) {
329 continue;
330 }
331 r.fLeft = rand.nextUScalar1() * 300;
332 r.fTop = rand.nextUScalar1() * 300;
333 r.fRight = r.fLeft + 2 * size;
334 r.fBottom = r.fTop + 2 * size;
335
336 SkPath temp;
337
338 make_arb_round_rect(&temp, r, 0, 0);
339
robertphillips@google.com194bf822013-01-23 20:45:26 +0000340#ifndef SK_IGNORE_CONVEX_QUAD_OPT
robertphillips@google.com158618e2012-10-23 16:56:56 +0000341 SkRect result;
342 REPORTER_ASSERT(reporter, temp.isRect(&result));
343 REPORTER_ASSERT(reporter, r == result);
344#endif
345 }
346}
347
reed@google.com0bb18bb2012-07-26 15:20:36 +0000348static void test_rect_isfinite(skiatest::Reporter* reporter) {
349 const SkScalar inf = SK_ScalarInfinity;
350 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000351
reed@google.com0bb18bb2012-07-26 15:20:36 +0000352 SkRect r;
353 r.setEmpty();
354 REPORTER_ASSERT(reporter, r.isFinite());
355 r.set(0, 0, inf, -inf);
356 REPORTER_ASSERT(reporter, !r.isFinite());
357 r.set(0, 0, nan, 0);
358 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000359
reed@google.com0bb18bb2012-07-26 15:20:36 +0000360 SkPoint pts[] = {
361 { 0, 0 },
362 { SK_Scalar1, 0 },
363 { 0, SK_Scalar1 },
364 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000365
reed@google.com0bb18bb2012-07-26 15:20:36 +0000366 bool isFine = r.setBoundsCheck(pts, 3);
367 REPORTER_ASSERT(reporter, isFine);
368 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000369
reed@google.com0bb18bb2012-07-26 15:20:36 +0000370 pts[1].set(inf, 0);
371 isFine = r.setBoundsCheck(pts, 3);
372 REPORTER_ASSERT(reporter, !isFine);
373 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000374
reed@google.com0bb18bb2012-07-26 15:20:36 +0000375 pts[1].set(nan, 0);
376 isFine = r.setBoundsCheck(pts, 3);
377 REPORTER_ASSERT(reporter, !isFine);
378 REPORTER_ASSERT(reporter, r.isEmpty());
379}
380
381static void test_path_isfinite(skiatest::Reporter* reporter) {
382 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000383 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000384 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000385
reed@google.com0bb18bb2012-07-26 15:20:36 +0000386 SkPath path;
387 REPORTER_ASSERT(reporter, path.isFinite());
388
389 path.reset();
390 REPORTER_ASSERT(reporter, path.isFinite());
391
392 path.reset();
393 path.moveTo(SK_Scalar1, 0);
394 REPORTER_ASSERT(reporter, path.isFinite());
395
396 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000397 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000398 REPORTER_ASSERT(reporter, !path.isFinite());
399
400 path.reset();
401 path.moveTo(nan, 0);
402 REPORTER_ASSERT(reporter, !path.isFinite());
403}
404
405static void test_isfinite(skiatest::Reporter* reporter) {
406 test_rect_isfinite(reporter);
407 test_path_isfinite(reporter);
408}
409
reed@google.com744faba2012-05-29 19:54:52 +0000410// assert that we always
411// start with a moveTo
412// only have 1 moveTo
413// only have Lines after that
414// end with a single close
415// only have (at most) 1 close
416//
417static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
418 const SkPoint srcPts[], int count, bool expectClose) {
419 SkPath::RawIter iter(path);
420 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000421
422 bool firstTime = true;
423 bool foundClose = false;
424 for (;;) {
425 switch (iter.next(pts)) {
426 case SkPath::kMove_Verb:
427 REPORTER_ASSERT(reporter, firstTime);
428 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
429 srcPts++;
430 firstTime = false;
431 break;
432 case SkPath::kLine_Verb:
433 REPORTER_ASSERT(reporter, !firstTime);
434 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
435 srcPts++;
436 break;
437 case SkPath::kQuad_Verb:
438 REPORTER_ASSERT(reporter, !"unexpected quad verb");
439 break;
440 case SkPath::kCubic_Verb:
441 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
442 break;
443 case SkPath::kClose_Verb:
444 REPORTER_ASSERT(reporter, !firstTime);
445 REPORTER_ASSERT(reporter, !foundClose);
446 REPORTER_ASSERT(reporter, expectClose);
447 foundClose = true;
448 break;
449 case SkPath::kDone_Verb:
450 goto DONE;
451 }
452 }
453DONE:
454 REPORTER_ASSERT(reporter, foundClose == expectClose);
455}
456
457static void test_addPoly(skiatest::Reporter* reporter) {
458 SkPoint pts[32];
459 SkRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000460
reed@google.com744faba2012-05-29 19:54:52 +0000461 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
462 pts[i].fX = rand.nextSScalar1();
463 pts[i].fY = rand.nextSScalar1();
464 }
465
466 for (int doClose = 0; doClose <= 1; ++doClose) {
467 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
468 SkPath path;
469 path.addPoly(pts, count, SkToBool(doClose));
470 test_poly(reporter, path, pts, count, SkToBool(doClose));
471 }
472 }
473}
474
reed@google.com8b06f1a2012-05-29 12:03:46 +0000475static void test_strokerec(skiatest::Reporter* reporter) {
476 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
477 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000478
reed@google.com8b06f1a2012-05-29 12:03:46 +0000479 rec.setHairlineStyle();
480 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000481
reed@google.com8b06f1a2012-05-29 12:03:46 +0000482 rec.setStrokeStyle(SK_Scalar1, false);
483 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000484
reed@google.com8b06f1a2012-05-29 12:03:46 +0000485 rec.setStrokeStyle(SK_Scalar1, true);
486 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000487
reed@google.com8b06f1a2012-05-29 12:03:46 +0000488 rec.setStrokeStyle(0, false);
489 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000490
reed@google.com8b06f1a2012-05-29 12:03:46 +0000491 rec.setStrokeStyle(0, true);
492 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
493}
494
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000495// Set this for paths that don't have a consistent direction such as a bowtie.
496// (cheapComputeDirection is not expected to catch these.)
497static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
498
499static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
500 SkPath::Direction expected) {
501 if (expected == kDontCheckDir) {
502 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000503 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000504 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
505
506 SkPath::Direction dir;
507 if (copy.cheapComputeDirection(&dir)) {
508 REPORTER_ASSERT(reporter, dir == expected);
509 } else {
510 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
511 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000512}
513
reed@google.com3e71a882012-01-10 18:44:37 +0000514static void test_direction(skiatest::Reporter* reporter) {
515 size_t i;
516 SkPath path;
517 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
518 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
519 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000520 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000521
522 static const char* gDegen[] = {
523 "M 10 10",
524 "M 10 10 M 20 20",
525 "M 10 10 L 20 20",
526 "M 10 10 L 10 10 L 10 10",
527 "M 10 10 Q 10 10 10 10",
528 "M 10 10 C 10 10 10 10 10 10",
529 };
530 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
531 path.reset();
532 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
533 REPORTER_ASSERT(reporter, valid);
534 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
535 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000536
reed@google.com3e71a882012-01-10 18:44:37 +0000537 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000538 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000539 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000540 "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 +0000541 // rect with top two corners replaced by cubics with identical middle
542 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000543 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
544 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000545 };
546 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
547 path.reset();
548 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
549 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000550 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000551 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000552
reed@google.com3e71a882012-01-10 18:44:37 +0000553 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000554 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000555 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000556 "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 +0000557 // rect with top two corners replaced by cubics with identical middle
558 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000559 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
560 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000561 };
562 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
563 path.reset();
564 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
565 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000566 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000567 }
reed@google.comac8543f2012-01-30 20:51:25 +0000568
569 // Test two donuts, each wound a different direction. Only the outer contour
570 // determines the cheap direction
571 path.reset();
572 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
573 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000574 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000575
reed@google.comac8543f2012-01-30 20:51:25 +0000576 path.reset();
577 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
578 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000579 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000580
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000581#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000582 // triangle with one point really far from the origin.
583 path.reset();
584 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000585 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
586 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
587 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000588 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000589#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000590}
591
reed@google.comffdb0182011-11-14 19:29:14 +0000592static void add_rect(SkPath* path, const SkRect& r) {
593 path->moveTo(r.fLeft, r.fTop);
594 path->lineTo(r.fRight, r.fTop);
595 path->lineTo(r.fRight, r.fBottom);
596 path->lineTo(r.fLeft, r.fBottom);
597 path->close();
598}
599
600static void test_bounds(skiatest::Reporter* reporter) {
601 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000602 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
603 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
604 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
605 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000606 };
607
608 SkPath path0, path1;
609 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
610 path0.addRect(rects[i]);
611 add_rect(&path1, rects[i]);
612 }
613
614 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
615}
616
reed@google.com55b5f4b2011-09-07 12:23:41 +0000617static void stroke_cubic(const SkPoint pts[4]) {
618 SkPath path;
619 path.moveTo(pts[0]);
620 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000621
reed@google.com55b5f4b2011-09-07 12:23:41 +0000622 SkPaint paint;
623 paint.setStyle(SkPaint::kStroke_Style);
624 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000625
reed@google.com55b5f4b2011-09-07 12:23:41 +0000626 SkPath fill;
627 paint.getFillPath(path, &fill);
628}
629
630// just ensure this can run w/o any SkASSERTS firing in the debug build
631// we used to assert due to differences in how we determine a degenerate vector
632// but that was fixed with the introduction of SkPoint::CanNormalize
633static void stroke_tiny_cubic() {
634 SkPoint p0[] = {
635 { 372.0f, 92.0f },
636 { 372.0f, 92.0f },
637 { 372.0f, 92.0f },
638 { 372.0f, 92.0f },
639 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000640
reed@google.com55b5f4b2011-09-07 12:23:41 +0000641 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000642
reed@google.com55b5f4b2011-09-07 12:23:41 +0000643 SkPoint p1[] = {
644 { 372.0f, 92.0f },
645 { 372.0007f, 92.000755f },
646 { 371.99927f, 92.003922f },
647 { 371.99826f, 92.003899f },
648 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000649
reed@google.com55b5f4b2011-09-07 12:23:41 +0000650 stroke_cubic(p1);
651}
652
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000653static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
654 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000655 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000656 SkPoint mv;
657 SkPoint pts[4];
658 SkPath::Verb v;
659 int nMT = 0;
660 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000661 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000662 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
663 switch (v) {
664 case SkPath::kMove_Verb:
665 mv = pts[0];
666 ++nMT;
667 break;
668 case SkPath::kClose_Verb:
669 REPORTER_ASSERT(reporter, mv == pts[0]);
670 ++nCL;
671 break;
672 default:
673 break;
674 }
675 }
676 // if we force a close on the interator we should have a close
677 // for every moveTo
678 REPORTER_ASSERT(reporter, !i || nMT == nCL);
679 }
680}
681
682static void test_close(skiatest::Reporter* reporter) {
683 SkPath closePt;
684 closePt.moveTo(0, 0);
685 closePt.close();
686 check_close(reporter, closePt);
687
688 SkPath openPt;
689 openPt.moveTo(0, 0);
690 check_close(reporter, openPt);
691
692 SkPath empty;
693 check_close(reporter, empty);
694 empty.close();
695 check_close(reporter, empty);
696
697 SkPath rect;
698 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
699 check_close(reporter, rect);
700 rect.close();
701 check_close(reporter, rect);
702
703 SkPath quad;
704 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
705 check_close(reporter, quad);
706 quad.close();
707 check_close(reporter, quad);
708
709 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000710 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000711 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
712 check_close(reporter, cubic);
713 cubic.close();
714 check_close(reporter, cubic);
715
716 SkPath line;
717 line.moveTo(SK_Scalar1, SK_Scalar1);
718 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
719 check_close(reporter, line);
720 line.close();
721 check_close(reporter, line);
722
723 SkPath rect2;
724 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
725 rect2.close();
726 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
727 check_close(reporter, rect2);
728 rect2.close();
729 check_close(reporter, rect2);
730
731 SkPath oval3;
732 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
733 oval3.close();
734 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
735 check_close(reporter, oval3);
736 oval3.close();
737 check_close(reporter, oval3);
738
739 SkPath moves;
740 moves.moveTo(SK_Scalar1, SK_Scalar1);
741 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
742 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
743 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
744 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000745
746 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000747}
748
reed@google.com7c424812011-05-15 04:38:34 +0000749static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
750 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000751 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
752 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000753 REPORTER_ASSERT(reporter, c == expected);
754}
755
756static void test_convexity2(skiatest::Reporter* reporter) {
757 SkPath pt;
758 pt.moveTo(0, 0);
759 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000760 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000761 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000762
reed@google.com7c424812011-05-15 04:38:34 +0000763 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000764 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
765 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000766 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000767 check_convexity(reporter, line, SkPath::kConvex_Convexity);
768 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000769
reed@google.com7c424812011-05-15 04:38:34 +0000770 SkPath triLeft;
771 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000772 triLeft.lineTo(SK_Scalar1, 0);
773 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000774 triLeft.close();
775 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000776 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000777
reed@google.com7c424812011-05-15 04:38:34 +0000778 SkPath triRight;
779 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000780 triRight.lineTo(-SK_Scalar1, 0);
781 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000782 triRight.close();
783 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000784 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000785
reed@google.com7c424812011-05-15 04:38:34 +0000786 SkPath square;
787 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000788 square.lineTo(SK_Scalar1, 0);
789 square.lineTo(SK_Scalar1, SK_Scalar1);
790 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000791 square.close();
792 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000793 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000794
reed@google.com7c424812011-05-15 04:38:34 +0000795 SkPath redundantSquare;
796 redundantSquare.moveTo(0, 0);
797 redundantSquare.lineTo(0, 0);
798 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000799 redundantSquare.lineTo(SK_Scalar1, 0);
800 redundantSquare.lineTo(SK_Scalar1, 0);
801 redundantSquare.lineTo(SK_Scalar1, 0);
802 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
803 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
804 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
805 redundantSquare.lineTo(0, SK_Scalar1);
806 redundantSquare.lineTo(0, SK_Scalar1);
807 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000808 redundantSquare.close();
809 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000810 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000811
reed@google.com7c424812011-05-15 04:38:34 +0000812 SkPath bowTie;
813 bowTie.moveTo(0, 0);
814 bowTie.lineTo(0, 0);
815 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000816 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
817 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
818 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
819 bowTie.lineTo(SK_Scalar1, 0);
820 bowTie.lineTo(SK_Scalar1, 0);
821 bowTie.lineTo(SK_Scalar1, 0);
822 bowTie.lineTo(0, SK_Scalar1);
823 bowTie.lineTo(0, SK_Scalar1);
824 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000825 bowTie.close();
826 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000827 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000828
reed@google.com7c424812011-05-15 04:38:34 +0000829 SkPath spiral;
830 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000831 spiral.lineTo(100*SK_Scalar1, 0);
832 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
833 spiral.lineTo(0, 100*SK_Scalar1);
834 spiral.lineTo(0, 50*SK_Scalar1);
835 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
836 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000837 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000838 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000839 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000840
reed@google.com7c424812011-05-15 04:38:34 +0000841 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000842 dent.moveTo(0, 0);
843 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
844 dent.lineTo(0, 100*SK_Scalar1);
845 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
846 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000847 dent.close();
848 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000849 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000850}
851
reed@android.com6b82d1a2009-06-03 02:35:01 +0000852static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
853 const SkRect& bounds) {
854 REPORTER_ASSERT(reporter, p.isConvex());
855 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000856
reed@android.com6b82d1a2009-06-03 02:35:01 +0000857 SkPath p2(p);
858 REPORTER_ASSERT(reporter, p2.isConvex());
859 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
860
861 SkPath other;
862 other.swap(p2);
863 REPORTER_ASSERT(reporter, other.isConvex());
864 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
865}
866
reed@google.com04863fa2011-05-15 04:08:24 +0000867static void setFromString(SkPath* path, const char str[]) {
868 bool first = true;
869 while (str) {
870 SkScalar x, y;
871 str = SkParse::FindScalar(str, &x);
872 if (NULL == str) {
873 break;
874 }
875 str = SkParse::FindScalar(str, &y);
876 SkASSERT(str);
877 if (first) {
878 path->moveTo(x, y);
879 first = false;
880 } else {
881 path->lineTo(x, y);
882 }
883 }
884}
885
886static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000887 SkPath path;
888
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000889 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000890 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000891 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000892 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000893 check_convexity(reporter, path, SkPath::kConcave_Convexity);
894
reed@google.com04863fa2011-05-15 04:08:24 +0000895 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000896 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000897 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000898 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000899
reed@google.com04863fa2011-05-15 04:08:24 +0000900 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000901 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000902 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000903 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000904
reed@google.com04863fa2011-05-15 04:08:24 +0000905 static const struct {
906 const char* fPathStr;
907 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000908 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000909 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000910 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
911 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
912 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
913 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
914 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
915 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
916 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
917 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000918 };
919
920 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
921 SkPath path;
922 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000923 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
924 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000925 }
926}
927
reed@google.com7e6c4d12012-05-10 14:05:43 +0000928static void test_isLine(skiatest::Reporter* reporter) {
929 SkPath path;
930 SkPoint pts[2];
931 const SkScalar value = SkIntToScalar(5);
932
933 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000934
reed@google.com7e6c4d12012-05-10 14:05:43 +0000935 // set some non-zero values
936 pts[0].set(value, value);
937 pts[1].set(value, value);
938 REPORTER_ASSERT(reporter, !path.isLine(pts));
939 // check that pts was untouched
940 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
941 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
942
943 const SkScalar moveX = SkIntToScalar(1);
944 const SkScalar moveY = SkIntToScalar(2);
945 SkASSERT(value != moveX && value != moveY);
946
947 path.moveTo(moveX, moveY);
948 REPORTER_ASSERT(reporter, !path.isLine(NULL));
949 REPORTER_ASSERT(reporter, !path.isLine(pts));
950 // check that pts was untouched
951 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
952 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
953
954 const SkScalar lineX = SkIntToScalar(2);
955 const SkScalar lineY = SkIntToScalar(2);
956 SkASSERT(value != lineX && value != lineY);
957
958 path.lineTo(lineX, lineY);
959 REPORTER_ASSERT(reporter, path.isLine(NULL));
960
961 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
962 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
963 REPORTER_ASSERT(reporter, path.isLine(pts));
964 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
965 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
966
967 path.lineTo(0, 0); // too many points/verbs
968 REPORTER_ASSERT(reporter, !path.isLine(NULL));
969 REPORTER_ASSERT(reporter, !path.isLine(pts));
970 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
971 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
972}
973
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000974static void test_conservativelyContains(skiatest::Reporter* reporter) {
975 SkPath path;
976
977 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
978 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
979
980 // A circle that bounds kBaseRect (with a significant amount of slop)
981 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
982 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
983 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
984
985 // round-rect radii
986 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +0000987
caryclark@google.com56f233a2012-11-19 13:06:06 +0000988 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000989 SkRect fQueryRect;
990 bool fInRect;
991 bool fInCircle;
992 bool fInRR;
993 } kQueries[] = {
994 {kBaseRect, true, true, false},
995
996 // rect well inside of kBaseRect
997 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
998 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
999 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1000 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1001 true, true, true},
1002
1003 // rects with edges off by one from kBaseRect's edges
1004 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1005 kBaseRect.width(), kBaseRect.height() + 1),
1006 false, true, false},
1007 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1008 kBaseRect.width() + 1, kBaseRect.height()),
1009 false, true, false},
1010 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1011 kBaseRect.width() + 1, kBaseRect.height() + 1),
1012 false, true, false},
1013 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1014 kBaseRect.width(), kBaseRect.height()),
1015 false, true, false},
1016 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1017 kBaseRect.width(), kBaseRect.height()),
1018 false, true, false},
1019 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1020 kBaseRect.width() + 2, kBaseRect.height()),
1021 false, true, false},
1022 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1023 kBaseRect.width() + 2, kBaseRect.height()),
1024 false, true, false},
1025
1026 // zero-w/h rects at each corner of kBaseRect
1027 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1028 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1029 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1030 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1031
1032 // far away rect
1033 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1034 SkIntToScalar(10), SkIntToScalar(10)),
1035 false, false, false},
1036
1037 // very large rect containing kBaseRect
1038 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1039 kBaseRect.fTop - 5 * kBaseRect.height(),
1040 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1041 false, false, false},
1042
1043 // skinny rect that spans same y-range as kBaseRect
1044 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1045 SkIntToScalar(1), kBaseRect.height()),
1046 true, true, true},
1047
1048 // short rect that spans same x-range as kBaseRect
1049 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1050 true, true, true},
1051
1052 // skinny rect that spans slightly larger y-range than kBaseRect
1053 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1054 SkIntToScalar(1), kBaseRect.height() + 1),
1055 false, true, false},
1056
1057 // short rect that spans slightly larger x-range than kBaseRect
1058 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1059 kBaseRect.width() + 1, SkScalar(1)),
1060 false, true, false},
1061 };
1062
1063 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001064 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001065 SkRect qRect = kQueries[q].fQueryRect;
1066 if (inv & 0x1) {
1067 SkTSwap(qRect.fLeft, qRect.fRight);
1068 }
1069 if (inv & 0x2) {
1070 SkTSwap(qRect.fTop, qRect.fBottom);
1071 }
1072 for (int d = 0; d < 2; ++d) {
1073 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1074 path.reset();
1075 path.addRect(kBaseRect, dir);
1076 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1077 path.conservativelyContainsRect(qRect));
1078
1079 path.reset();
1080 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1081 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1082 path.conservativelyContainsRect(qRect));
1083
1084 path.reset();
1085 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1086 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1087 path.conservativelyContainsRect(qRect));
1088 }
1089 // Slightly non-convex shape, shouldn't contain any rects.
1090 path.reset();
1091 path.moveTo(0, 0);
1092 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1093 path.lineTo(SkIntToScalar(100), 0);
1094 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1095 path.lineTo(0, SkIntToScalar(100));
1096 path.close();
1097 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1098 }
1099 }
1100
1101 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1102 path.reset();
1103 path.moveTo(0, 0);
1104 path.lineTo(SkIntToScalar(100), 0);
1105 path.lineTo(0, SkIntToScalar(100));
1106
1107 // inside, on along top edge
1108 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1109 SkIntToScalar(10),
1110 SkIntToScalar(10))));
1111 // above
1112 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1113 SkRect::MakeXYWH(SkIntToScalar(50),
1114 SkIntToScalar(-10),
1115 SkIntToScalar(10),
1116 SkIntToScalar(10))));
1117 // to the left
1118 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1119 SkIntToScalar(5),
1120 SkIntToScalar(5),
1121 SkIntToScalar(5))));
1122
1123 // outside the diagonal edge
1124 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1125 SkIntToScalar(200),
1126 SkIntToScalar(20),
1127 SkIntToScalar(5))));
1128}
1129
caryclark@google.comf1316942011-07-26 19:54:45 +00001130// Simple isRect test is inline TestPath, below.
1131// test_isRect provides more extensive testing.
1132static void test_isRect(skiatest::Reporter* reporter) {
1133 // passing tests (all moveTo / lineTo...
1134 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1135 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1136 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1137 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1138 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1139 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1140 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1141 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1142 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1143 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1144 {1, 0}, {.5f, 0}};
1145 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1146 {0, 1}, {0, .5f}};
1147 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1148 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1149 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001150 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001151
caryclark@google.comf1316942011-07-26 19:54:45 +00001152 // failing tests
1153 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1154 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1155 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1156 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1157 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1158 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1159 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1160 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001161 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1162 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1163 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001164
caryclark@google.comf1316942011-07-26 19:54:45 +00001165 // failing, no close
1166 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1167 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1168
1169 size_t testLen[] = {
1170 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1171 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001172 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001173 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001174 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001175 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001176 };
1177 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001178 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1179 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001180 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001181 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001182 SkPoint* lastPass = rf;
1183 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001184 bool fail = false;
1185 bool close = true;
1186 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1187 size_t index;
1188 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1189 SkPath path;
1190 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1191 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1192 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1193 }
1194 if (close) {
1195 path.close();
1196 }
1197 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001198 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1199
caryclark@google.com56f233a2012-11-19 13:06:06 +00001200 if (!fail) {
1201 SkRect computed, expected;
1202 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1203 REPORTER_ASSERT(reporter, path.isRect(&computed));
1204 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001205
caryclark@google.comf68154a2012-11-21 15:18:06 +00001206 bool isClosed;
1207 SkPath::Direction direction, cheapDirection;
1208 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1209 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1210 REPORTER_ASSERT(reporter, isClosed == close);
1211 REPORTER_ASSERT(reporter, direction == cheapDirection);
1212 } else {
1213 SkRect computed;
1214 computed.set(123, 456, 789, 1011);
1215 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1216 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1217 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1218
1219 bool isClosed = (bool) -1;
1220 SkPath::Direction direction = (SkPath::Direction) -1;
1221 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1222 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1223 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001224 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001225
caryclark@google.comf1316942011-07-26 19:54:45 +00001226 if (tests[testIndex] == lastPass) {
1227 fail = true;
1228 }
1229 if (tests[testIndex] == lastClose) {
1230 close = false;
1231 }
1232 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001233
caryclark@google.comf1316942011-07-26 19:54:45 +00001234 // fail, close then line
1235 SkPath path1;
1236 path1.moveTo(r1[0].fX, r1[0].fY);
1237 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1238 path1.lineTo(r1[index].fX, r1[index].fY);
1239 }
1240 path1.close();
1241 path1.lineTo(1, 0);
1242 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001243
caryclark@google.comf1316942011-07-26 19:54:45 +00001244 // fail, move in the middle
1245 path1.reset();
1246 path1.moveTo(r1[0].fX, r1[0].fY);
1247 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1248 if (index == 2) {
1249 path1.moveTo(1, .5f);
1250 }
1251 path1.lineTo(r1[index].fX, r1[index].fY);
1252 }
1253 path1.close();
1254 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1255
1256 // fail, move on the edge
1257 path1.reset();
1258 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1259 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1260 path1.lineTo(r1[index].fX, r1[index].fY);
1261 }
1262 path1.close();
1263 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001264
caryclark@google.comf1316942011-07-26 19:54:45 +00001265 // fail, quad
1266 path1.reset();
1267 path1.moveTo(r1[0].fX, r1[0].fY);
1268 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1269 if (index == 2) {
1270 path1.quadTo(1, .5f, 1, .5f);
1271 }
1272 path1.lineTo(r1[index].fX, r1[index].fY);
1273 }
1274 path1.close();
1275 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001276
caryclark@google.comf1316942011-07-26 19:54:45 +00001277 // fail, cubic
1278 path1.reset();
1279 path1.moveTo(r1[0].fX, r1[0].fY);
1280 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1281 if (index == 2) {
1282 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1283 }
1284 path1.lineTo(r1[index].fX, r1[index].fY);
1285 }
1286 path1.close();
1287 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1288}
1289
caryclark@google.com56f233a2012-11-19 13:06:06 +00001290static void test_isNestedRects(skiatest::Reporter* reporter) {
1291 // passing tests (all moveTo / lineTo...
1292 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1293 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1294 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1295 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1296 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1297 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1298 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1299 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1300 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1301 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1302 {1, 0}, {.5f, 0}};
1303 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1304 {0, 1}, {0, .5f}};
1305 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1306 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1307 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1308
1309 // failing tests
1310 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1311 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1312 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1313 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1314 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1315 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1316 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1317 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1318
1319 // failing, no close
1320 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1321 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1322
1323 size_t testLen[] = {
1324 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1325 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1326 sizeof(rd), sizeof(re),
1327 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1328 sizeof(f7), sizeof(f8),
1329 sizeof(c1), sizeof(c2)
1330 };
1331 SkPoint* tests[] = {
1332 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1333 f1, f2, f3, f4, f5, f6, f7, f8,
1334 c1, c2
1335 };
1336 const SkPoint* lastPass = re;
1337 const SkPoint* lastClose = f8;
1338 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1339 size_t index;
1340 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1341 bool fail = false;
1342 bool close = true;
1343 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1344 SkPath path;
1345 if (rectFirst) {
1346 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1347 }
1348 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1349 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1350 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1351 }
1352 if (close) {
1353 path.close();
1354 }
1355 if (!rectFirst) {
1356 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1357 }
1358 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1359 if (!fail) {
1360 SkRect expected[2], computed[2];
1361 SkRect testBounds;
1362 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1363 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1364 expected[1] = testBounds;
1365 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1366 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1367 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1368 }
1369 if (tests[testIndex] == lastPass) {
1370 fail = true;
1371 }
1372 if (tests[testIndex] == lastClose) {
1373 close = false;
1374 }
1375 }
1376
1377 // fail, close then line
1378 SkPath path1;
1379 if (rectFirst) {
1380 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1381 }
1382 path1.moveTo(r1[0].fX, r1[0].fY);
1383 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1384 path1.lineTo(r1[index].fX, r1[index].fY);
1385 }
1386 path1.close();
1387 path1.lineTo(1, 0);
1388 if (!rectFirst) {
1389 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1390 }
1391 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1392
1393 // fail, move in the middle
1394 path1.reset();
1395 if (rectFirst) {
1396 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1397 }
1398 path1.moveTo(r1[0].fX, r1[0].fY);
1399 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1400 if (index == 2) {
1401 path1.moveTo(1, .5f);
1402 }
1403 path1.lineTo(r1[index].fX, r1[index].fY);
1404 }
1405 path1.close();
1406 if (!rectFirst) {
1407 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1408 }
1409 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1410
1411 // fail, move on the edge
1412 path1.reset();
1413 if (rectFirst) {
1414 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1415 }
1416 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1417 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1418 path1.lineTo(r1[index].fX, r1[index].fY);
1419 }
1420 path1.close();
1421 if (!rectFirst) {
1422 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1423 }
1424 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1425
1426 // fail, quad
1427 path1.reset();
1428 if (rectFirst) {
1429 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1430 }
1431 path1.moveTo(r1[0].fX, r1[0].fY);
1432 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1433 if (index == 2) {
1434 path1.quadTo(1, .5f, 1, .5f);
1435 }
1436 path1.lineTo(r1[index].fX, r1[index].fY);
1437 }
1438 path1.close();
1439 if (!rectFirst) {
1440 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1441 }
1442 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1443
1444 // fail, cubic
1445 path1.reset();
1446 if (rectFirst) {
1447 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1448 }
1449 path1.moveTo(r1[0].fX, r1[0].fY);
1450 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1451 if (index == 2) {
1452 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1453 }
1454 path1.lineTo(r1[index].fX, r1[index].fY);
1455 }
1456 path1.close();
1457 if (!rectFirst) {
1458 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1459 }
1460 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001461
caryclark@google.com56f233a2012-11-19 13:06:06 +00001462 // fail, not nested
1463 path1.reset();
1464 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1465 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1466 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1467 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001468
1469 // pass, stroke rect
1470 SkPath src, dst;
1471 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1472 SkPaint strokePaint;
1473 strokePaint.setStyle(SkPaint::kStroke_Style);
1474 strokePaint.setStrokeWidth(2);
1475 strokePaint.getFillPath(src, &dst);
1476 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001477}
1478
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001479static void write_and_read_back(skiatest::Reporter* reporter,
1480 const SkPath& p) {
1481 SkWriter32 writer(100);
1482 writer.writePath(p);
1483 size_t size = writer.size();
1484 SkAutoMalloc storage(size);
1485 writer.flatten(storage.get());
1486 SkReader32 reader(storage.get(), size);
1487
1488 SkPath readBack;
1489 REPORTER_ASSERT(reporter, readBack != p);
1490 reader.readPath(&readBack);
1491 REPORTER_ASSERT(reporter, readBack == p);
1492
rmistry@google.comd6176b02012-08-23 18:14:13 +00001493 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001494 p.getConvexityOrUnknown());
1495
1496 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1497
1498 const SkRect& origBounds = p.getBounds();
1499 const SkRect& readBackBounds = readBack.getBounds();
1500
1501 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1502}
1503
reed@google.com53effc52011-09-21 19:05:12 +00001504static void test_flattening(skiatest::Reporter* reporter) {
1505 SkPath p;
1506
1507 static const SkPoint pts[] = {
1508 { 0, 0 },
1509 { SkIntToScalar(10), SkIntToScalar(10) },
1510 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1511 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1512 };
1513 p.moveTo(pts[0]);
1514 p.lineTo(pts[1]);
1515 p.quadTo(pts[2], pts[3]);
1516 p.cubicTo(pts[4], pts[5], pts[6]);
1517
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001518 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001519
1520 // create a buffer that should be much larger than the path so we don't
1521 // kill our stack if writer goes too far.
1522 char buffer[1024];
1523 uint32_t size1 = p.writeToMemory(NULL);
1524 uint32_t size2 = p.writeToMemory(buffer);
1525 REPORTER_ASSERT(reporter, size1 == size2);
1526
1527 SkPath p2;
1528 uint32_t size3 = p2.readFromMemory(buffer);
1529 REPORTER_ASSERT(reporter, size1 == size3);
1530 REPORTER_ASSERT(reporter, p == p2);
1531
1532 char buffer2[1024];
1533 size3 = p2.writeToMemory(buffer2);
1534 REPORTER_ASSERT(reporter, size1 == size3);
1535 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001536
1537 // test persistence of the oval flag & convexity
1538 {
1539 SkPath oval;
1540 SkRect rect = SkRect::MakeWH(10, 10);
1541 oval.addOval(rect);
1542
1543 write_and_read_back(reporter, oval);
1544 }
reed@google.com53effc52011-09-21 19:05:12 +00001545}
1546
1547static void test_transform(skiatest::Reporter* reporter) {
1548 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001549
reed@google.com53effc52011-09-21 19:05:12 +00001550 static const SkPoint pts[] = {
1551 { 0, 0 },
1552 { SkIntToScalar(10), SkIntToScalar(10) },
1553 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1554 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1555 };
1556 p.moveTo(pts[0]);
1557 p.lineTo(pts[1]);
1558 p.quadTo(pts[2], pts[3]);
1559 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001560
reed@google.com53effc52011-09-21 19:05:12 +00001561 SkMatrix matrix;
1562 matrix.reset();
1563 p.transform(matrix, &p1);
1564 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001565
reed@google.com53effc52011-09-21 19:05:12 +00001566 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1567 p.transform(matrix, &p1);
1568 SkPoint pts1[7];
1569 int count = p1.getPoints(pts1, 7);
1570 REPORTER_ASSERT(reporter, 7 == count);
1571 for (int i = 0; i < count; ++i) {
1572 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1573 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1574 }
1575}
1576
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001577static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001578 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001579 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001580
caryclark@google.com56f233a2012-11-19 13:06:06 +00001581 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001582 const char* testPath;
1583 const size_t numResultPts;
1584 const SkRect resultBound;
1585 const SkPath::Verb* resultVerbs;
1586 const size_t numResultVerbs;
1587 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001588
schenney@chromium.org7e963602012-06-13 17:05:43 +00001589 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1590 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1591 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1592 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1593 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1594 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1595 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1596 static const SkPath::Verb resultVerbs8[] = {
1597 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1598 };
1599 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1600 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1601 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1602 static const SkPath::Verb resultVerbs12[] = {
1603 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1604 };
1605 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1606 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1607 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1608 static const SkPath::Verb resultVerbs16[] = {
1609 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1610 };
1611 static const struct zeroPathTestData gZeroLengthTests[] = {
1612 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001613 { "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 +00001614 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001615 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1616 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1617 { "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) },
1618 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1619 { "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) },
1620 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1621 { "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) },
1622 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1623 { "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) },
1624 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1625 { "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 +00001626 SK_ARRAY_COUNT(resultVerbs14)
1627 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001628 { "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) },
1629 { "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 +00001630 SK_ARRAY_COUNT(resultVerbs16)
1631 }
1632 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001633
schenney@chromium.org7e963602012-06-13 17:05:43 +00001634 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1635 p.reset();
1636 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1637 REPORTER_ASSERT(reporter, valid);
1638 REPORTER_ASSERT(reporter, !p.isEmpty());
1639 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1640 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1641 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1642 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1643 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1644 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001645 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001646}
1647
1648struct SegmentInfo {
1649 SkPath fPath;
1650 int fPointCount;
1651};
1652
reed@google.com10296cc2011-09-21 12:29:05 +00001653#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1654
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001655static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001656 SkPath p, p2;
1657
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001658 p.moveTo(0, 0);
1659 p.quadTo(100, 100, 200, 200);
1660 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1661 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001662 p2 = p;
1663 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001664 p.cubicTo(100, 100, 200, 200, 300, 300);
1665 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1666 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001667 p2 = p;
1668 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1669
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001670 p.reset();
1671 p.moveTo(0, 0);
1672 p.cubicTo(100, 100, 200, 200, 300, 300);
1673 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001674 p2 = p;
1675 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001676
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001677 REPORTER_ASSERT(reporter, !p.isEmpty());
1678}
1679
1680static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001681 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001682 SkPoint pts[4];
1683
1684 // Test an iterator with no path
1685 SkPath::Iter noPathIter;
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 setting an empty path works
1689 noPathIter.setPath(p, false);
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 that close path makes no difference for an empty path
1693 noPathIter.setPath(p, true);
1694 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001695
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001696 // Test an iterator with an initial empty path
1697 SkPath::Iter iter(p, false);
1698 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1699
1700 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001701 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001702 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1703
rmistry@google.comd6176b02012-08-23 18:14:13 +00001704
schenney@chromium.org7e963602012-06-13 17:05:43 +00001705 struct iterTestData {
1706 const char* testPath;
1707 const bool forceClose;
1708 const bool consumeDegenerates;
1709 const size_t* numResultPtsPerVerb;
1710 const SkPoint* resultPts;
1711 const SkPath::Verb* resultVerbs;
1712 const size_t numResultVerbs;
1713 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001714
schenney@chromium.org7e963602012-06-13 17:05:43 +00001715 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1716 static const SkPath::Verb resultVerbs2[] = {
1717 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1718 };
1719 static const SkPath::Verb resultVerbs3[] = {
1720 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1721 };
1722 static const SkPath::Verb resultVerbs4[] = {
1723 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1724 };
1725 static const SkPath::Verb resultVerbs5[] = {
1726 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1727 };
1728 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001729 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1730 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1731 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1732 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001733 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001734 static const SkPoint resultPts2[] = {
1735 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1736 };
1737 static const SkPoint resultPts3[] = {
1738 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1739 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1740 };
1741 static const SkPoint resultPts4[] = {
1742 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1743 };
1744 static const SkPoint resultPts5[] = {
1745 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1746 };
1747 static const struct iterTestData gIterTests[] = {
1748 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001749 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1750 { "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 +00001751 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1752 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1753 { "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) },
1754 { "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 +00001755 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1756 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1757 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1758 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1759 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1760 { "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 +00001761 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001762
schenney@chromium.org7e963602012-06-13 17:05:43 +00001763 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1764 p.reset();
1765 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1766 REPORTER_ASSERT(reporter, valid);
1767 iter.setPath(p, gIterTests[i].forceClose);
1768 int j = 0, l = 0;
1769 do {
1770 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1771 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1772 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1773 }
1774 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1775 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1776 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001777
1778 // The GM degeneratesegments.cpp test is more extensive
1779}
1780
1781static void test_raw_iter(skiatest::Reporter* reporter) {
1782 SkPath p;
1783 SkPoint pts[4];
1784
1785 // Test an iterator with no path
1786 SkPath::RawIter noPathIter;
1787 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1788 // Test that setting an empty path works
1789 noPathIter.setPath(p);
1790 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001791
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001792 // Test an iterator with an initial empty path
1793 SkPath::RawIter iter(p);
1794 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1795
1796 // Test that a move-only path returns the move.
1797 p.moveTo(SK_Scalar1, 0);
1798 iter.setPath(p);
1799 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1800 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1801 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1802 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1803
1804 // No matter how many moves we add, we should get them all back
1805 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1806 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1807 iter.setPath(p);
1808 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1809 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1810 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1811 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1812 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1813 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1814 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1815 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1816 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1817 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1818
1819 // Initial close is never ever stored
1820 p.reset();
1821 p.close();
1822 iter.setPath(p);
1823 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1824
1825 // Move/close sequences
1826 p.reset();
1827 p.close(); // Not stored, no purpose
1828 p.moveTo(SK_Scalar1, 0);
1829 p.close();
1830 p.close(); // Not stored, no purpose
1831 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1832 p.close();
1833 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1834 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1835 p.close();
1836 iter.setPath(p);
1837 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1838 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1839 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1840 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1841 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1842 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1843 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1844 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1845 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1846 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1847 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1848 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1849 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1850 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1851 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1852 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1853 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1854 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1855 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1856 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1857 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1859
1860 // Generate random paths and verify
1861 SkPoint randomPts[25];
1862 for (int i = 0; i < 5; ++i) {
1863 for (int j = 0; j < 5; ++j) {
1864 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1865 }
1866 }
1867
1868 // Max of 10 segments, max 3 points per segment
1869 SkRandom rand(9876543);
1870 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001871 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001872 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001873
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001874 for (int i = 0; i < 500; ++i) {
1875 p.reset();
1876 bool lastWasClose = true;
1877 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001878 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001879 int numPoints = 0;
1880 int numVerbs = (rand.nextU() >> 16) % 10;
1881 int numIterVerbs = 0;
1882 for (int j = 0; j < numVerbs; ++j) {
1883 do {
1884 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1885 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001886 switch (nextVerb) {
1887 case SkPath::kMove_Verb:
1888 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1889 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001890 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001891 numPoints += 1;
1892 lastWasClose = false;
1893 haveMoveTo = true;
1894 break;
1895 case SkPath::kLine_Verb:
1896 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001897 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001898 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1899 haveMoveTo = true;
1900 }
1901 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1902 p.lineTo(expectedPts[numPoints]);
1903 numPoints += 1;
1904 lastWasClose = false;
1905 break;
1906 case SkPath::kQuad_Verb:
1907 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001908 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001909 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1910 haveMoveTo = true;
1911 }
1912 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1913 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1914 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1915 numPoints += 2;
1916 lastWasClose = false;
1917 break;
1918 case SkPath::kCubic_Verb:
1919 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001920 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001921 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1922 haveMoveTo = true;
1923 }
1924 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1925 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1926 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1927 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1928 expectedPts[numPoints + 2]);
1929 numPoints += 3;
1930 lastWasClose = false;
1931 break;
1932 case SkPath::kClose_Verb:
1933 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001934 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001935 lastWasClose = true;
1936 break;
1937 default:;
1938 }
1939 expectedVerbs[numIterVerbs++] = nextVerb;
1940 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001941
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001942 iter.setPath(p);
1943 numVerbs = numIterVerbs;
1944 numIterVerbs = 0;
1945 int numIterPts = 0;
1946 SkPoint lastMoveTo;
1947 SkPoint lastPt;
1948 lastMoveTo.set(0, 0);
1949 lastPt.set(0, 0);
1950 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1951 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1952 numIterVerbs++;
1953 switch (nextVerb) {
1954 case SkPath::kMove_Verb:
1955 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1956 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1957 lastPt = lastMoveTo = pts[0];
1958 numIterPts += 1;
1959 break;
1960 case SkPath::kLine_Verb:
1961 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1962 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1963 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1964 lastPt = pts[1];
1965 numIterPts += 1;
1966 break;
1967 case SkPath::kQuad_Verb:
1968 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1969 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1970 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1971 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1972 lastPt = pts[2];
1973 numIterPts += 2;
1974 break;
1975 case SkPath::kCubic_Verb:
1976 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1977 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1978 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1979 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1980 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1981 lastPt = pts[3];
1982 numIterPts += 3;
1983 break;
1984 case SkPath::kClose_Verb:
1985 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1986 lastPt = lastMoveTo;
1987 break;
1988 default:;
1989 }
1990 }
1991 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1992 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1993 }
1994}
1995
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001996static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001997 const SkPath& path,
1998 bool expectedCircle,
1999 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002000 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002001 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2002 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002003
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002004 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002005 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2006 }
2007}
2008
2009static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002010 const SkPath& path,
2011 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002012 SkPath tmp;
2013
2014 SkMatrix m;
2015 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2016 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002017 // this matrix reverses the direction.
2018 if (SkPath::kCCW_Direction == dir) {
2019 dir = SkPath::kCW_Direction;
2020 } else {
2021 SkASSERT(SkPath::kCW_Direction == dir);
2022 dir = SkPath::kCCW_Direction;
2023 }
2024 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002025}
2026
2027static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002028 const SkPath& path,
2029 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002030 SkPath tmp;
2031
2032 // translate at small offset
2033 SkMatrix m;
2034 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2035 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002036 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002037
2038 tmp.reset();
2039 m.reset();
2040
2041 // translate at a relatively big offset
2042 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2043 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002044 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002045}
2046
2047static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002048 const SkPath& path,
2049 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002050 for (int angle = 0; angle < 360; ++angle) {
2051 SkPath tmp;
2052 SkMatrix m;
2053 m.setRotate(SkIntToScalar(angle));
2054 path.transform(m, &tmp);
2055
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002056 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002057 // degrees is not an oval anymore, this can be improved. we made this
2058 // for the simplicity of our implementation.
2059 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002060 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002061 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002062 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002063 }
2064 }
2065}
2066
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002067static void test_circle_mirror_x(skiatest::Reporter* reporter,
2068 const SkPath& path,
2069 SkPath::Direction dir) {
2070 SkPath tmp;
2071 SkMatrix m;
2072 m.reset();
2073 m.setScaleX(-SK_Scalar1);
2074 path.transform(m, &tmp);
2075
2076 if (SkPath::kCW_Direction == dir) {
2077 dir = SkPath::kCCW_Direction;
2078 } else {
2079 SkASSERT(SkPath::kCCW_Direction == dir);
2080 dir = SkPath::kCW_Direction;
2081 }
2082
2083 check_for_circle(reporter, tmp, true, dir);
2084}
2085
2086static void test_circle_mirror_y(skiatest::Reporter* reporter,
2087 const SkPath& path,
2088 SkPath::Direction dir) {
2089 SkPath tmp;
2090 SkMatrix m;
2091 m.reset();
2092 m.setScaleY(-SK_Scalar1);
2093 path.transform(m, &tmp);
2094
2095 if (SkPath::kCW_Direction == dir) {
2096 dir = SkPath::kCCW_Direction;
2097 } else {
2098 SkASSERT(SkPath::kCCW_Direction == dir);
2099 dir = SkPath::kCW_Direction;
2100 }
2101
2102 check_for_circle(reporter, tmp, true, dir);
2103}
2104
2105static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2106 const SkPath& path,
2107 SkPath::Direction dir) {
2108 SkPath tmp;
2109 SkMatrix m;
2110 m.reset();
2111 m.setScaleX(-SK_Scalar1);
2112 m.setScaleY(-SK_Scalar1);
2113 path.transform(m, &tmp);
2114
2115 check_for_circle(reporter, tmp, true, dir);
2116}
2117
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002118static void test_circle_with_direction(skiatest::Reporter* reporter,
2119 SkPath::Direction dir) {
2120 SkPath path;
2121
2122 // circle at origin
2123 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002124 check_for_circle(reporter, path, true, dir);
2125 test_circle_rotate(reporter, path, dir);
2126 test_circle_translate(reporter, path, dir);
2127 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002128
2129 // circle at an offset at (10, 10)
2130 path.reset();
2131 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2132 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002133 check_for_circle(reporter, path, true, dir);
2134 test_circle_rotate(reporter, path, dir);
2135 test_circle_translate(reporter, path, dir);
2136 test_circle_skew(reporter, path, dir);
2137 test_circle_mirror_x(reporter, path, dir);
2138 test_circle_mirror_y(reporter, path, dir);
2139 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002140}
2141
2142static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2143 SkPath path;
2144 SkPath circle;
2145 SkPath rect;
2146 SkPath empty;
2147
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002148 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2149 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2150
2151 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002152 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2153 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2154
2155 SkMatrix translate;
2156 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2157
2158 // For simplicity, all the path concatenation related operations
2159 // would mark it non-circle, though in theory it's still a circle.
2160
2161 // empty + circle (translate)
2162 path = empty;
2163 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002164 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002165
2166 // circle + empty (translate)
2167 path = circle;
2168 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002169 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002170
2171 // test reverseAddPath
2172 path = circle;
2173 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002174 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002175}
2176
2177static void test_circle(skiatest::Reporter* reporter) {
2178 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2179 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2180
2181 // multiple addCircle()
2182 SkPath path;
2183 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2184 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002185 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002186
2187 // some extra lineTo() would make isOval() fail
2188 path.reset();
2189 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2190 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002191 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002192
2193 // not back to the original point
2194 path.reset();
2195 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2196 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002197 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002198
2199 test_circle_with_add_paths(reporter);
2200}
2201
2202static void test_oval(skiatest::Reporter* reporter) {
2203 SkRect rect;
2204 SkMatrix m;
2205 SkPath path;
2206
2207 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2208 path.addOval(rect);
2209
2210 REPORTER_ASSERT(reporter, path.isOval(NULL));
2211
2212 m.setRotate(SkIntToScalar(90));
2213 SkPath tmp;
2214 path.transform(m, &tmp);
2215 // an oval rotated 90 degrees is still an oval.
2216 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2217
2218 m.reset();
2219 m.setRotate(SkIntToScalar(30));
2220 tmp.reset();
2221 path.transform(m, &tmp);
2222 // an oval rotated 30 degrees is not an oval anymore.
2223 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2224
2225 // since empty path being transformed.
2226 path.reset();
2227 tmp.reset();
2228 m.reset();
2229 path.transform(m, &tmp);
2230 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2231
2232 // empty path is not an oval
2233 tmp.reset();
2234 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2235
2236 // only has moveTo()s
2237 tmp.reset();
2238 tmp.moveTo(0, 0);
2239 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2240 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2241
2242 // mimic WebKit's calling convention,
2243 // call moveTo() first and then call addOval()
2244 path.reset();
2245 path.moveTo(0, 0);
2246 path.addOval(rect);
2247 REPORTER_ASSERT(reporter, path.isOval(NULL));
2248
2249 // copy path
2250 path.reset();
2251 tmp.reset();
2252 tmp.addOval(rect);
2253 path = tmp;
2254 REPORTER_ASSERT(reporter, path.isOval(NULL));
2255}
2256
caryclark@google.com42639cd2012-06-06 12:03:39 +00002257static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002258 SkTSize<SkScalar>::Make(3,4);
2259
reed@android.com3abec1d2009-03-02 05:36:20 +00002260 SkPath p, p2;
2261 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002262
reed@android.com3abec1d2009-03-02 05:36:20 +00002263 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002264 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002265 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002266 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002267 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002268 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2269 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2270 REPORTER_ASSERT(reporter, p == p2);
2271 REPORTER_ASSERT(reporter, !(p != p2));
2272
reed@android.comd252db02009-04-01 18:31:44 +00002273 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002274
reed@android.com3abec1d2009-03-02 05:36:20 +00002275 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002276
reed@android.com6b82d1a2009-06-03 02:35:01 +00002277 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2278 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002279 // we have quads or cubics
2280 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002281 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002282
reed@android.com6b82d1a2009-06-03 02:35:01 +00002283 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002284 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002285 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002286
reed@android.com6b82d1a2009-06-03 02:35:01 +00002287 p.addOval(bounds);
2288 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002289 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002290
reed@android.com6b82d1a2009-06-03 02:35:01 +00002291 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002292 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002293 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002294 // we have only lines
2295 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002296 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002297
2298 REPORTER_ASSERT(reporter, p != p2);
2299 REPORTER_ASSERT(reporter, !(p == p2));
2300
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002301 // do getPoints and getVerbs return the right result
2302 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2303 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002304 SkPoint pts[4];
2305 int count = p.getPoints(pts, 4);
2306 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002307 uint8_t verbs[6];
2308 verbs[5] = 0xff;
2309 p.getVerbs(verbs, 5);
2310 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2311 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2312 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2313 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2314 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2315 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002316 bounds2.set(pts, 4);
2317 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002318
reed@android.com3abec1d2009-03-02 05:36:20 +00002319 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2320 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002321 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002322
reed@android.com3abec1d2009-03-02 05:36:20 +00002323 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002324 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002325 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2326 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002327
reed@android.com3abec1d2009-03-02 05:36:20 +00002328 // now force p to not be a rect
2329 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2330 p.addRect(bounds);
2331 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002332
reed@google.com7e6c4d12012-05-10 14:05:43 +00002333 test_isLine(reporter);
2334 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002335 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002336 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002337 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002338 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002339 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002340 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002341 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002342 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002343 test_flattening(reporter);
2344 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002345 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002346 test_iter(reporter);
2347 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002348 test_circle(reporter);
2349 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002350 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002351 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002352 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002353 test_isfinite_after_transform(reporter);
reed@google.com8cae8352012-09-14 15:18:41 +00002354 test_tricky_cubic(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002355 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002356 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002357 test_addrect_isfinite(reporter);
reed@google.com848148e2013-01-15 15:51:59 +00002358 test_clipped_cubic(reporter);
reed@google.com64d62952013-01-18 17:49:28 +00002359 test_crbug_170666(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002360}
2361
2362#include "TestClassDef.h"
2363DEFINE_TESTCLASS("Path", PathTestClass, TestPath)