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