blob: e33c912062228589f95e585aaa3b64771c89206b [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;
reed@google.com277c3f82013-05-31 15:17:50 +0000473 case SkPath::kConic_Verb:
474 REPORTER_ASSERT(reporter, !"unexpected conic verb");
475 break;
reed@google.com744faba2012-05-29 19:54:52 +0000476 case SkPath::kCubic_Verb:
477 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
478 break;
479 case SkPath::kClose_Verb:
480 REPORTER_ASSERT(reporter, !firstTime);
481 REPORTER_ASSERT(reporter, !foundClose);
482 REPORTER_ASSERT(reporter, expectClose);
483 foundClose = true;
484 break;
485 case SkPath::kDone_Verb:
486 goto DONE;
487 }
488 }
489DONE:
490 REPORTER_ASSERT(reporter, foundClose == expectClose);
491}
492
493static void test_addPoly(skiatest::Reporter* reporter) {
494 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000495 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000496
reed@google.com744faba2012-05-29 19:54:52 +0000497 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
498 pts[i].fX = rand.nextSScalar1();
499 pts[i].fY = rand.nextSScalar1();
500 }
501
502 for (int doClose = 0; doClose <= 1; ++doClose) {
503 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
504 SkPath path;
505 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000506 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000507 }
508 }
509}
510
reed@google.com8b06f1a2012-05-29 12:03:46 +0000511static void test_strokerec(skiatest::Reporter* reporter) {
512 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
513 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000514
reed@google.com8b06f1a2012-05-29 12:03:46 +0000515 rec.setHairlineStyle();
516 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000517
reed@google.com8b06f1a2012-05-29 12:03:46 +0000518 rec.setStrokeStyle(SK_Scalar1, false);
519 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000520
reed@google.com8b06f1a2012-05-29 12:03:46 +0000521 rec.setStrokeStyle(SK_Scalar1, true);
522 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000523
reed@google.com8b06f1a2012-05-29 12:03:46 +0000524 rec.setStrokeStyle(0, false);
525 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000526
reed@google.com8b06f1a2012-05-29 12:03:46 +0000527 rec.setStrokeStyle(0, true);
528 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
529}
530
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000531// Set this for paths that don't have a consistent direction such as a bowtie.
532// (cheapComputeDirection is not expected to catch these.)
533static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
534
535static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
536 SkPath::Direction expected) {
537 if (expected == kDontCheckDir) {
538 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000539 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000540 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
541
542 SkPath::Direction dir;
543 if (copy.cheapComputeDirection(&dir)) {
544 REPORTER_ASSERT(reporter, dir == expected);
545 } else {
546 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
547 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000548}
549
reed@google.com3e71a882012-01-10 18:44:37 +0000550static void test_direction(skiatest::Reporter* reporter) {
551 size_t i;
552 SkPath path;
553 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
554 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
555 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000556 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000557
558 static const char* gDegen[] = {
559 "M 10 10",
560 "M 10 10 M 20 20",
561 "M 10 10 L 20 20",
562 "M 10 10 L 10 10 L 10 10",
563 "M 10 10 Q 10 10 10 10",
564 "M 10 10 C 10 10 10 10 10 10",
565 };
566 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
567 path.reset();
568 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
569 REPORTER_ASSERT(reporter, valid);
570 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
571 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000572
reed@google.com3e71a882012-01-10 18:44:37 +0000573 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000574 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000575 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000576 "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 +0000577 // rect with top two corners replaced by cubics with identical middle
578 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000579 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
580 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000581 };
582 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
583 path.reset();
584 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
585 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000586 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000587 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000588
reed@google.com3e71a882012-01-10 18:44:37 +0000589 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000590 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000591 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000592 "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 +0000593 // rect with top two corners replaced by cubics with identical middle
594 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000595 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
596 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000597 };
598 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
599 path.reset();
600 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
601 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000602 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000603 }
reed@google.comac8543f2012-01-30 20:51:25 +0000604
605 // Test two donuts, each wound a different direction. Only the outer contour
606 // determines the cheap direction
607 path.reset();
608 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
609 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000610 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000611
reed@google.comac8543f2012-01-30 20:51:25 +0000612 path.reset();
613 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
614 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000615 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000616
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000617#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000618 // triangle with one point really far from the origin.
619 path.reset();
620 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000621 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
622 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
623 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000624 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000625#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000626}
627
reed@google.comffdb0182011-11-14 19:29:14 +0000628static void add_rect(SkPath* path, const SkRect& r) {
629 path->moveTo(r.fLeft, r.fTop);
630 path->lineTo(r.fRight, r.fTop);
631 path->lineTo(r.fRight, r.fBottom);
632 path->lineTo(r.fLeft, r.fBottom);
633 path->close();
634}
635
636static void test_bounds(skiatest::Reporter* reporter) {
637 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000638 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
639 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
640 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
641 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000642 };
643
644 SkPath path0, path1;
645 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
646 path0.addRect(rects[i]);
647 add_rect(&path1, rects[i]);
648 }
649
650 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
651}
652
reed@google.com55b5f4b2011-09-07 12:23:41 +0000653static void stroke_cubic(const SkPoint pts[4]) {
654 SkPath path;
655 path.moveTo(pts[0]);
656 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000657
reed@google.com55b5f4b2011-09-07 12:23:41 +0000658 SkPaint paint;
659 paint.setStyle(SkPaint::kStroke_Style);
660 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000661
reed@google.com55b5f4b2011-09-07 12:23:41 +0000662 SkPath fill;
663 paint.getFillPath(path, &fill);
664}
665
666// just ensure this can run w/o any SkASSERTS firing in the debug build
667// we used to assert due to differences in how we determine a degenerate vector
668// but that was fixed with the introduction of SkPoint::CanNormalize
669static void stroke_tiny_cubic() {
670 SkPoint p0[] = {
671 { 372.0f, 92.0f },
672 { 372.0f, 92.0f },
673 { 372.0f, 92.0f },
674 { 372.0f, 92.0f },
675 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000676
reed@google.com55b5f4b2011-09-07 12:23:41 +0000677 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000678
reed@google.com55b5f4b2011-09-07 12:23:41 +0000679 SkPoint p1[] = {
680 { 372.0f, 92.0f },
681 { 372.0007f, 92.000755f },
682 { 371.99927f, 92.003922f },
683 { 371.99826f, 92.003899f },
684 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000685
reed@google.com55b5f4b2011-09-07 12:23:41 +0000686 stroke_cubic(p1);
687}
688
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000689static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
690 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000691 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000692 SkPoint mv;
693 SkPoint pts[4];
694 SkPath::Verb v;
695 int nMT = 0;
696 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000697 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000698 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
699 switch (v) {
700 case SkPath::kMove_Verb:
701 mv = pts[0];
702 ++nMT;
703 break;
704 case SkPath::kClose_Verb:
705 REPORTER_ASSERT(reporter, mv == pts[0]);
706 ++nCL;
707 break;
708 default:
709 break;
710 }
711 }
712 // if we force a close on the interator we should have a close
713 // for every moveTo
714 REPORTER_ASSERT(reporter, !i || nMT == nCL);
715 }
716}
717
718static void test_close(skiatest::Reporter* reporter) {
719 SkPath closePt;
720 closePt.moveTo(0, 0);
721 closePt.close();
722 check_close(reporter, closePt);
723
724 SkPath openPt;
725 openPt.moveTo(0, 0);
726 check_close(reporter, openPt);
727
728 SkPath empty;
729 check_close(reporter, empty);
730 empty.close();
731 check_close(reporter, empty);
732
733 SkPath rect;
734 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
735 check_close(reporter, rect);
736 rect.close();
737 check_close(reporter, rect);
738
739 SkPath quad;
740 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
741 check_close(reporter, quad);
742 quad.close();
743 check_close(reporter, quad);
744
745 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000746 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000747 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
748 check_close(reporter, cubic);
749 cubic.close();
750 check_close(reporter, cubic);
751
752 SkPath line;
753 line.moveTo(SK_Scalar1, SK_Scalar1);
754 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
755 check_close(reporter, line);
756 line.close();
757 check_close(reporter, line);
758
759 SkPath rect2;
760 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
761 rect2.close();
762 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
763 check_close(reporter, rect2);
764 rect2.close();
765 check_close(reporter, rect2);
766
767 SkPath oval3;
768 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
769 oval3.close();
770 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
771 check_close(reporter, oval3);
772 oval3.close();
773 check_close(reporter, oval3);
774
775 SkPath moves;
776 moves.moveTo(SK_Scalar1, SK_Scalar1);
777 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
778 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
779 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
780 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000781
782 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000783}
784
reed@google.com7c424812011-05-15 04:38:34 +0000785static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
786 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000787 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
788 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000789 REPORTER_ASSERT(reporter, c == expected);
790}
791
792static void test_convexity2(skiatest::Reporter* reporter) {
793 SkPath pt;
794 pt.moveTo(0, 0);
795 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000796 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000797 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000798
reed@google.com7c424812011-05-15 04:38:34 +0000799 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000800 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
801 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000802 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000803 check_convexity(reporter, line, SkPath::kConvex_Convexity);
804 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000805
reed@google.com7c424812011-05-15 04:38:34 +0000806 SkPath triLeft;
807 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000808 triLeft.lineTo(SK_Scalar1, 0);
809 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000810 triLeft.close();
811 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000812 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000813
reed@google.com7c424812011-05-15 04:38:34 +0000814 SkPath triRight;
815 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000816 triRight.lineTo(-SK_Scalar1, 0);
817 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000818 triRight.close();
819 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000820 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000821
reed@google.com7c424812011-05-15 04:38:34 +0000822 SkPath square;
823 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000824 square.lineTo(SK_Scalar1, 0);
825 square.lineTo(SK_Scalar1, SK_Scalar1);
826 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000827 square.close();
828 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000829 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000830
reed@google.com7c424812011-05-15 04:38:34 +0000831 SkPath redundantSquare;
832 redundantSquare.moveTo(0, 0);
833 redundantSquare.lineTo(0, 0);
834 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000835 redundantSquare.lineTo(SK_Scalar1, 0);
836 redundantSquare.lineTo(SK_Scalar1, 0);
837 redundantSquare.lineTo(SK_Scalar1, 0);
838 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
839 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
840 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
841 redundantSquare.lineTo(0, SK_Scalar1);
842 redundantSquare.lineTo(0, SK_Scalar1);
843 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000844 redundantSquare.close();
845 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000846 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000847
reed@google.com7c424812011-05-15 04:38:34 +0000848 SkPath bowTie;
849 bowTie.moveTo(0, 0);
850 bowTie.lineTo(0, 0);
851 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000852 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
853 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
854 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
855 bowTie.lineTo(SK_Scalar1, 0);
856 bowTie.lineTo(SK_Scalar1, 0);
857 bowTie.lineTo(SK_Scalar1, 0);
858 bowTie.lineTo(0, SK_Scalar1);
859 bowTie.lineTo(0, SK_Scalar1);
860 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000861 bowTie.close();
862 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000863 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000864
reed@google.com7c424812011-05-15 04:38:34 +0000865 SkPath spiral;
866 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000867 spiral.lineTo(100*SK_Scalar1, 0);
868 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
869 spiral.lineTo(0, 100*SK_Scalar1);
870 spiral.lineTo(0, 50*SK_Scalar1);
871 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
872 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000873 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000874 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000875 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000876
reed@google.com7c424812011-05-15 04:38:34 +0000877 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000878 dent.moveTo(0, 0);
879 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
880 dent.lineTo(0, 100*SK_Scalar1);
881 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
882 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000883 dent.close();
884 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000885 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000886}
887
reed@android.com6b82d1a2009-06-03 02:35:01 +0000888static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
889 const SkRect& bounds) {
890 REPORTER_ASSERT(reporter, p.isConvex());
891 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000892
reed@android.com6b82d1a2009-06-03 02:35:01 +0000893 SkPath p2(p);
894 REPORTER_ASSERT(reporter, p2.isConvex());
895 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
896
897 SkPath other;
898 other.swap(p2);
899 REPORTER_ASSERT(reporter, other.isConvex());
900 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
901}
902
reed@google.com04863fa2011-05-15 04:08:24 +0000903static void setFromString(SkPath* path, const char str[]) {
904 bool first = true;
905 while (str) {
906 SkScalar x, y;
907 str = SkParse::FindScalar(str, &x);
908 if (NULL == str) {
909 break;
910 }
911 str = SkParse::FindScalar(str, &y);
912 SkASSERT(str);
913 if (first) {
914 path->moveTo(x, y);
915 first = false;
916 } else {
917 path->lineTo(x, y);
918 }
919 }
920}
921
922static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000923 SkPath path;
924
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000925 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000926 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000927 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000928 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000929 check_convexity(reporter, path, SkPath::kConcave_Convexity);
930
reed@google.com04863fa2011-05-15 04:08:24 +0000931 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000932 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000933 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000934 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000935
reed@google.com04863fa2011-05-15 04:08:24 +0000936 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000937 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000938 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000939 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000940
reed@google.com04863fa2011-05-15 04:08:24 +0000941 static const struct {
942 const char* fPathStr;
943 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000944 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000945 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000946 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
947 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
948 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
949 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
950 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
951 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
952 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
953 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000954 };
955
956 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
957 SkPath path;
958 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000959 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
960 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000961 }
962}
963
reed@google.com7e6c4d12012-05-10 14:05:43 +0000964static void test_isLine(skiatest::Reporter* reporter) {
965 SkPath path;
966 SkPoint pts[2];
967 const SkScalar value = SkIntToScalar(5);
968
969 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000970
reed@google.com7e6c4d12012-05-10 14:05:43 +0000971 // set some non-zero values
972 pts[0].set(value, value);
973 pts[1].set(value, value);
974 REPORTER_ASSERT(reporter, !path.isLine(pts));
975 // check that pts was untouched
976 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
977 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
978
979 const SkScalar moveX = SkIntToScalar(1);
980 const SkScalar moveY = SkIntToScalar(2);
981 SkASSERT(value != moveX && value != moveY);
982
983 path.moveTo(moveX, moveY);
984 REPORTER_ASSERT(reporter, !path.isLine(NULL));
985 REPORTER_ASSERT(reporter, !path.isLine(pts));
986 // check that pts was untouched
987 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
988 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
989
990 const SkScalar lineX = SkIntToScalar(2);
991 const SkScalar lineY = SkIntToScalar(2);
992 SkASSERT(value != lineX && value != lineY);
993
994 path.lineTo(lineX, lineY);
995 REPORTER_ASSERT(reporter, path.isLine(NULL));
996
997 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
998 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
999 REPORTER_ASSERT(reporter, path.isLine(pts));
1000 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1001 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1002
1003 path.lineTo(0, 0); // too many points/verbs
1004 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1005 REPORTER_ASSERT(reporter, !path.isLine(pts));
1006 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1007 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1008}
1009
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001010static void test_conservativelyContains(skiatest::Reporter* reporter) {
1011 SkPath path;
1012
1013 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1014 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1015
1016 // A circle that bounds kBaseRect (with a significant amount of slop)
1017 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1018 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1019 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1020
1021 // round-rect radii
1022 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001023
caryclark@google.com56f233a2012-11-19 13:06:06 +00001024 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001025 SkRect fQueryRect;
1026 bool fInRect;
1027 bool fInCircle;
1028 bool fInRR;
1029 } kQueries[] = {
1030 {kBaseRect, true, true, false},
1031
1032 // rect well inside of kBaseRect
1033 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1034 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1035 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1036 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1037 true, true, true},
1038
1039 // rects with edges off by one from kBaseRect's edges
1040 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1041 kBaseRect.width(), kBaseRect.height() + 1),
1042 false, true, false},
1043 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1044 kBaseRect.width() + 1, kBaseRect.height()),
1045 false, true, false},
1046 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1047 kBaseRect.width() + 1, kBaseRect.height() + 1),
1048 false, true, false},
1049 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1050 kBaseRect.width(), kBaseRect.height()),
1051 false, true, false},
1052 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1053 kBaseRect.width(), kBaseRect.height()),
1054 false, true, false},
1055 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1056 kBaseRect.width() + 2, kBaseRect.height()),
1057 false, true, false},
1058 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1059 kBaseRect.width() + 2, kBaseRect.height()),
1060 false, true, false},
1061
1062 // zero-w/h rects at each corner of kBaseRect
1063 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1064 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1065 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1066 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1067
1068 // far away rect
1069 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1070 SkIntToScalar(10), SkIntToScalar(10)),
1071 false, false, false},
1072
1073 // very large rect containing kBaseRect
1074 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1075 kBaseRect.fTop - 5 * kBaseRect.height(),
1076 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1077 false, false, false},
1078
1079 // skinny rect that spans same y-range as kBaseRect
1080 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1081 SkIntToScalar(1), kBaseRect.height()),
1082 true, true, true},
1083
1084 // short rect that spans same x-range as kBaseRect
1085 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1086 true, true, true},
1087
1088 // skinny rect that spans slightly larger y-range than kBaseRect
1089 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1090 SkIntToScalar(1), kBaseRect.height() + 1),
1091 false, true, false},
1092
1093 // short rect that spans slightly larger x-range than kBaseRect
1094 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1095 kBaseRect.width() + 1, SkScalar(1)),
1096 false, true, false},
1097 };
1098
1099 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001100 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001101 SkRect qRect = kQueries[q].fQueryRect;
1102 if (inv & 0x1) {
1103 SkTSwap(qRect.fLeft, qRect.fRight);
1104 }
1105 if (inv & 0x2) {
1106 SkTSwap(qRect.fTop, qRect.fBottom);
1107 }
1108 for (int d = 0; d < 2; ++d) {
1109 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1110 path.reset();
1111 path.addRect(kBaseRect, dir);
1112 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1113 path.conservativelyContainsRect(qRect));
1114
1115 path.reset();
1116 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1117 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1118 path.conservativelyContainsRect(qRect));
1119
1120 path.reset();
1121 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1122 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1123 path.conservativelyContainsRect(qRect));
1124 }
1125 // Slightly non-convex shape, shouldn't contain any rects.
1126 path.reset();
1127 path.moveTo(0, 0);
1128 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1129 path.lineTo(SkIntToScalar(100), 0);
1130 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1131 path.lineTo(0, SkIntToScalar(100));
1132 path.close();
1133 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1134 }
1135 }
1136
1137 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1138 path.reset();
1139 path.moveTo(0, 0);
1140 path.lineTo(SkIntToScalar(100), 0);
1141 path.lineTo(0, SkIntToScalar(100));
1142
1143 // inside, on along top edge
1144 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1145 SkIntToScalar(10),
1146 SkIntToScalar(10))));
1147 // above
1148 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1149 SkRect::MakeXYWH(SkIntToScalar(50),
1150 SkIntToScalar(-10),
1151 SkIntToScalar(10),
1152 SkIntToScalar(10))));
1153 // to the left
1154 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1155 SkIntToScalar(5),
1156 SkIntToScalar(5),
1157 SkIntToScalar(5))));
1158
1159 // outside the diagonal edge
1160 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1161 SkIntToScalar(200),
1162 SkIntToScalar(20),
1163 SkIntToScalar(5))));
commit-bot@chromium.org62df5262013-08-01 15:35:06 +00001164
1165 // same as above path and first test but with an extra moveTo.
1166 path.reset();
1167 path.moveTo(100, 100);
1168 path.moveTo(0, 0);
1169 path.lineTo(SkIntToScalar(100), 0);
1170 path.lineTo(0, SkIntToScalar(100));
1171
1172 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1173 SkIntToScalar(10),
1174 SkIntToScalar(10))));
1175
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001176}
1177
caryclark@google.comf1316942011-07-26 19:54:45 +00001178// Simple isRect test is inline TestPath, below.
1179// test_isRect provides more extensive testing.
1180static void test_isRect(skiatest::Reporter* reporter) {
1181 // passing tests (all moveTo / lineTo...
1182 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1183 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1184 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1185 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1186 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1187 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1188 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1189 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1190 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1191 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1192 {1, 0}, {.5f, 0}};
1193 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1194 {0, 1}, {0, .5f}};
1195 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1196 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1197 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001198 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001199
caryclark@google.comf1316942011-07-26 19:54:45 +00001200 // failing tests
1201 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1202 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1203 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1204 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1205 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1206 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1207 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1208 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001209 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1210 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1211 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001212
caryclark@google.comf1316942011-07-26 19:54:45 +00001213 // failing, no close
1214 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1215 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1216
1217 size_t testLen[] = {
1218 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1219 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001220 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001221 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001222 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001223 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001224 };
1225 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001226 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1227 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001228 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001229 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001230 SkPoint* lastPass = rf;
1231 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001232 bool fail = false;
1233 bool close = true;
1234 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1235 size_t index;
1236 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1237 SkPath path;
1238 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1239 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1240 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1241 }
1242 if (close) {
1243 path.close();
1244 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001245 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1246 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001247
caryclark@google.com56f233a2012-11-19 13:06:06 +00001248 if (!fail) {
1249 SkRect computed, expected;
1250 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1251 REPORTER_ASSERT(reporter, path.isRect(&computed));
1252 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001253
caryclark@google.comf68154a2012-11-21 15:18:06 +00001254 bool isClosed;
1255 SkPath::Direction direction, cheapDirection;
1256 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001257 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001258 REPORTER_ASSERT(reporter, isClosed == close);
1259 REPORTER_ASSERT(reporter, direction == cheapDirection);
1260 } else {
1261 SkRect computed;
1262 computed.set(123, 456, 789, 1011);
1263 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1264 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1265 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1266
1267 bool isClosed = (bool) -1;
1268 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001269 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001270 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1271 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001272 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001273
caryclark@google.comf1316942011-07-26 19:54:45 +00001274 if (tests[testIndex] == lastPass) {
1275 fail = true;
1276 }
1277 if (tests[testIndex] == lastClose) {
1278 close = false;
1279 }
1280 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001281
caryclark@google.comf1316942011-07-26 19:54:45 +00001282 // fail, close then line
1283 SkPath path1;
1284 path1.moveTo(r1[0].fX, r1[0].fY);
1285 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1286 path1.lineTo(r1[index].fX, r1[index].fY);
1287 }
1288 path1.close();
1289 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001290 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001291
caryclark@google.comf1316942011-07-26 19:54:45 +00001292 // fail, move in the middle
1293 path1.reset();
1294 path1.moveTo(r1[0].fX, r1[0].fY);
1295 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1296 if (index == 2) {
1297 path1.moveTo(1, .5f);
1298 }
1299 path1.lineTo(r1[index].fX, r1[index].fY);
1300 }
1301 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001302 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001303
1304 // fail, move on the edge
1305 path1.reset();
1306 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1307 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1308 path1.lineTo(r1[index].fX, r1[index].fY);
1309 }
1310 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001311 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001312
caryclark@google.comf1316942011-07-26 19:54:45 +00001313 // fail, quad
1314 path1.reset();
1315 path1.moveTo(r1[0].fX, r1[0].fY);
1316 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1317 if (index == 2) {
1318 path1.quadTo(1, .5f, 1, .5f);
1319 }
1320 path1.lineTo(r1[index].fX, r1[index].fY);
1321 }
1322 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001323 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001324
caryclark@google.comf1316942011-07-26 19:54:45 +00001325 // fail, cubic
1326 path1.reset();
1327 path1.moveTo(r1[0].fX, r1[0].fY);
1328 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1329 if (index == 2) {
1330 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1331 }
1332 path1.lineTo(r1[index].fX, r1[index].fY);
1333 }
1334 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001335 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001336}
1337
caryclark@google.com56f233a2012-11-19 13:06:06 +00001338static void test_isNestedRects(skiatest::Reporter* reporter) {
1339 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001340 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001341 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1342 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1343 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001344 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001345 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1346 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1347 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1348 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001349 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001350 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001351 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001352 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001353 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1354 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1355 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001356
1357 // failing tests
1358 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1359 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1360 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1361 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1362 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1363 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1364 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1365 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1366
1367 // failing, no close
1368 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1369 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1370
1371 size_t testLen[] = {
1372 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1373 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1374 sizeof(rd), sizeof(re),
1375 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1376 sizeof(f7), sizeof(f8),
1377 sizeof(c1), sizeof(c2)
1378 };
1379 SkPoint* tests[] = {
1380 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1381 f1, f2, f3, f4, f5, f6, f7, f8,
1382 c1, c2
1383 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001384 SkPath::Direction dirs[] = {
1385 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001386 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001387 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1388 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001389 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1390 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1391 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1392 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1393 };
1394 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1395
caryclark@google.com56f233a2012-11-19 13:06:06 +00001396 const SkPoint* lastPass = re;
1397 const SkPoint* lastClose = f8;
1398 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1399 size_t index;
1400 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1401 bool fail = false;
1402 bool close = true;
1403 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1404 SkPath path;
1405 if (rectFirst) {
1406 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1407 }
1408 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1409 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1410 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1411 }
1412 if (close) {
1413 path.close();
1414 }
1415 if (!rectFirst) {
1416 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1417 }
1418 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1419 if (!fail) {
1420 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001421 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001422 SkRect testBounds;
1423 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1424 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1425 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001426 if (rectFirst) {
1427 expectedDirs[0] = SkPath::kCW_Direction;
1428 } else {
1429 expectedDirs[0] = SkPath::kCCW_Direction;
1430 }
1431 expectedDirs[1] = dirs[testIndex];
1432 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001433 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1434 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001435 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1436 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001437 }
1438 if (tests[testIndex] == lastPass) {
1439 fail = true;
1440 }
1441 if (tests[testIndex] == lastClose) {
1442 close = false;
1443 }
1444 }
1445
1446 // fail, close then line
1447 SkPath path1;
1448 if (rectFirst) {
1449 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1450 }
1451 path1.moveTo(r1[0].fX, r1[0].fY);
1452 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1453 path1.lineTo(r1[index].fX, r1[index].fY);
1454 }
1455 path1.close();
1456 path1.lineTo(1, 0);
1457 if (!rectFirst) {
1458 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1459 }
1460 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1461
1462 // fail, move in the middle
1463 path1.reset();
1464 if (rectFirst) {
1465 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1466 }
1467 path1.moveTo(r1[0].fX, r1[0].fY);
1468 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1469 if (index == 2) {
1470 path1.moveTo(1, .5f);
1471 }
1472 path1.lineTo(r1[index].fX, r1[index].fY);
1473 }
1474 path1.close();
1475 if (!rectFirst) {
1476 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1477 }
1478 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1479
1480 // fail, move on the edge
1481 path1.reset();
1482 if (rectFirst) {
1483 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1484 }
1485 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1486 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
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));
1494
1495 // fail, quad
1496 path1.reset();
1497 if (rectFirst) {
1498 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1499 }
1500 path1.moveTo(r1[0].fX, r1[0].fY);
1501 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1502 if (index == 2) {
1503 path1.quadTo(1, .5f, 1, .5f);
1504 }
1505 path1.lineTo(r1[index].fX, r1[index].fY);
1506 }
1507 path1.close();
1508 if (!rectFirst) {
1509 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1510 }
1511 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1512
1513 // fail, cubic
1514 path1.reset();
1515 if (rectFirst) {
1516 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1517 }
1518 path1.moveTo(r1[0].fX, r1[0].fY);
1519 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1520 if (index == 2) {
1521 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1522 }
1523 path1.lineTo(r1[index].fX, r1[index].fY);
1524 }
1525 path1.close();
1526 if (!rectFirst) {
1527 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1528 }
1529 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001530
caryclark@google.com56f233a2012-11-19 13:06:06 +00001531 // fail, not nested
1532 path1.reset();
1533 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1534 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1535 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1536 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001537
1538 // pass, stroke rect
1539 SkPath src, dst;
1540 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1541 SkPaint strokePaint;
1542 strokePaint.setStyle(SkPaint::kStroke_Style);
1543 strokePaint.setStrokeWidth(2);
1544 strokePaint.getFillPath(src, &dst);
1545 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001546}
1547
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001548static void write_and_read_back(skiatest::Reporter* reporter,
1549 const SkPath& p) {
1550 SkWriter32 writer(100);
1551 writer.writePath(p);
1552 size_t size = writer.size();
1553 SkAutoMalloc storage(size);
1554 writer.flatten(storage.get());
1555 SkReader32 reader(storage.get(), size);
1556
1557 SkPath readBack;
1558 REPORTER_ASSERT(reporter, readBack != p);
1559 reader.readPath(&readBack);
1560 REPORTER_ASSERT(reporter, readBack == p);
1561
rmistry@google.comd6176b02012-08-23 18:14:13 +00001562 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001563 p.getConvexityOrUnknown());
1564
1565 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1566
1567 const SkRect& origBounds = p.getBounds();
1568 const SkRect& readBackBounds = readBack.getBounds();
1569
1570 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1571}
1572
reed@google.com53effc52011-09-21 19:05:12 +00001573static void test_flattening(skiatest::Reporter* reporter) {
1574 SkPath p;
1575
1576 static const SkPoint pts[] = {
1577 { 0, 0 },
1578 { SkIntToScalar(10), SkIntToScalar(10) },
1579 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1580 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1581 };
1582 p.moveTo(pts[0]);
1583 p.lineTo(pts[1]);
1584 p.quadTo(pts[2], pts[3]);
1585 p.cubicTo(pts[4], pts[5], pts[6]);
1586
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001587 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001588
1589 // create a buffer that should be much larger than the path so we don't
1590 // kill our stack if writer goes too far.
1591 char buffer[1024];
1592 uint32_t size1 = p.writeToMemory(NULL);
1593 uint32_t size2 = p.writeToMemory(buffer);
1594 REPORTER_ASSERT(reporter, size1 == size2);
1595
1596 SkPath p2;
1597 uint32_t size3 = p2.readFromMemory(buffer);
1598 REPORTER_ASSERT(reporter, size1 == size3);
1599 REPORTER_ASSERT(reporter, p == p2);
1600
1601 char buffer2[1024];
1602 size3 = p2.writeToMemory(buffer2);
1603 REPORTER_ASSERT(reporter, size1 == size3);
1604 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001605
1606 // test persistence of the oval flag & convexity
1607 {
1608 SkPath oval;
1609 SkRect rect = SkRect::MakeWH(10, 10);
1610 oval.addOval(rect);
1611
1612 write_and_read_back(reporter, oval);
1613 }
reed@google.com53effc52011-09-21 19:05:12 +00001614}
1615
1616static void test_transform(skiatest::Reporter* reporter) {
1617 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001618
reed@google.com53effc52011-09-21 19:05:12 +00001619 static const SkPoint pts[] = {
1620 { 0, 0 },
1621 { SkIntToScalar(10), SkIntToScalar(10) },
1622 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1623 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1624 };
1625 p.moveTo(pts[0]);
1626 p.lineTo(pts[1]);
1627 p.quadTo(pts[2], pts[3]);
1628 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001629
reed@google.com53effc52011-09-21 19:05:12 +00001630 SkMatrix matrix;
1631 matrix.reset();
1632 p.transform(matrix, &p1);
1633 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001634
reed@google.com53effc52011-09-21 19:05:12 +00001635 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1636 p.transform(matrix, &p1);
1637 SkPoint pts1[7];
1638 int count = p1.getPoints(pts1, 7);
1639 REPORTER_ASSERT(reporter, 7 == count);
1640 for (int i = 0; i < count; ++i) {
1641 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1642 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1643 }
1644}
1645
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001646static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001647 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001648 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001649
caryclark@google.com56f233a2012-11-19 13:06:06 +00001650 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001651 const char* testPath;
1652 const size_t numResultPts;
1653 const SkRect resultBound;
1654 const SkPath::Verb* resultVerbs;
1655 const size_t numResultVerbs;
1656 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001657
schenney@chromium.org7e963602012-06-13 17:05:43 +00001658 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1659 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1660 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1661 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1662 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1663 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1664 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1665 static const SkPath::Verb resultVerbs8[] = {
1666 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1667 };
1668 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1669 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1670 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1671 static const SkPath::Verb resultVerbs12[] = {
1672 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1673 };
1674 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1675 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1676 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1677 static const SkPath::Verb resultVerbs16[] = {
1678 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1679 };
1680 static const struct zeroPathTestData gZeroLengthTests[] = {
1681 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001682 { "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 +00001683 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001684 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1685 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1686 { "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) },
1687 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1688 { "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) },
1689 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1690 { "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) },
1691 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1692 { "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) },
1693 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1694 { "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 +00001695 SK_ARRAY_COUNT(resultVerbs14)
1696 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001697 { "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) },
1698 { "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 +00001699 SK_ARRAY_COUNT(resultVerbs16)
1700 }
1701 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001702
schenney@chromium.org7e963602012-06-13 17:05:43 +00001703 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1704 p.reset();
1705 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1706 REPORTER_ASSERT(reporter, valid);
1707 REPORTER_ASSERT(reporter, !p.isEmpty());
1708 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1709 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1710 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1711 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1712 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1713 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001714 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001715}
1716
1717struct SegmentInfo {
1718 SkPath fPath;
1719 int fPointCount;
1720};
1721
reed@google.com10296cc2011-09-21 12:29:05 +00001722#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1723
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001724static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001725 SkPath p, p2;
1726
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001727 p.moveTo(0, 0);
1728 p.quadTo(100, 100, 200, 200);
1729 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1730 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001731 p2 = p;
1732 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001733 p.cubicTo(100, 100, 200, 200, 300, 300);
1734 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1735 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001736 p2 = p;
1737 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1738
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001739 p.reset();
1740 p.moveTo(0, 0);
1741 p.cubicTo(100, 100, 200, 200, 300, 300);
1742 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001743 p2 = p;
1744 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001745
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001746 REPORTER_ASSERT(reporter, !p.isEmpty());
1747}
1748
1749static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001750 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001751 SkPoint pts[4];
1752
1753 // Test an iterator with no path
1754 SkPath::Iter noPathIter;
1755 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001756
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001757 // Test that setting an empty path works
1758 noPathIter.setPath(p, false);
1759 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001760
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001761 // Test that close path makes no difference for an empty path
1762 noPathIter.setPath(p, true);
1763 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001764
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001765 // Test an iterator with an initial empty path
1766 SkPath::Iter iter(p, false);
1767 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1768
1769 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001770 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001771 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1772
rmistry@google.comd6176b02012-08-23 18:14:13 +00001773
schenney@chromium.org7e963602012-06-13 17:05:43 +00001774 struct iterTestData {
1775 const char* testPath;
1776 const bool forceClose;
1777 const bool consumeDegenerates;
1778 const size_t* numResultPtsPerVerb;
1779 const SkPoint* resultPts;
1780 const SkPath::Verb* resultVerbs;
1781 const size_t numResultVerbs;
1782 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001783
schenney@chromium.org7e963602012-06-13 17:05:43 +00001784 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1785 static const SkPath::Verb resultVerbs2[] = {
1786 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1787 };
1788 static const SkPath::Verb resultVerbs3[] = {
1789 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1790 };
1791 static const SkPath::Verb resultVerbs4[] = {
1792 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1793 };
1794 static const SkPath::Verb resultVerbs5[] = {
1795 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1796 };
1797 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001798 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1799 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1800 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1801 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001802 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001803 static const SkPoint resultPts2[] = {
1804 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1805 };
1806 static const SkPoint resultPts3[] = {
1807 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1808 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1809 };
1810 static const SkPoint resultPts4[] = {
1811 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1812 };
1813 static const SkPoint resultPts5[] = {
1814 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1815 };
1816 static const struct iterTestData gIterTests[] = {
1817 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001818 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1819 { "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 +00001820 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1821 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1822 { "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) },
1823 { "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 +00001824 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1825 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1826 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1827 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1828 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1829 { "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 +00001830 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001831
schenney@chromium.org7e963602012-06-13 17:05:43 +00001832 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1833 p.reset();
1834 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1835 REPORTER_ASSERT(reporter, valid);
1836 iter.setPath(p, gIterTests[i].forceClose);
1837 int j = 0, l = 0;
1838 do {
1839 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1840 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1841 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1842 }
1843 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1844 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1845 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001846
1847 // The GM degeneratesegments.cpp test is more extensive
1848}
1849
1850static void test_raw_iter(skiatest::Reporter* reporter) {
1851 SkPath p;
1852 SkPoint pts[4];
1853
1854 // Test an iterator with no path
1855 SkPath::RawIter noPathIter;
1856 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1857 // Test that setting an empty path works
1858 noPathIter.setPath(p);
1859 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001860
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001861 // Test an iterator with an initial empty path
1862 SkPath::RawIter iter(p);
1863 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1864
1865 // Test that a move-only path returns the move.
1866 p.moveTo(SK_Scalar1, 0);
1867 iter.setPath(p);
1868 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1869 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1870 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1871 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1872
1873 // No matter how many moves we add, we should get them all back
1874 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1875 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1876 iter.setPath(p);
1877 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1878 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1879 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1880 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1881 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1882 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1883 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1884 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1885 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1886 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1887
1888 // Initial close is never ever stored
1889 p.reset();
1890 p.close();
1891 iter.setPath(p);
1892 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1893
1894 // Move/close sequences
1895 p.reset();
1896 p.close(); // Not stored, no purpose
1897 p.moveTo(SK_Scalar1, 0);
1898 p.close();
1899 p.close(); // Not stored, no purpose
1900 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1901 p.close();
1902 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1903 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1904 p.close();
1905 iter.setPath(p);
1906 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1907 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1908 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1909 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1910 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1911 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1912 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1913 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1914 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1915 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1916 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1917 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1918 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1919 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1920 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1921 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1922 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1923 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1924 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1925 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1926 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1927 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1928
1929 // Generate random paths and verify
1930 SkPoint randomPts[25];
1931 for (int i = 0; i < 5; ++i) {
1932 for (int j = 0; j < 5; ++j) {
1933 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1934 }
1935 }
1936
1937 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001938 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001939 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001940 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001941 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001942
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001943 for (int i = 0; i < 500; ++i) {
1944 p.reset();
1945 bool lastWasClose = true;
1946 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001947 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001948 int numPoints = 0;
1949 int numVerbs = (rand.nextU() >> 16) % 10;
1950 int numIterVerbs = 0;
1951 for (int j = 0; j < numVerbs; ++j) {
1952 do {
1953 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1954 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001955 switch (nextVerb) {
1956 case SkPath::kMove_Verb:
1957 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1958 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001959 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001960 numPoints += 1;
1961 lastWasClose = false;
1962 haveMoveTo = true;
1963 break;
1964 case SkPath::kLine_Verb:
1965 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001966 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001967 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1968 haveMoveTo = true;
1969 }
1970 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1971 p.lineTo(expectedPts[numPoints]);
1972 numPoints += 1;
1973 lastWasClose = false;
1974 break;
1975 case SkPath::kQuad_Verb:
1976 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001977 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001978 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1979 haveMoveTo = true;
1980 }
1981 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1982 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1983 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1984 numPoints += 2;
1985 lastWasClose = false;
1986 break;
reed@google.com277c3f82013-05-31 15:17:50 +00001987 case SkPath::kConic_Verb:
1988 if (!haveMoveTo) {
1989 expectedPts[numPoints++] = lastMoveToPt;
1990 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1991 haveMoveTo = true;
1992 }
1993 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1994 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1995 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1996 rand.nextUScalar1() * 4);
1997 numPoints += 2;
1998 lastWasClose = false;
1999 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002000 case SkPath::kCubic_Verb:
2001 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002002 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002003 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2004 haveMoveTo = true;
2005 }
2006 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2007 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2008 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2009 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2010 expectedPts[numPoints + 2]);
2011 numPoints += 3;
2012 lastWasClose = false;
2013 break;
2014 case SkPath::kClose_Verb:
2015 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002016 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002017 lastWasClose = true;
2018 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002019 default:
2020 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002021 }
2022 expectedVerbs[numIterVerbs++] = nextVerb;
2023 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002024
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002025 iter.setPath(p);
2026 numVerbs = numIterVerbs;
2027 numIterVerbs = 0;
2028 int numIterPts = 0;
2029 SkPoint lastMoveTo;
2030 SkPoint lastPt;
2031 lastMoveTo.set(0, 0);
2032 lastPt.set(0, 0);
2033 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2034 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2035 numIterVerbs++;
2036 switch (nextVerb) {
2037 case SkPath::kMove_Verb:
2038 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2039 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2040 lastPt = lastMoveTo = pts[0];
2041 numIterPts += 1;
2042 break;
2043 case SkPath::kLine_Verb:
2044 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2045 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2046 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2047 lastPt = pts[1];
2048 numIterPts += 1;
2049 break;
2050 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002051 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002052 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2053 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2054 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2055 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2056 lastPt = pts[2];
2057 numIterPts += 2;
2058 break;
2059 case SkPath::kCubic_Verb:
2060 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2061 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2062 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2063 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2064 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2065 lastPt = pts[3];
2066 numIterPts += 3;
2067 break;
2068 case SkPath::kClose_Verb:
2069 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2070 lastPt = lastMoveTo;
2071 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002072 default:
2073 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002074 }
2075 }
2076 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2077 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2078 }
2079}
2080
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002081static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002082 const SkPath& path,
2083 bool expectedCircle,
2084 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002085 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002086 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2087 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002088
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002089 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002090 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2091 }
2092}
2093
2094static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002095 const SkPath& path,
2096 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002097 SkPath tmp;
2098
2099 SkMatrix m;
2100 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2101 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002102 // this matrix reverses the direction.
2103 if (SkPath::kCCW_Direction == dir) {
2104 dir = SkPath::kCW_Direction;
2105 } else {
2106 SkASSERT(SkPath::kCW_Direction == dir);
2107 dir = SkPath::kCCW_Direction;
2108 }
2109 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002110}
2111
2112static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002113 const SkPath& path,
2114 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002115 SkPath tmp;
2116
2117 // translate at small offset
2118 SkMatrix m;
2119 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2120 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002121 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002122
2123 tmp.reset();
2124 m.reset();
2125
2126 // translate at a relatively big offset
2127 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2128 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002129 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002130}
2131
2132static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002133 const SkPath& path,
2134 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002135 for (int angle = 0; angle < 360; ++angle) {
2136 SkPath tmp;
2137 SkMatrix m;
2138 m.setRotate(SkIntToScalar(angle));
2139 path.transform(m, &tmp);
2140
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002141 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002142 // degrees is not an oval anymore, this can be improved. we made this
2143 // for the simplicity of our implementation.
2144 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002145 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002146 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002147 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002148 }
2149 }
2150}
2151
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002152static void test_circle_mirror_x(skiatest::Reporter* reporter,
2153 const SkPath& path,
2154 SkPath::Direction dir) {
2155 SkPath tmp;
2156 SkMatrix m;
2157 m.reset();
2158 m.setScaleX(-SK_Scalar1);
2159 path.transform(m, &tmp);
2160
2161 if (SkPath::kCW_Direction == dir) {
2162 dir = SkPath::kCCW_Direction;
2163 } else {
2164 SkASSERT(SkPath::kCCW_Direction == dir);
2165 dir = SkPath::kCW_Direction;
2166 }
2167
2168 check_for_circle(reporter, tmp, true, dir);
2169}
2170
2171static void test_circle_mirror_y(skiatest::Reporter* reporter,
2172 const SkPath& path,
2173 SkPath::Direction dir) {
2174 SkPath tmp;
2175 SkMatrix m;
2176 m.reset();
2177 m.setScaleY(-SK_Scalar1);
2178 path.transform(m, &tmp);
2179
2180 if (SkPath::kCW_Direction == dir) {
2181 dir = SkPath::kCCW_Direction;
2182 } else {
2183 SkASSERT(SkPath::kCCW_Direction == dir);
2184 dir = SkPath::kCW_Direction;
2185 }
2186
2187 check_for_circle(reporter, tmp, true, dir);
2188}
2189
2190static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2191 const SkPath& path,
2192 SkPath::Direction dir) {
2193 SkPath tmp;
2194 SkMatrix m;
2195 m.reset();
2196 m.setScaleX(-SK_Scalar1);
2197 m.setScaleY(-SK_Scalar1);
2198 path.transform(m, &tmp);
2199
2200 check_for_circle(reporter, tmp, true, dir);
2201}
2202
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002203static void test_circle_with_direction(skiatest::Reporter* reporter,
2204 SkPath::Direction dir) {
2205 SkPath path;
2206
2207 // circle at origin
2208 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002209 check_for_circle(reporter, path, true, dir);
2210 test_circle_rotate(reporter, path, dir);
2211 test_circle_translate(reporter, path, dir);
2212 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002213
2214 // circle at an offset at (10, 10)
2215 path.reset();
2216 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2217 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002218 check_for_circle(reporter, path, true, dir);
2219 test_circle_rotate(reporter, path, dir);
2220 test_circle_translate(reporter, path, dir);
2221 test_circle_skew(reporter, path, dir);
2222 test_circle_mirror_x(reporter, path, dir);
2223 test_circle_mirror_y(reporter, path, dir);
2224 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002225}
2226
2227static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2228 SkPath path;
2229 SkPath circle;
2230 SkPath rect;
2231 SkPath empty;
2232
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002233 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2234 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2235
2236 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002237 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2238 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2239
2240 SkMatrix translate;
2241 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2242
2243 // For simplicity, all the path concatenation related operations
2244 // would mark it non-circle, though in theory it's still a circle.
2245
2246 // empty + circle (translate)
2247 path = empty;
2248 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002249 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002250
2251 // circle + empty (translate)
2252 path = circle;
2253 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002254 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002255
2256 // test reverseAddPath
2257 path = circle;
2258 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002259 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002260}
2261
2262static void test_circle(skiatest::Reporter* reporter) {
2263 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2264 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2265
2266 // multiple addCircle()
2267 SkPath path;
2268 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2269 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002270 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002271
2272 // some extra lineTo() would make isOval() fail
2273 path.reset();
2274 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2275 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002276 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002277
2278 // not back to the original point
2279 path.reset();
2280 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2281 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002282 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002283
2284 test_circle_with_add_paths(reporter);
2285}
2286
2287static void test_oval(skiatest::Reporter* reporter) {
2288 SkRect rect;
2289 SkMatrix m;
2290 SkPath path;
2291
2292 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2293 path.addOval(rect);
2294
2295 REPORTER_ASSERT(reporter, path.isOval(NULL));
2296
2297 m.setRotate(SkIntToScalar(90));
2298 SkPath tmp;
2299 path.transform(m, &tmp);
2300 // an oval rotated 90 degrees is still an oval.
2301 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2302
2303 m.reset();
2304 m.setRotate(SkIntToScalar(30));
2305 tmp.reset();
2306 path.transform(m, &tmp);
2307 // an oval rotated 30 degrees is not an oval anymore.
2308 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2309
2310 // since empty path being transformed.
2311 path.reset();
2312 tmp.reset();
2313 m.reset();
2314 path.transform(m, &tmp);
2315 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2316
2317 // empty path is not an oval
2318 tmp.reset();
2319 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2320
2321 // only has moveTo()s
2322 tmp.reset();
2323 tmp.moveTo(0, 0);
2324 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2325 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2326
2327 // mimic WebKit's calling convention,
2328 // call moveTo() first and then call addOval()
2329 path.reset();
2330 path.moveTo(0, 0);
2331 path.addOval(rect);
2332 REPORTER_ASSERT(reporter, path.isOval(NULL));
2333
2334 // copy path
2335 path.reset();
2336 tmp.reset();
2337 tmp.addOval(rect);
2338 path = tmp;
2339 REPORTER_ASSERT(reporter, path.isOval(NULL));
2340}
2341
bungeman@google.coma5809a32013-06-21 15:13:34 +00002342static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2343 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002344
reed@android.com3abec1d2009-03-02 05:36:20 +00002345 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002346 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002347 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002348 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002349 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002350 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2351 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002352 REPORTER_ASSERT(reporter, p == empty);
2353 REPORTER_ASSERT(reporter, !(p != empty));
2354}
2355
2356static void TestPath(skiatest::Reporter* reporter) {
2357 SkTSize<SkScalar>::Make(3,4);
2358
2359 SkPath p, empty;
2360 SkRect bounds, bounds2;
2361 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002362
reed@android.comd252db02009-04-01 18:31:44 +00002363 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002364
reed@android.com3abec1d2009-03-02 05:36:20 +00002365 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002366
reed@android.com6b82d1a2009-06-03 02:35:01 +00002367 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2368 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002369 // we have quads or cubics
2370 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002371 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002372
reed@android.com6b82d1a2009-06-03 02:35:01 +00002373 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002374 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002375
reed@android.com6b82d1a2009-06-03 02:35:01 +00002376 p.addOval(bounds);
2377 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002378 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002379
bungeman@google.coma5809a32013-06-21 15:13:34 +00002380 p.rewind();
2381 test_empty(reporter, p);
2382
reed@android.com3abec1d2009-03-02 05:36:20 +00002383 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002384 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002385 // we have only lines
2386 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002387 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002388
bungeman@google.coma5809a32013-06-21 15:13:34 +00002389 REPORTER_ASSERT(reporter, p != empty);
2390 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002391
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002392 // do getPoints and getVerbs return the right result
2393 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2394 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002395 SkPoint pts[4];
2396 int count = p.getPoints(pts, 4);
2397 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002398 uint8_t verbs[6];
2399 verbs[5] = 0xff;
2400 p.getVerbs(verbs, 5);
2401 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2402 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2403 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2404 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2405 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2406 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002407 bounds2.set(pts, 4);
2408 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002409
reed@android.com3abec1d2009-03-02 05:36:20 +00002410 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2411 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002412 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002413
reed@android.com3abec1d2009-03-02 05:36:20 +00002414 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002415 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002416 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2417 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002418
reed@android.com3abec1d2009-03-02 05:36:20 +00002419 // now force p to not be a rect
2420 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2421 p.addRect(bounds);
2422 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002423
reed@google.com7e6c4d12012-05-10 14:05:43 +00002424 test_isLine(reporter);
2425 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002426 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002427 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002428 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002429 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002430 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002431 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002432 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002433 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002434 test_flattening(reporter);
2435 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002436 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002437 test_iter(reporter);
2438 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002439 test_circle(reporter);
2440 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002441 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002442 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002443 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002444 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002445 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002446 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002447 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002448 test_tricky_cubic();
2449 test_clipped_cubic();
2450 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002451 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002452 test_bad_cubic_crbug234190();
reed@android.com3abec1d2009-03-02 05:36:20 +00002453}
2454
2455#include "TestClassDef.h"
2456DEFINE_TESTCLASS("Path", PathTestClass, TestPath)