blob: d62a8e4d314faa909d4966046a2db9f86535b923 [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.com3eff3592013-05-08 21:08:21 +000034// This used to assert in the debug build, as the edges did not all line-up.
35static void test_bad_cubic_crbug234190() {
36 SkPath path;
37 path.moveTo(13.8509f, 3.16858f);
38 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
39 -2.38991e+08f, -4.26573e+08f,
40 -2.41016e+08f, -4.30188e+08f);
41
42 SkPaint paint;
43 paint.setAntiAlias(true);
44 SkAutoTUnref<SkSurface> surface(new_surface(84, 88));
45 surface->getCanvas()->drawPath(path, paint);
46}
47
reed@google.com7a90daf2013-04-10 18:44:00 +000048static void test_bad_cubic_crbug229478() {
49 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000050 { 4595.91064f, -11596.9873f },
51 { 4597.2168f, -11595.9414f },
52 { 4598.52344f, -11594.8955f },
53 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +000054 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000055
reed@google.com7a90daf2013-04-10 18:44:00 +000056 SkPath path;
57 path.moveTo(pts[0]);
58 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000059
reed@google.com7a90daf2013-04-10 18:44:00 +000060 SkPaint paint;
61 paint.setStyle(SkPaint::kStroke_Style);
62 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000063
reed@google.com7a90daf2013-04-10 18:44:00 +000064 SkPath dst;
65 // Before the fix, this would infinite-recurse, and run out of stack
66 // because we would keep trying to subdivide a degenerate cubic segment.
67 paint.getFillPath(path, &dst, NULL);
68}
69
reed@google.com64d62952013-01-18 17:49:28 +000070static void build_path_170666(SkPath& path) {
71 path.moveTo(17.9459f, 21.6344f);
72 path.lineTo(139.545f, -47.8105f);
73 path.lineTo(139.545f, -47.8105f);
74 path.lineTo(131.07f, -47.3888f);
75 path.lineTo(131.07f, -47.3888f);
76 path.lineTo(122.586f, -46.9532f);
77 path.lineTo(122.586f, -46.9532f);
78 path.lineTo(18076.6f, 31390.9f);
79 path.lineTo(18076.6f, 31390.9f);
80 path.lineTo(18085.1f, 31390.5f);
81 path.lineTo(18085.1f, 31390.5f);
82 path.lineTo(18076.6f, 31390.9f);
83 path.lineTo(18076.6f, 31390.9f);
84 path.lineTo(17955, 31460.3f);
85 path.lineTo(17955, 31460.3f);
86 path.lineTo(17963.5f, 31459.9f);
87 path.lineTo(17963.5f, 31459.9f);
88 path.lineTo(17971.9f, 31459.5f);
89 path.lineTo(17971.9f, 31459.5f);
90 path.lineTo(17.9551f, 21.6205f);
91 path.lineTo(17.9551f, 21.6205f);
92 path.lineTo(9.47091f, 22.0561f);
93 path.lineTo(9.47091f, 22.0561f);
94 path.lineTo(17.9459f, 21.6344f);
95 path.lineTo(17.9459f, 21.6344f);
96 path.close();path.moveTo(0.995934f, 22.4779f);
97 path.lineTo(0.986725f, 22.4918f);
98 path.lineTo(0.986725f, 22.4918f);
99 path.lineTo(17955, 31460.4f);
100 path.lineTo(17955, 31460.4f);
101 path.lineTo(17971.9f, 31459.5f);
102 path.lineTo(17971.9f, 31459.5f);
103 path.lineTo(18093.6f, 31390.1f);
104 path.lineTo(18093.6f, 31390.1f);
105 path.lineTo(18093.6f, 31390);
106 path.lineTo(18093.6f, 31390);
107 path.lineTo(139.555f, -47.8244f);
108 path.lineTo(139.555f, -47.8244f);
109 path.lineTo(122.595f, -46.9671f);
110 path.lineTo(122.595f, -46.9671f);
111 path.lineTo(0.995934f, 22.4779f);
112 path.lineTo(0.995934f, 22.4779f);
113 path.close();
114 path.moveTo(5.43941f, 25.5223f);
115 path.lineTo(798267, -28871.1f);
116 path.lineTo(798267, -28871.1f);
117 path.lineTo(3.12512e+06f, -113102);
118 path.lineTo(3.12512e+06f, -113102);
119 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
120 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
121 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
122 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
123 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
124 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
125 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
126 path.lineTo(2.78271e+08f, -1.00733e+07f);
127 path.lineTo(2.78271e+08f, -1.00733e+07f);
128 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
129 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
130 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
131 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
132 path.lineTo(2.77473e+08f, -1.00444e+07f);
133 path.lineTo(2.77473e+08f, -1.00444e+07f);
134 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
135 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
136 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
137 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
138 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
139 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
140 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
141 path.lineTo(798284, -28872);
142 path.lineTo(798284, -28872);
143 path.lineTo(22.4044f, 24.6677f);
144 path.lineTo(22.4044f, 24.6677f);
145 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
146 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
147 path.close();
148}
149
150static void build_path_simple_170666(SkPath& path) {
151 path.moveTo(126.677f, 24.1591f);
152 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
153}
154
155// This used to assert in the SK_DEBUG build, as the clip step would fail with
156// too-few interations in our cubic-line intersection code. That code now runs
157// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000158static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000159 SkPath path;
160 SkPaint paint;
161 paint.setAntiAlias(true);
162
163 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000164
reed@google.com64d62952013-01-18 17:49:28 +0000165 build_path_simple_170666(path);
166 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000167
reed@google.com64d62952013-01-18 17:49:28 +0000168 build_path_170666(path);
169 surface->getCanvas()->drawPath(path, paint);
170}
171
reed@google.coma8790de2012-10-24 21:04:04 +0000172// Make sure we stay non-finite once we get there (unless we reset or rewind).
173static void test_addrect_isfinite(skiatest::Reporter* reporter) {
174 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000175
reed@google.coma8790de2012-10-24 21:04:04 +0000176 path.addRect(SkRect::MakeWH(50, 100));
177 REPORTER_ASSERT(reporter, path.isFinite());
178
179 path.moveTo(0, 0);
180 path.lineTo(SK_ScalarInfinity, 42);
181 REPORTER_ASSERT(reporter, !path.isFinite());
182
183 path.addRect(SkRect::MakeWH(50, 100));
184 REPORTER_ASSERT(reporter, !path.isFinite());
185
186 path.reset();
187 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000188
reed@google.coma8790de2012-10-24 21:04:04 +0000189 path.addRect(SkRect::MakeWH(50, 100));
190 REPORTER_ASSERT(reporter, path.isFinite());
191}
192
reed@google.com848148e2013-01-15 15:51:59 +0000193static void build_big_path(SkPath* path, bool reducedCase) {
194 if (reducedCase) {
195 path->moveTo(577330, 1971.72f);
196 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
197 } else {
198 path->moveTo(60.1631f, 7.70567f);
199 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
200 path->lineTo(577379, 1977.77f);
201 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
202 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
203 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
204 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
205 path->quadTo(577198, 1972, 577238, 1971.31f);
206 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
207 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
208 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
209 path->moveTo(306.718f, -32.912f);
210 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
211 }
212}
213
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000214static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000215 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
216
217 // This path used to assert, because our cubic-chopping code incorrectly
218 // moved control points after the chop. This test should be run in SK_DEBUG
219 // mode to ensure that we no long assert.
220 SkPath path;
221 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
222 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000223
reed@google.com848148e2013-01-15 15:51:59 +0000224 SkPaint paint;
225 for (int doAA = 0; doAA <= 1; ++doAA) {
226 paint.setAntiAlias(SkToBool(doAA));
227 surface->getCanvas()->drawPath(path, paint);
228 }
229 }
230}
231
reed@google.com8cae8352012-09-14 15:18:41 +0000232// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
233// which triggered an assert, from a tricky cubic. This test replicates that
234// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
235// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000236static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000237 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000238 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
239 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
240 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
241 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000242 };
243
244 SkPath path;
245 path.moveTo(pts[0]);
246 path.cubicTo(pts[1], pts[2], pts[3]);
247
248 SkPaint paint;
249 paint.setAntiAlias(true);
250
251 SkSurface* surface = new_surface(19, 130);
252 surface->getCanvas()->drawPath(path, paint);
253 surface->unref();
254}
reed@android.com3abec1d2009-03-02 05:36:20 +0000255
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000256// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
257//
258static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
259 SkPath path;
260 path.quadTo(157, 366, 286, 208);
261 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000262
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000263 SkMatrix matrix;
264 matrix.setScale(1000*1000, 1000*1000);
265
266 // Be sure that path::transform correctly updates isFinite and the bounds
267 // if the transformation overflows. The previous bug was that isFinite was
268 // set to true in this case, but the bounds were not set to empty (which
269 // they should be).
270 while (path.isFinite()) {
271 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
272 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
273 path.transform(matrix);
274 }
275 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
276
277 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
278 path.transform(matrix);
279 // we need to still be non-finite
280 REPORTER_ASSERT(reporter, !path.isFinite());
281 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
282}
283
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000284static void add_corner_arc(SkPath* path, const SkRect& rect,
285 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000286 int startAngle)
287{
288
289 SkScalar rx = SkMinScalar(rect.width(), xIn);
290 SkScalar ry = SkMinScalar(rect.height(), yIn);
291
292 SkRect arcRect;
293 arcRect.set(-rx, -ry, rx, ry);
294 switch (startAngle) {
295 case 0:
296 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
297 break;
298 case 90:
299 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
300 break;
301 case 180:
302 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
303 break;
304 case 270:
305 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
306 break;
307 default:
308 break;
309 }
310
311 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
312}
313
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000314static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000315 SkScalar xCorner, SkScalar yCorner) {
316 // we are lazy here and use the same x & y for each corner
317 add_corner_arc(path, r, xCorner, yCorner, 270);
318 add_corner_arc(path, r, xCorner, yCorner, 0);
319 add_corner_arc(path, r, xCorner, yCorner, 90);
320 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000321 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000322}
323
324// Chrome creates its own round rects with each corner possibly being different.
325// Performance will suffer if they are not convex.
326// Note: PathBench::ArbRoundRectBench performs almost exactly
327// the same test (but with drawing)
328static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000329 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000330 SkRect r;
331
332 for (int i = 0; i < 5000; ++i) {
333
robertphillips@google.com158618e2012-10-23 16:56:56 +0000334 SkScalar size = rand.nextUScalar1() * 30;
335 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000336 continue;
337 }
338 r.fLeft = rand.nextUScalar1() * 300;
339 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000340 r.fRight = r.fLeft + 2 * size;
341 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000342
343 SkPath temp;
344
345 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
346
347 REPORTER_ASSERT(reporter, temp.isConvex());
348 }
349}
350
robertphillips@google.com158618e2012-10-23 16:56:56 +0000351// Chrome will sometimes create a 0 radius round rect. The degenerate
352// quads prevent the path from being converted to a rect
353// Note: PathBench::ArbRoundRectBench performs almost exactly
354// the same test (but with drawing)
355static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000356 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000357 SkRect r;
358
359 for (int i = 0; i < 5000; ++i) {
360
361 SkScalar size = rand.nextUScalar1() * 30;
362 if (size < SK_Scalar1) {
363 continue;
364 }
365 r.fLeft = rand.nextUScalar1() * 300;
366 r.fTop = rand.nextUScalar1() * 300;
367 r.fRight = r.fLeft + 2 * size;
368 r.fBottom = r.fTop + 2 * size;
369
370 SkPath temp;
371
372 make_arb_round_rect(&temp, r, 0, 0);
373
robertphillips@google.com158618e2012-10-23 16:56:56 +0000374 SkRect result;
375 REPORTER_ASSERT(reporter, temp.isRect(&result));
376 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000377 }
378}
379
reed@google.com0bb18bb2012-07-26 15:20:36 +0000380static void test_rect_isfinite(skiatest::Reporter* reporter) {
381 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000382 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000383 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
reed@google.com0bb18bb2012-07-26 15:20:36 +0000385 SkRect r;
386 r.setEmpty();
387 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000388 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000389 REPORTER_ASSERT(reporter, !r.isFinite());
390 r.set(0, 0, nan, 0);
391 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000392
reed@google.com0bb18bb2012-07-26 15:20:36 +0000393 SkPoint pts[] = {
394 { 0, 0 },
395 { SK_Scalar1, 0 },
396 { 0, SK_Scalar1 },
397 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000398
reed@google.com0bb18bb2012-07-26 15:20:36 +0000399 bool isFine = r.setBoundsCheck(pts, 3);
400 REPORTER_ASSERT(reporter, isFine);
401 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000402
reed@google.com0bb18bb2012-07-26 15:20:36 +0000403 pts[1].set(inf, 0);
404 isFine = r.setBoundsCheck(pts, 3);
405 REPORTER_ASSERT(reporter, !isFine);
406 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000407
reed@google.com0bb18bb2012-07-26 15:20:36 +0000408 pts[1].set(nan, 0);
409 isFine = r.setBoundsCheck(pts, 3);
410 REPORTER_ASSERT(reporter, !isFine);
411 REPORTER_ASSERT(reporter, r.isEmpty());
412}
413
414static void test_path_isfinite(skiatest::Reporter* reporter) {
415 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000416 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000417 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000418
reed@google.com0bb18bb2012-07-26 15:20:36 +0000419 SkPath path;
420 REPORTER_ASSERT(reporter, path.isFinite());
421
422 path.reset();
423 REPORTER_ASSERT(reporter, path.isFinite());
424
425 path.reset();
426 path.moveTo(SK_Scalar1, 0);
427 REPORTER_ASSERT(reporter, path.isFinite());
428
429 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000430 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000431 REPORTER_ASSERT(reporter, !path.isFinite());
432
433 path.reset();
434 path.moveTo(nan, 0);
435 REPORTER_ASSERT(reporter, !path.isFinite());
436}
437
438static void test_isfinite(skiatest::Reporter* reporter) {
439 test_rect_isfinite(reporter);
440 test_path_isfinite(reporter);
441}
442
reed@google.com744faba2012-05-29 19:54:52 +0000443// assert that we always
444// start with a moveTo
445// only have 1 moveTo
446// only have Lines after that
447// end with a single close
448// only have (at most) 1 close
449//
450static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000451 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000452 SkPath::RawIter iter(path);
453 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000454
455 bool firstTime = true;
456 bool foundClose = false;
457 for (;;) {
458 switch (iter.next(pts)) {
459 case SkPath::kMove_Verb:
460 REPORTER_ASSERT(reporter, firstTime);
461 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
462 srcPts++;
463 firstTime = false;
464 break;
465 case SkPath::kLine_Verb:
466 REPORTER_ASSERT(reporter, !firstTime);
467 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
468 srcPts++;
469 break;
470 case SkPath::kQuad_Verb:
471 REPORTER_ASSERT(reporter, !"unexpected quad verb");
472 break;
473 case SkPath::kCubic_Verb:
474 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
475 break;
476 case SkPath::kClose_Verb:
477 REPORTER_ASSERT(reporter, !firstTime);
478 REPORTER_ASSERT(reporter, !foundClose);
479 REPORTER_ASSERT(reporter, expectClose);
480 foundClose = true;
481 break;
482 case SkPath::kDone_Verb:
483 goto DONE;
484 }
485 }
486DONE:
487 REPORTER_ASSERT(reporter, foundClose == expectClose);
488}
489
490static void test_addPoly(skiatest::Reporter* reporter) {
491 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000492 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000493
reed@google.com744faba2012-05-29 19:54:52 +0000494 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
495 pts[i].fX = rand.nextSScalar1();
496 pts[i].fY = rand.nextSScalar1();
497 }
498
499 for (int doClose = 0; doClose <= 1; ++doClose) {
500 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
501 SkPath path;
502 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000503 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000504 }
505 }
506}
507
reed@google.com8b06f1a2012-05-29 12:03:46 +0000508static void test_strokerec(skiatest::Reporter* reporter) {
509 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
510 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000511
reed@google.com8b06f1a2012-05-29 12:03:46 +0000512 rec.setHairlineStyle();
513 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000514
reed@google.com8b06f1a2012-05-29 12:03:46 +0000515 rec.setStrokeStyle(SK_Scalar1, false);
516 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000517
reed@google.com8b06f1a2012-05-29 12:03:46 +0000518 rec.setStrokeStyle(SK_Scalar1, true);
519 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000520
reed@google.com8b06f1a2012-05-29 12:03:46 +0000521 rec.setStrokeStyle(0, false);
522 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000523
reed@google.com8b06f1a2012-05-29 12:03:46 +0000524 rec.setStrokeStyle(0, true);
525 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
526}
527
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000528// Set this for paths that don't have a consistent direction such as a bowtie.
529// (cheapComputeDirection is not expected to catch these.)
530static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
531
532static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
533 SkPath::Direction expected) {
534 if (expected == kDontCheckDir) {
535 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000536 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000537 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
538
539 SkPath::Direction dir;
540 if (copy.cheapComputeDirection(&dir)) {
541 REPORTER_ASSERT(reporter, dir == expected);
542 } else {
543 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
544 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000545}
546
reed@google.com3e71a882012-01-10 18:44:37 +0000547static void test_direction(skiatest::Reporter* reporter) {
548 size_t i;
549 SkPath path;
550 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
551 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
552 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000553 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000554
555 static const char* gDegen[] = {
556 "M 10 10",
557 "M 10 10 M 20 20",
558 "M 10 10 L 20 20",
559 "M 10 10 L 10 10 L 10 10",
560 "M 10 10 Q 10 10 10 10",
561 "M 10 10 C 10 10 10 10 10 10",
562 };
563 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
564 path.reset();
565 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
566 REPORTER_ASSERT(reporter, valid);
567 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
568 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000569
reed@google.com3e71a882012-01-10 18:44:37 +0000570 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000571 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000572 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000573 "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 +0000574 // rect with top two corners replaced by cubics with identical middle
575 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000576 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
577 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000578 };
579 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
580 path.reset();
581 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
582 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000583 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000584 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000585
reed@google.com3e71a882012-01-10 18:44:37 +0000586 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000587 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000588 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000589 "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 +0000590 // rect with top two corners replaced by cubics with identical middle
591 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000592 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
593 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000594 };
595 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
596 path.reset();
597 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
598 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000599 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000600 }
reed@google.comac8543f2012-01-30 20:51:25 +0000601
602 // Test two donuts, each wound a different direction. Only the outer contour
603 // determines the cheap direction
604 path.reset();
605 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
606 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000607 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000608
reed@google.comac8543f2012-01-30 20:51:25 +0000609 path.reset();
610 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
611 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000612 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000613
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000614#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000615 // triangle with one point really far from the origin.
616 path.reset();
617 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000618 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
619 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
620 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000621 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000622#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000623}
624
reed@google.comffdb0182011-11-14 19:29:14 +0000625static void add_rect(SkPath* path, const SkRect& r) {
626 path->moveTo(r.fLeft, r.fTop);
627 path->lineTo(r.fRight, r.fTop);
628 path->lineTo(r.fRight, r.fBottom);
629 path->lineTo(r.fLeft, r.fBottom);
630 path->close();
631}
632
633static void test_bounds(skiatest::Reporter* reporter) {
634 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000635 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
636 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
637 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
638 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000639 };
640
641 SkPath path0, path1;
642 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
643 path0.addRect(rects[i]);
644 add_rect(&path1, rects[i]);
645 }
646
647 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
648}
649
reed@google.com55b5f4b2011-09-07 12:23:41 +0000650static void stroke_cubic(const SkPoint pts[4]) {
651 SkPath path;
652 path.moveTo(pts[0]);
653 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000654
reed@google.com55b5f4b2011-09-07 12:23:41 +0000655 SkPaint paint;
656 paint.setStyle(SkPaint::kStroke_Style);
657 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000658
reed@google.com55b5f4b2011-09-07 12:23:41 +0000659 SkPath fill;
660 paint.getFillPath(path, &fill);
661}
662
663// just ensure this can run w/o any SkASSERTS firing in the debug build
664// we used to assert due to differences in how we determine a degenerate vector
665// but that was fixed with the introduction of SkPoint::CanNormalize
666static void stroke_tiny_cubic() {
667 SkPoint p0[] = {
668 { 372.0f, 92.0f },
669 { 372.0f, 92.0f },
670 { 372.0f, 92.0f },
671 { 372.0f, 92.0f },
672 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000673
reed@google.com55b5f4b2011-09-07 12:23:41 +0000674 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000675
reed@google.com55b5f4b2011-09-07 12:23:41 +0000676 SkPoint p1[] = {
677 { 372.0f, 92.0f },
678 { 372.0007f, 92.000755f },
679 { 371.99927f, 92.003922f },
680 { 371.99826f, 92.003899f },
681 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000682
reed@google.com55b5f4b2011-09-07 12:23:41 +0000683 stroke_cubic(p1);
684}
685
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000686static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
687 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000688 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000689 SkPoint mv;
690 SkPoint pts[4];
691 SkPath::Verb v;
692 int nMT = 0;
693 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000694 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000695 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
696 switch (v) {
697 case SkPath::kMove_Verb:
698 mv = pts[0];
699 ++nMT;
700 break;
701 case SkPath::kClose_Verb:
702 REPORTER_ASSERT(reporter, mv == pts[0]);
703 ++nCL;
704 break;
705 default:
706 break;
707 }
708 }
709 // if we force a close on the interator we should have a close
710 // for every moveTo
711 REPORTER_ASSERT(reporter, !i || nMT == nCL);
712 }
713}
714
715static void test_close(skiatest::Reporter* reporter) {
716 SkPath closePt;
717 closePt.moveTo(0, 0);
718 closePt.close();
719 check_close(reporter, closePt);
720
721 SkPath openPt;
722 openPt.moveTo(0, 0);
723 check_close(reporter, openPt);
724
725 SkPath empty;
726 check_close(reporter, empty);
727 empty.close();
728 check_close(reporter, empty);
729
730 SkPath rect;
731 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
732 check_close(reporter, rect);
733 rect.close();
734 check_close(reporter, rect);
735
736 SkPath quad;
737 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
738 check_close(reporter, quad);
739 quad.close();
740 check_close(reporter, quad);
741
742 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000743 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000744 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
745 check_close(reporter, cubic);
746 cubic.close();
747 check_close(reporter, cubic);
748
749 SkPath line;
750 line.moveTo(SK_Scalar1, SK_Scalar1);
751 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
752 check_close(reporter, line);
753 line.close();
754 check_close(reporter, line);
755
756 SkPath rect2;
757 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
758 rect2.close();
759 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
760 check_close(reporter, rect2);
761 rect2.close();
762 check_close(reporter, rect2);
763
764 SkPath oval3;
765 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
766 oval3.close();
767 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
768 check_close(reporter, oval3);
769 oval3.close();
770 check_close(reporter, oval3);
771
772 SkPath moves;
773 moves.moveTo(SK_Scalar1, SK_Scalar1);
774 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
775 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
776 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
777 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000778
779 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000780}
781
reed@google.com7c424812011-05-15 04:38:34 +0000782static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
783 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000784 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
785 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000786 REPORTER_ASSERT(reporter, c == expected);
787}
788
789static void test_convexity2(skiatest::Reporter* reporter) {
790 SkPath pt;
791 pt.moveTo(0, 0);
792 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000793 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000794 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000795
reed@google.com7c424812011-05-15 04:38:34 +0000796 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000797 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
798 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000799 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000800 check_convexity(reporter, line, SkPath::kConvex_Convexity);
801 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000802
reed@google.com7c424812011-05-15 04:38:34 +0000803 SkPath triLeft;
804 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000805 triLeft.lineTo(SK_Scalar1, 0);
806 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000807 triLeft.close();
808 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000809 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000810
reed@google.com7c424812011-05-15 04:38:34 +0000811 SkPath triRight;
812 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000813 triRight.lineTo(-SK_Scalar1, 0);
814 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000815 triRight.close();
816 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000817 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000818
reed@google.com7c424812011-05-15 04:38:34 +0000819 SkPath square;
820 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000821 square.lineTo(SK_Scalar1, 0);
822 square.lineTo(SK_Scalar1, SK_Scalar1);
823 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000824 square.close();
825 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000826 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000827
reed@google.com7c424812011-05-15 04:38:34 +0000828 SkPath redundantSquare;
829 redundantSquare.moveTo(0, 0);
830 redundantSquare.lineTo(0, 0);
831 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000832 redundantSquare.lineTo(SK_Scalar1, 0);
833 redundantSquare.lineTo(SK_Scalar1, 0);
834 redundantSquare.lineTo(SK_Scalar1, 0);
835 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
836 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
837 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
838 redundantSquare.lineTo(0, SK_Scalar1);
839 redundantSquare.lineTo(0, SK_Scalar1);
840 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000841 redundantSquare.close();
842 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000843 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000844
reed@google.com7c424812011-05-15 04:38:34 +0000845 SkPath bowTie;
846 bowTie.moveTo(0, 0);
847 bowTie.lineTo(0, 0);
848 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000849 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
850 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
851 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
852 bowTie.lineTo(SK_Scalar1, 0);
853 bowTie.lineTo(SK_Scalar1, 0);
854 bowTie.lineTo(SK_Scalar1, 0);
855 bowTie.lineTo(0, SK_Scalar1);
856 bowTie.lineTo(0, SK_Scalar1);
857 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000858 bowTie.close();
859 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000860 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000861
reed@google.com7c424812011-05-15 04:38:34 +0000862 SkPath spiral;
863 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000864 spiral.lineTo(100*SK_Scalar1, 0);
865 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
866 spiral.lineTo(0, 100*SK_Scalar1);
867 spiral.lineTo(0, 50*SK_Scalar1);
868 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
869 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000870 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000871 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000872 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000873
reed@google.com7c424812011-05-15 04:38:34 +0000874 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000875 dent.moveTo(0, 0);
876 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
877 dent.lineTo(0, 100*SK_Scalar1);
878 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
879 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000880 dent.close();
881 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000882 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000883}
884
reed@android.com6b82d1a2009-06-03 02:35:01 +0000885static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
886 const SkRect& bounds) {
887 REPORTER_ASSERT(reporter, p.isConvex());
888 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000889
reed@android.com6b82d1a2009-06-03 02:35:01 +0000890 SkPath p2(p);
891 REPORTER_ASSERT(reporter, p2.isConvex());
892 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
893
894 SkPath other;
895 other.swap(p2);
896 REPORTER_ASSERT(reporter, other.isConvex());
897 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
898}
899
reed@google.com04863fa2011-05-15 04:08:24 +0000900static void setFromString(SkPath* path, const char str[]) {
901 bool first = true;
902 while (str) {
903 SkScalar x, y;
904 str = SkParse::FindScalar(str, &x);
905 if (NULL == str) {
906 break;
907 }
908 str = SkParse::FindScalar(str, &y);
909 SkASSERT(str);
910 if (first) {
911 path->moveTo(x, y);
912 first = false;
913 } else {
914 path->lineTo(x, y);
915 }
916 }
917}
918
919static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000920 SkPath path;
921
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000922 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000923 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000924 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000925 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000926 check_convexity(reporter, path, SkPath::kConcave_Convexity);
927
reed@google.com04863fa2011-05-15 04:08:24 +0000928 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000929 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000930 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000931 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000932
reed@google.com04863fa2011-05-15 04:08:24 +0000933 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000934 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000935 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000936 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000937
reed@google.com04863fa2011-05-15 04:08:24 +0000938 static const struct {
939 const char* fPathStr;
940 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000941 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000942 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000943 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
944 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
945 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
946 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
947 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
948 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
949 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
950 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000951 };
952
953 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
954 SkPath path;
955 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000956 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
957 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000958 }
959}
960
reed@google.com7e6c4d12012-05-10 14:05:43 +0000961static void test_isLine(skiatest::Reporter* reporter) {
962 SkPath path;
963 SkPoint pts[2];
964 const SkScalar value = SkIntToScalar(5);
965
966 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000967
reed@google.com7e6c4d12012-05-10 14:05:43 +0000968 // set some non-zero values
969 pts[0].set(value, value);
970 pts[1].set(value, value);
971 REPORTER_ASSERT(reporter, !path.isLine(pts));
972 // check that pts was untouched
973 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
974 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
975
976 const SkScalar moveX = SkIntToScalar(1);
977 const SkScalar moveY = SkIntToScalar(2);
978 SkASSERT(value != moveX && value != moveY);
979
980 path.moveTo(moveX, moveY);
981 REPORTER_ASSERT(reporter, !path.isLine(NULL));
982 REPORTER_ASSERT(reporter, !path.isLine(pts));
983 // check that pts was untouched
984 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
985 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
986
987 const SkScalar lineX = SkIntToScalar(2);
988 const SkScalar lineY = SkIntToScalar(2);
989 SkASSERT(value != lineX && value != lineY);
990
991 path.lineTo(lineX, lineY);
992 REPORTER_ASSERT(reporter, path.isLine(NULL));
993
994 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
995 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
996 REPORTER_ASSERT(reporter, path.isLine(pts));
997 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
998 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
999
1000 path.lineTo(0, 0); // too many points/verbs
1001 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1002 REPORTER_ASSERT(reporter, !path.isLine(pts));
1003 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1004 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1005}
1006
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001007static void test_conservativelyContains(skiatest::Reporter* reporter) {
1008 SkPath path;
1009
1010 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1011 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1012
1013 // A circle that bounds kBaseRect (with a significant amount of slop)
1014 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1015 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1016 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1017
1018 // round-rect radii
1019 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001020
caryclark@google.com56f233a2012-11-19 13:06:06 +00001021 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001022 SkRect fQueryRect;
1023 bool fInRect;
1024 bool fInCircle;
1025 bool fInRR;
1026 } kQueries[] = {
1027 {kBaseRect, true, true, false},
1028
1029 // rect well inside of kBaseRect
1030 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1031 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1032 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1033 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1034 true, true, true},
1035
1036 // rects with edges off by one from kBaseRect's edges
1037 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1038 kBaseRect.width(), kBaseRect.height() + 1),
1039 false, true, false},
1040 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1041 kBaseRect.width() + 1, kBaseRect.height()),
1042 false, true, false},
1043 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1044 kBaseRect.width() + 1, kBaseRect.height() + 1),
1045 false, true, false},
1046 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1047 kBaseRect.width(), kBaseRect.height()),
1048 false, true, false},
1049 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1050 kBaseRect.width(), kBaseRect.height()),
1051 false, true, false},
1052 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1053 kBaseRect.width() + 2, kBaseRect.height()),
1054 false, true, false},
1055 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1056 kBaseRect.width() + 2, kBaseRect.height()),
1057 false, true, false},
1058
1059 // zero-w/h rects at each corner of kBaseRect
1060 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1061 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1062 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1063 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1064
1065 // far away rect
1066 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1067 SkIntToScalar(10), SkIntToScalar(10)),
1068 false, false, false},
1069
1070 // very large rect containing kBaseRect
1071 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1072 kBaseRect.fTop - 5 * kBaseRect.height(),
1073 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1074 false, false, false},
1075
1076 // skinny rect that spans same y-range as kBaseRect
1077 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1078 SkIntToScalar(1), kBaseRect.height()),
1079 true, true, true},
1080
1081 // short rect that spans same x-range as kBaseRect
1082 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1083 true, true, true},
1084
1085 // skinny rect that spans slightly larger y-range than kBaseRect
1086 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1087 SkIntToScalar(1), kBaseRect.height() + 1),
1088 false, true, false},
1089
1090 // short rect that spans slightly larger x-range than kBaseRect
1091 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1092 kBaseRect.width() + 1, SkScalar(1)),
1093 false, true, false},
1094 };
1095
1096 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001097 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001098 SkRect qRect = kQueries[q].fQueryRect;
1099 if (inv & 0x1) {
1100 SkTSwap(qRect.fLeft, qRect.fRight);
1101 }
1102 if (inv & 0x2) {
1103 SkTSwap(qRect.fTop, qRect.fBottom);
1104 }
1105 for (int d = 0; d < 2; ++d) {
1106 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1107 path.reset();
1108 path.addRect(kBaseRect, dir);
1109 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1110 path.conservativelyContainsRect(qRect));
1111
1112 path.reset();
1113 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1114 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1115 path.conservativelyContainsRect(qRect));
1116
1117 path.reset();
1118 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1119 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1120 path.conservativelyContainsRect(qRect));
1121 }
1122 // Slightly non-convex shape, shouldn't contain any rects.
1123 path.reset();
1124 path.moveTo(0, 0);
1125 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1126 path.lineTo(SkIntToScalar(100), 0);
1127 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1128 path.lineTo(0, SkIntToScalar(100));
1129 path.close();
1130 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1131 }
1132 }
1133
1134 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1135 path.reset();
1136 path.moveTo(0, 0);
1137 path.lineTo(SkIntToScalar(100), 0);
1138 path.lineTo(0, SkIntToScalar(100));
1139
1140 // inside, on along top edge
1141 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1142 SkIntToScalar(10),
1143 SkIntToScalar(10))));
1144 // above
1145 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1146 SkRect::MakeXYWH(SkIntToScalar(50),
1147 SkIntToScalar(-10),
1148 SkIntToScalar(10),
1149 SkIntToScalar(10))));
1150 // to the left
1151 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1152 SkIntToScalar(5),
1153 SkIntToScalar(5),
1154 SkIntToScalar(5))));
1155
1156 // outside the diagonal edge
1157 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1158 SkIntToScalar(200),
1159 SkIntToScalar(20),
1160 SkIntToScalar(5))));
1161}
1162
caryclark@google.comf1316942011-07-26 19:54:45 +00001163// Simple isRect test is inline TestPath, below.
1164// test_isRect provides more extensive testing.
1165static void test_isRect(skiatest::Reporter* reporter) {
1166 // passing tests (all moveTo / lineTo...
1167 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1168 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1169 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1170 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1171 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1172 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1173 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1174 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1175 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1176 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1177 {1, 0}, {.5f, 0}};
1178 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1179 {0, 1}, {0, .5f}};
1180 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1181 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1182 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001183 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001184
caryclark@google.comf1316942011-07-26 19:54:45 +00001185 // failing tests
1186 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1187 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1188 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1189 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1190 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1191 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1192 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1193 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001194 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1195 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1196 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001197
caryclark@google.comf1316942011-07-26 19:54:45 +00001198 // failing, no close
1199 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1200 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1201
1202 size_t testLen[] = {
1203 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1204 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001205 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001206 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001207 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001208 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001209 };
1210 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001211 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1212 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001213 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001214 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001215 SkPoint* lastPass = rf;
1216 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001217 bool fail = false;
1218 bool close = true;
1219 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1220 size_t index;
1221 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1222 SkPath path;
1223 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1224 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1225 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1226 }
1227 if (close) {
1228 path.close();
1229 }
1230 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001231 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1232
caryclark@google.com56f233a2012-11-19 13:06:06 +00001233 if (!fail) {
1234 SkRect computed, expected;
1235 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1236 REPORTER_ASSERT(reporter, path.isRect(&computed));
1237 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001238
caryclark@google.comf68154a2012-11-21 15:18:06 +00001239 bool isClosed;
1240 SkPath::Direction direction, cheapDirection;
1241 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1242 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1243 REPORTER_ASSERT(reporter, isClosed == close);
1244 REPORTER_ASSERT(reporter, direction == cheapDirection);
1245 } else {
1246 SkRect computed;
1247 computed.set(123, 456, 789, 1011);
1248 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1249 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1250 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1251
1252 bool isClosed = (bool) -1;
1253 SkPath::Direction direction = (SkPath::Direction) -1;
1254 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1255 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1256 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001257 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001258
caryclark@google.comf1316942011-07-26 19:54:45 +00001259 if (tests[testIndex] == lastPass) {
1260 fail = true;
1261 }
1262 if (tests[testIndex] == lastClose) {
1263 close = false;
1264 }
1265 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001266
caryclark@google.comf1316942011-07-26 19:54:45 +00001267 // fail, close then line
1268 SkPath path1;
1269 path1.moveTo(r1[0].fX, r1[0].fY);
1270 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1271 path1.lineTo(r1[index].fX, r1[index].fY);
1272 }
1273 path1.close();
1274 path1.lineTo(1, 0);
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, move in the middle
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.moveTo(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 // fail, move on the edge
1290 path1.reset();
1291 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1292 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1293 path1.lineTo(r1[index].fX, r1[index].fY);
1294 }
1295 path1.close();
1296 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001297
caryclark@google.comf1316942011-07-26 19:54:45 +00001298 // fail, quad
1299 path1.reset();
1300 path1.moveTo(r1[0].fX, r1[0].fY);
1301 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1302 if (index == 2) {
1303 path1.quadTo(1, .5f, 1, .5f);
1304 }
1305 path1.lineTo(r1[index].fX, r1[index].fY);
1306 }
1307 path1.close();
1308 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001309
caryclark@google.comf1316942011-07-26 19:54:45 +00001310 // fail, cubic
1311 path1.reset();
1312 path1.moveTo(r1[0].fX, r1[0].fY);
1313 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1314 if (index == 2) {
1315 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1316 }
1317 path1.lineTo(r1[index].fX, r1[index].fY);
1318 }
1319 path1.close();
1320 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1321}
1322
caryclark@google.com56f233a2012-11-19 13:06:06 +00001323static void test_isNestedRects(skiatest::Reporter* reporter) {
1324 // passing tests (all moveTo / lineTo...
1325 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1326 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1327 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1328 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1329 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1330 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1331 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1332 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1333 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1334 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1335 {1, 0}, {.5f, 0}};
1336 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1337 {0, 1}, {0, .5f}};
1338 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1339 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1340 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1341
1342 // failing tests
1343 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1344 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1345 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1346 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1347 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1348 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1349 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1350 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1351
1352 // failing, no close
1353 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1354 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1355
1356 size_t testLen[] = {
1357 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1358 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1359 sizeof(rd), sizeof(re),
1360 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1361 sizeof(f7), sizeof(f8),
1362 sizeof(c1), sizeof(c2)
1363 };
1364 SkPoint* tests[] = {
1365 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1366 f1, f2, f3, f4, f5, f6, f7, f8,
1367 c1, c2
1368 };
1369 const SkPoint* lastPass = re;
1370 const SkPoint* lastClose = f8;
1371 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1372 size_t index;
1373 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1374 bool fail = false;
1375 bool close = true;
1376 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1377 SkPath path;
1378 if (rectFirst) {
1379 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1380 }
1381 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1382 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1383 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1384 }
1385 if (close) {
1386 path.close();
1387 }
1388 if (!rectFirst) {
1389 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1390 }
1391 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1392 if (!fail) {
1393 SkRect expected[2], computed[2];
1394 SkRect testBounds;
1395 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1396 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1397 expected[1] = testBounds;
1398 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1399 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1400 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1401 }
1402 if (tests[testIndex] == lastPass) {
1403 fail = true;
1404 }
1405 if (tests[testIndex] == lastClose) {
1406 close = false;
1407 }
1408 }
1409
1410 // fail, close then line
1411 SkPath path1;
1412 if (rectFirst) {
1413 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1414 }
1415 path1.moveTo(r1[0].fX, r1[0].fY);
1416 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1417 path1.lineTo(r1[index].fX, r1[index].fY);
1418 }
1419 path1.close();
1420 path1.lineTo(1, 0);
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, move in the middle
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.moveTo(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, move on the edge
1445 path1.reset();
1446 if (rectFirst) {
1447 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1448 }
1449 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1450 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1451 path1.lineTo(r1[index].fX, r1[index].fY);
1452 }
1453 path1.close();
1454 if (!rectFirst) {
1455 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1456 }
1457 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1458
1459 // fail, quad
1460 path1.reset();
1461 if (rectFirst) {
1462 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1463 }
1464 path1.moveTo(r1[0].fX, r1[0].fY);
1465 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1466 if (index == 2) {
1467 path1.quadTo(1, .5f, 1, .5f);
1468 }
1469 path1.lineTo(r1[index].fX, r1[index].fY);
1470 }
1471 path1.close();
1472 if (!rectFirst) {
1473 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1474 }
1475 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1476
1477 // fail, cubic
1478 path1.reset();
1479 if (rectFirst) {
1480 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1481 }
1482 path1.moveTo(r1[0].fX, r1[0].fY);
1483 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1484 if (index == 2) {
1485 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1486 }
1487 path1.lineTo(r1[index].fX, r1[index].fY);
1488 }
1489 path1.close();
1490 if (!rectFirst) {
1491 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1492 }
1493 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001494
caryclark@google.com56f233a2012-11-19 13:06:06 +00001495 // fail, not nested
1496 path1.reset();
1497 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1498 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1499 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1500 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001501
1502 // pass, stroke rect
1503 SkPath src, dst;
1504 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1505 SkPaint strokePaint;
1506 strokePaint.setStyle(SkPaint::kStroke_Style);
1507 strokePaint.setStrokeWidth(2);
1508 strokePaint.getFillPath(src, &dst);
1509 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001510}
1511
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001512static void write_and_read_back(skiatest::Reporter* reporter,
1513 const SkPath& p) {
1514 SkWriter32 writer(100);
1515 writer.writePath(p);
1516 size_t size = writer.size();
1517 SkAutoMalloc storage(size);
1518 writer.flatten(storage.get());
1519 SkReader32 reader(storage.get(), size);
1520
1521 SkPath readBack;
1522 REPORTER_ASSERT(reporter, readBack != p);
1523 reader.readPath(&readBack);
1524 REPORTER_ASSERT(reporter, readBack == p);
1525
rmistry@google.comd6176b02012-08-23 18:14:13 +00001526 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001527 p.getConvexityOrUnknown());
1528
1529 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1530
1531 const SkRect& origBounds = p.getBounds();
1532 const SkRect& readBackBounds = readBack.getBounds();
1533
1534 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1535}
1536
reed@google.com53effc52011-09-21 19:05:12 +00001537static void test_flattening(skiatest::Reporter* reporter) {
1538 SkPath p;
1539
1540 static const SkPoint pts[] = {
1541 { 0, 0 },
1542 { SkIntToScalar(10), SkIntToScalar(10) },
1543 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1544 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1545 };
1546 p.moveTo(pts[0]);
1547 p.lineTo(pts[1]);
1548 p.quadTo(pts[2], pts[3]);
1549 p.cubicTo(pts[4], pts[5], pts[6]);
1550
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001551 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001552
1553 // create a buffer that should be much larger than the path so we don't
1554 // kill our stack if writer goes too far.
1555 char buffer[1024];
1556 uint32_t size1 = p.writeToMemory(NULL);
1557 uint32_t size2 = p.writeToMemory(buffer);
1558 REPORTER_ASSERT(reporter, size1 == size2);
1559
1560 SkPath p2;
1561 uint32_t size3 = p2.readFromMemory(buffer);
1562 REPORTER_ASSERT(reporter, size1 == size3);
1563 REPORTER_ASSERT(reporter, p == p2);
1564
1565 char buffer2[1024];
1566 size3 = p2.writeToMemory(buffer2);
1567 REPORTER_ASSERT(reporter, size1 == size3);
1568 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001569
1570 // test persistence of the oval flag & convexity
1571 {
1572 SkPath oval;
1573 SkRect rect = SkRect::MakeWH(10, 10);
1574 oval.addOval(rect);
1575
1576 write_and_read_back(reporter, oval);
1577 }
reed@google.com53effc52011-09-21 19:05:12 +00001578}
1579
1580static void test_transform(skiatest::Reporter* reporter) {
1581 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001582
reed@google.com53effc52011-09-21 19:05:12 +00001583 static const SkPoint pts[] = {
1584 { 0, 0 },
1585 { SkIntToScalar(10), SkIntToScalar(10) },
1586 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1587 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1588 };
1589 p.moveTo(pts[0]);
1590 p.lineTo(pts[1]);
1591 p.quadTo(pts[2], pts[3]);
1592 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001593
reed@google.com53effc52011-09-21 19:05:12 +00001594 SkMatrix matrix;
1595 matrix.reset();
1596 p.transform(matrix, &p1);
1597 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001598
reed@google.com53effc52011-09-21 19:05:12 +00001599 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1600 p.transform(matrix, &p1);
1601 SkPoint pts1[7];
1602 int count = p1.getPoints(pts1, 7);
1603 REPORTER_ASSERT(reporter, 7 == count);
1604 for (int i = 0; i < count; ++i) {
1605 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1606 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1607 }
1608}
1609
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001610static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001611 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001612 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001613
caryclark@google.com56f233a2012-11-19 13:06:06 +00001614 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001615 const char* testPath;
1616 const size_t numResultPts;
1617 const SkRect resultBound;
1618 const SkPath::Verb* resultVerbs;
1619 const size_t numResultVerbs;
1620 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001621
schenney@chromium.org7e963602012-06-13 17:05:43 +00001622 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1623 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1624 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1625 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1626 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1627 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1628 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1629 static const SkPath::Verb resultVerbs8[] = {
1630 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1631 };
1632 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1633 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1634 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1635 static const SkPath::Verb resultVerbs12[] = {
1636 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1637 };
1638 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1639 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1640 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1641 static const SkPath::Verb resultVerbs16[] = {
1642 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1643 };
1644 static const struct zeroPathTestData gZeroLengthTests[] = {
1645 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001646 { "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 +00001647 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001648 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1649 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1650 { "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) },
1651 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1652 { "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) },
1653 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1654 { "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) },
1655 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1656 { "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) },
1657 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1658 { "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 +00001659 SK_ARRAY_COUNT(resultVerbs14)
1660 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001661 { "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) },
1662 { "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 +00001663 SK_ARRAY_COUNT(resultVerbs16)
1664 }
1665 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001666
schenney@chromium.org7e963602012-06-13 17:05:43 +00001667 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1668 p.reset();
1669 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1670 REPORTER_ASSERT(reporter, valid);
1671 REPORTER_ASSERT(reporter, !p.isEmpty());
1672 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1673 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1674 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1675 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1676 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1677 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001678 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001679}
1680
1681struct SegmentInfo {
1682 SkPath fPath;
1683 int fPointCount;
1684};
1685
reed@google.com10296cc2011-09-21 12:29:05 +00001686#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1687
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001688static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001689 SkPath p, p2;
1690
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001691 p.moveTo(0, 0);
1692 p.quadTo(100, 100, 200, 200);
1693 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1694 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001695 p2 = p;
1696 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001697 p.cubicTo(100, 100, 200, 200, 300, 300);
1698 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1699 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001700 p2 = p;
1701 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1702
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001703 p.reset();
1704 p.moveTo(0, 0);
1705 p.cubicTo(100, 100, 200, 200, 300, 300);
1706 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001707 p2 = p;
1708 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001709
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001710 REPORTER_ASSERT(reporter, !p.isEmpty());
1711}
1712
1713static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001714 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001715 SkPoint pts[4];
1716
1717 // Test an iterator with no path
1718 SkPath::Iter noPathIter;
1719 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001720
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001721 // Test that setting an empty path works
1722 noPathIter.setPath(p, false);
1723 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001724
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001725 // Test that close path makes no difference for an empty path
1726 noPathIter.setPath(p, true);
1727 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001728
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001729 // Test an iterator with an initial empty path
1730 SkPath::Iter iter(p, false);
1731 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1732
1733 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001734 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001735 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1736
rmistry@google.comd6176b02012-08-23 18:14:13 +00001737
schenney@chromium.org7e963602012-06-13 17:05:43 +00001738 struct iterTestData {
1739 const char* testPath;
1740 const bool forceClose;
1741 const bool consumeDegenerates;
1742 const size_t* numResultPtsPerVerb;
1743 const SkPoint* resultPts;
1744 const SkPath::Verb* resultVerbs;
1745 const size_t numResultVerbs;
1746 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001747
schenney@chromium.org7e963602012-06-13 17:05:43 +00001748 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1749 static const SkPath::Verb resultVerbs2[] = {
1750 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1751 };
1752 static const SkPath::Verb resultVerbs3[] = {
1753 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1754 };
1755 static const SkPath::Verb resultVerbs4[] = {
1756 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1757 };
1758 static const SkPath::Verb resultVerbs5[] = {
1759 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1760 };
1761 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001762 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1763 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1764 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1765 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001766 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001767 static const SkPoint resultPts2[] = {
1768 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1769 };
1770 static const SkPoint resultPts3[] = {
1771 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1772 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1773 };
1774 static const SkPoint resultPts4[] = {
1775 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1776 };
1777 static const SkPoint resultPts5[] = {
1778 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1779 };
1780 static const struct iterTestData gIterTests[] = {
1781 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001782 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1783 { "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 +00001784 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1785 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1786 { "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) },
1787 { "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 +00001788 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1789 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1790 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1791 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1792 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1793 { "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 +00001794 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001795
schenney@chromium.org7e963602012-06-13 17:05:43 +00001796 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1797 p.reset();
1798 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1799 REPORTER_ASSERT(reporter, valid);
1800 iter.setPath(p, gIterTests[i].forceClose);
1801 int j = 0, l = 0;
1802 do {
1803 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1804 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1805 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1806 }
1807 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1808 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1809 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001810
1811 // The GM degeneratesegments.cpp test is more extensive
1812}
1813
1814static void test_raw_iter(skiatest::Reporter* reporter) {
1815 SkPath p;
1816 SkPoint pts[4];
1817
1818 // Test an iterator with no path
1819 SkPath::RawIter noPathIter;
1820 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1821 // Test that setting an empty path works
1822 noPathIter.setPath(p);
1823 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001824
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001825 // Test an iterator with an initial empty path
1826 SkPath::RawIter iter(p);
1827 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1828
1829 // Test that a move-only path returns the move.
1830 p.moveTo(SK_Scalar1, 0);
1831 iter.setPath(p);
1832 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1833 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1834 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1835 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1836
1837 // No matter how many moves we add, we should get them all back
1838 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1839 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1840 iter.setPath(p);
1841 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1842 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1843 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1844 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1845 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1846 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1847 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1848 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1849 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1850 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1851
1852 // Initial close is never ever stored
1853 p.reset();
1854 p.close();
1855 iter.setPath(p);
1856 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1857
1858 // Move/close sequences
1859 p.reset();
1860 p.close(); // Not stored, no purpose
1861 p.moveTo(SK_Scalar1, 0);
1862 p.close();
1863 p.close(); // Not stored, no purpose
1864 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1865 p.close();
1866 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1867 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1868 p.close();
1869 iter.setPath(p);
1870 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1871 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1872 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1873 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1874 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1875 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1876 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1877 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1878 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1879 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1880 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1881 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1882 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1883 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1884 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1885 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1886 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1887 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1888 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1889 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1890 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1891 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1892
1893 // Generate random paths and verify
1894 SkPoint randomPts[25];
1895 for (int i = 0; i < 5; ++i) {
1896 for (int j = 0; j < 5; ++j) {
1897 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1898 }
1899 }
1900
1901 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001902 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001903 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001904 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001905 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001906
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001907 for (int i = 0; i < 500; ++i) {
1908 p.reset();
1909 bool lastWasClose = true;
1910 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001911 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001912 int numPoints = 0;
1913 int numVerbs = (rand.nextU() >> 16) % 10;
1914 int numIterVerbs = 0;
1915 for (int j = 0; j < numVerbs; ++j) {
1916 do {
1917 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1918 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001919 switch (nextVerb) {
1920 case SkPath::kMove_Verb:
1921 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1922 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001923 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001924 numPoints += 1;
1925 lastWasClose = false;
1926 haveMoveTo = true;
1927 break;
1928 case SkPath::kLine_Verb:
1929 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001930 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001931 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1932 haveMoveTo = true;
1933 }
1934 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1935 p.lineTo(expectedPts[numPoints]);
1936 numPoints += 1;
1937 lastWasClose = false;
1938 break;
1939 case SkPath::kQuad_Verb:
1940 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001941 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001942 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1943 haveMoveTo = true;
1944 }
1945 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1946 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1947 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1948 numPoints += 2;
1949 lastWasClose = false;
1950 break;
1951 case SkPath::kCubic_Verb:
1952 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001953 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001954 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1955 haveMoveTo = true;
1956 }
1957 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1958 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1959 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1960 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1961 expectedPts[numPoints + 2]);
1962 numPoints += 3;
1963 lastWasClose = false;
1964 break;
1965 case SkPath::kClose_Verb:
1966 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00001967 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001968 lastWasClose = true;
1969 break;
1970 default:;
1971 }
1972 expectedVerbs[numIterVerbs++] = nextVerb;
1973 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001974
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001975 iter.setPath(p);
1976 numVerbs = numIterVerbs;
1977 numIterVerbs = 0;
1978 int numIterPts = 0;
1979 SkPoint lastMoveTo;
1980 SkPoint lastPt;
1981 lastMoveTo.set(0, 0);
1982 lastPt.set(0, 0);
1983 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1984 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1985 numIterVerbs++;
1986 switch (nextVerb) {
1987 case SkPath::kMove_Verb:
1988 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1989 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1990 lastPt = lastMoveTo = pts[0];
1991 numIterPts += 1;
1992 break;
1993 case SkPath::kLine_Verb:
1994 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1995 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1996 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1997 lastPt = pts[1];
1998 numIterPts += 1;
1999 break;
2000 case SkPath::kQuad_Verb:
2001 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2002 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2003 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2004 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2005 lastPt = pts[2];
2006 numIterPts += 2;
2007 break;
2008 case SkPath::kCubic_Verb:
2009 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2010 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2011 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2012 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2013 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2014 lastPt = pts[3];
2015 numIterPts += 3;
2016 break;
2017 case SkPath::kClose_Verb:
2018 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2019 lastPt = lastMoveTo;
2020 break;
2021 default:;
2022 }
2023 }
2024 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2025 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2026 }
2027}
2028
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002029static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002030 const SkPath& path,
2031 bool expectedCircle,
2032 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002033 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002034 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2035 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002036
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002037 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002038 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2039 }
2040}
2041
2042static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002043 const SkPath& path,
2044 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002045 SkPath tmp;
2046
2047 SkMatrix m;
2048 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2049 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002050 // this matrix reverses the direction.
2051 if (SkPath::kCCW_Direction == dir) {
2052 dir = SkPath::kCW_Direction;
2053 } else {
2054 SkASSERT(SkPath::kCW_Direction == dir);
2055 dir = SkPath::kCCW_Direction;
2056 }
2057 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002058}
2059
2060static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002061 const SkPath& path,
2062 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002063 SkPath tmp;
2064
2065 // translate at small offset
2066 SkMatrix m;
2067 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2068 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002069 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002070
2071 tmp.reset();
2072 m.reset();
2073
2074 // translate at a relatively big offset
2075 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2076 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002077 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002078}
2079
2080static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002081 const SkPath& path,
2082 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002083 for (int angle = 0; angle < 360; ++angle) {
2084 SkPath tmp;
2085 SkMatrix m;
2086 m.setRotate(SkIntToScalar(angle));
2087 path.transform(m, &tmp);
2088
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002089 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002090 // degrees is not an oval anymore, this can be improved. we made this
2091 // for the simplicity of our implementation.
2092 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002093 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002094 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002095 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002096 }
2097 }
2098}
2099
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002100static void test_circle_mirror_x(skiatest::Reporter* reporter,
2101 const SkPath& path,
2102 SkPath::Direction dir) {
2103 SkPath tmp;
2104 SkMatrix m;
2105 m.reset();
2106 m.setScaleX(-SK_Scalar1);
2107 path.transform(m, &tmp);
2108
2109 if (SkPath::kCW_Direction == dir) {
2110 dir = SkPath::kCCW_Direction;
2111 } else {
2112 SkASSERT(SkPath::kCCW_Direction == dir);
2113 dir = SkPath::kCW_Direction;
2114 }
2115
2116 check_for_circle(reporter, tmp, true, dir);
2117}
2118
2119static void test_circle_mirror_y(skiatest::Reporter* reporter,
2120 const SkPath& path,
2121 SkPath::Direction dir) {
2122 SkPath tmp;
2123 SkMatrix m;
2124 m.reset();
2125 m.setScaleY(-SK_Scalar1);
2126 path.transform(m, &tmp);
2127
2128 if (SkPath::kCW_Direction == dir) {
2129 dir = SkPath::kCCW_Direction;
2130 } else {
2131 SkASSERT(SkPath::kCCW_Direction == dir);
2132 dir = SkPath::kCW_Direction;
2133 }
2134
2135 check_for_circle(reporter, tmp, true, dir);
2136}
2137
2138static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2139 const SkPath& path,
2140 SkPath::Direction dir) {
2141 SkPath tmp;
2142 SkMatrix m;
2143 m.reset();
2144 m.setScaleX(-SK_Scalar1);
2145 m.setScaleY(-SK_Scalar1);
2146 path.transform(m, &tmp);
2147
2148 check_for_circle(reporter, tmp, true, dir);
2149}
2150
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002151static void test_circle_with_direction(skiatest::Reporter* reporter,
2152 SkPath::Direction dir) {
2153 SkPath path;
2154
2155 // circle at origin
2156 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002157 check_for_circle(reporter, path, true, dir);
2158 test_circle_rotate(reporter, path, dir);
2159 test_circle_translate(reporter, path, dir);
2160 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002161
2162 // circle at an offset at (10, 10)
2163 path.reset();
2164 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2165 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002166 check_for_circle(reporter, path, true, dir);
2167 test_circle_rotate(reporter, path, dir);
2168 test_circle_translate(reporter, path, dir);
2169 test_circle_skew(reporter, path, dir);
2170 test_circle_mirror_x(reporter, path, dir);
2171 test_circle_mirror_y(reporter, path, dir);
2172 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002173}
2174
2175static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2176 SkPath path;
2177 SkPath circle;
2178 SkPath rect;
2179 SkPath empty;
2180
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002181 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2182 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2183
2184 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002185 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2186 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2187
2188 SkMatrix translate;
2189 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2190
2191 // For simplicity, all the path concatenation related operations
2192 // would mark it non-circle, though in theory it's still a circle.
2193
2194 // empty + circle (translate)
2195 path = empty;
2196 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002197 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002198
2199 // circle + empty (translate)
2200 path = circle;
2201 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002202 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002203
2204 // test reverseAddPath
2205 path = circle;
2206 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002207 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002208}
2209
2210static void test_circle(skiatest::Reporter* reporter) {
2211 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2212 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2213
2214 // multiple addCircle()
2215 SkPath path;
2216 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2217 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002218 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002219
2220 // some extra lineTo() would make isOval() fail
2221 path.reset();
2222 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2223 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002224 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002225
2226 // not back to the original point
2227 path.reset();
2228 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2229 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002230 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002231
2232 test_circle_with_add_paths(reporter);
2233}
2234
2235static void test_oval(skiatest::Reporter* reporter) {
2236 SkRect rect;
2237 SkMatrix m;
2238 SkPath path;
2239
2240 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2241 path.addOval(rect);
2242
2243 REPORTER_ASSERT(reporter, path.isOval(NULL));
2244
2245 m.setRotate(SkIntToScalar(90));
2246 SkPath tmp;
2247 path.transform(m, &tmp);
2248 // an oval rotated 90 degrees is still an oval.
2249 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2250
2251 m.reset();
2252 m.setRotate(SkIntToScalar(30));
2253 tmp.reset();
2254 path.transform(m, &tmp);
2255 // an oval rotated 30 degrees is not an oval anymore.
2256 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2257
2258 // since empty path being transformed.
2259 path.reset();
2260 tmp.reset();
2261 m.reset();
2262 path.transform(m, &tmp);
2263 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2264
2265 // empty path is not an oval
2266 tmp.reset();
2267 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2268
2269 // only has moveTo()s
2270 tmp.reset();
2271 tmp.moveTo(0, 0);
2272 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2273 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2274
2275 // mimic WebKit's calling convention,
2276 // call moveTo() first and then call addOval()
2277 path.reset();
2278 path.moveTo(0, 0);
2279 path.addOval(rect);
2280 REPORTER_ASSERT(reporter, path.isOval(NULL));
2281
2282 // copy path
2283 path.reset();
2284 tmp.reset();
2285 tmp.addOval(rect);
2286 path = tmp;
2287 REPORTER_ASSERT(reporter, path.isOval(NULL));
2288}
2289
caryclark@google.com42639cd2012-06-06 12:03:39 +00002290static void TestPath(skiatest::Reporter* reporter) {
reed@android.com60bc6d52010-02-11 11:09:39 +00002291 SkTSize<SkScalar>::Make(3,4);
2292
reed@android.com3abec1d2009-03-02 05:36:20 +00002293 SkPath p, p2;
2294 SkRect bounds, bounds2;
reed@android.com80e39a72009-04-02 16:59:40 +00002295
reed@android.com3abec1d2009-03-02 05:36:20 +00002296 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002297 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002298 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002299 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002300 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002301 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2302 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2303 REPORTER_ASSERT(reporter, p == p2);
2304 REPORTER_ASSERT(reporter, !(p != p2));
2305
reed@android.comd252db02009-04-01 18:31:44 +00002306 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002307
reed@android.com3abec1d2009-03-02 05:36:20 +00002308 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002309
reed@android.com6b82d1a2009-06-03 02:35:01 +00002310 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2311 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002312 // we have quads or cubics
2313 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002314 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002315
reed@android.com6b82d1a2009-06-03 02:35:01 +00002316 p.reset();
reed@google.com10296cc2011-09-21 12:29:05 +00002317 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002318 REPORTER_ASSERT(reporter, p.isEmpty());
reed@google.com10296cc2011-09-21 12:29:05 +00002319
reed@android.com6b82d1a2009-06-03 02:35:01 +00002320 p.addOval(bounds);
2321 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002322 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002323
reed@android.com6b82d1a2009-06-03 02:35:01 +00002324 p.reset();
reed@android.com3abec1d2009-03-02 05:36:20 +00002325 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002326 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002327 // we have only lines
2328 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002329 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002330
2331 REPORTER_ASSERT(reporter, p != p2);
2332 REPORTER_ASSERT(reporter, !(p == p2));
2333
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002334 // do getPoints and getVerbs return the right result
2335 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2336 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002337 SkPoint pts[4];
2338 int count = p.getPoints(pts, 4);
2339 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002340 uint8_t verbs[6];
2341 verbs[5] = 0xff;
2342 p.getVerbs(verbs, 5);
2343 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2344 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2345 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2346 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2347 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2348 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002349 bounds2.set(pts, 4);
2350 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002351
reed@android.com3abec1d2009-03-02 05:36:20 +00002352 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2353 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002354 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002355
reed@android.com3abec1d2009-03-02 05:36:20 +00002356 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002357 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002358 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2359 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002360
reed@android.com3abec1d2009-03-02 05:36:20 +00002361 // now force p to not be a rect
2362 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2363 p.addRect(bounds);
2364 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002365
reed@google.com7e6c4d12012-05-10 14:05:43 +00002366 test_isLine(reporter);
2367 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002368 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002369 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002370 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002371 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002372 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002373 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002374 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002375 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002376 test_flattening(reporter);
2377 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002378 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002379 test_iter(reporter);
2380 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002381 test_circle(reporter);
2382 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002383 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002384 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002385 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002386 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002387 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002388 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002389 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002390 test_tricky_cubic();
2391 test_clipped_cubic();
2392 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002393 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002394 test_bad_cubic_crbug234190();
reed@android.com3abec1d2009-03-02 05:36:20 +00002395}
2396
2397#include "TestClassDef.h"
2398DEFINE_TESTCLASS("Path", PathTestClass, TestPath)