blob: 6c8f83e5401feef74ed264f99907460e284498a9 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com3abec1d2009-03-02 05:36:20 +00008#include "Test.h"
reed@google.com8cae8352012-09-14 15:18:41 +00009#include "SkCanvas.h"
reed@google.com55b5f4b2011-09-07 12:23:41 +000010#include "SkPaint.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000011#include "SkPath.h"
reed@google.com04863fa2011-05-15 04:08:24 +000012#include "SkParse.h"
reed@google.com3e71a882012-01-10 18:44:37 +000013#include "SkParsePath.h"
reed@google.com8b06f1a2012-05-29 12:03:46 +000014#include "SkPathEffect.h"
schenney@chromium.org6630d8d2012-01-04 21:05:51 +000015#include "SkRandom.h"
reed@google.com53effc52011-09-21 19:05:12 +000016#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000017#include "SkSize.h"
reed@google.com53effc52011-09-21 19:05:12 +000018#include "SkWriter32.h"
reed@google.com8cae8352012-09-14 15:18:41 +000019#include "SkSurface.h"
20
caryclark@google.com56f233a2012-11-19 13:06:06 +000021#if defined(WIN32)
22 #define SUPPRESS_VISIBILITY_WARNING
23#else
24 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
25#endif
26
reed@google.com8cae8352012-09-14 15:18:41 +000027static SkSurface* new_surface(int w, int h) {
28 SkImage::Info info = {
29 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
30 };
mike@reedtribe.orgb9476252012-11-15 02:37:45 +000031 return SkSurface::NewRaster(info);
reed@google.com8cae8352012-09-14 15:18:41 +000032}
33
reed@google.com3eff3592013-05-08 21:08:21 +000034// This used to assert in the debug build, as the edges did not all line-up.
35static void test_bad_cubic_crbug234190() {
36 SkPath path;
37 path.moveTo(13.8509f, 3.16858f);
38 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
39 -2.38991e+08f, -4.26573e+08f,
40 -2.41016e+08f, -4.30188e+08f);
41
42 SkPaint paint;
43 paint.setAntiAlias(true);
44 SkAutoTUnref<SkSurface> surface(new_surface(84, 88));
45 surface->getCanvas()->drawPath(path, paint);
46}
47
reed@google.com7a90daf2013-04-10 18:44:00 +000048static void test_bad_cubic_crbug229478() {
49 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000050 { 4595.91064f, -11596.9873f },
51 { 4597.2168f, -11595.9414f },
52 { 4598.52344f, -11594.8955f },
53 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +000054 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000055
reed@google.com7a90daf2013-04-10 18:44:00 +000056 SkPath path;
57 path.moveTo(pts[0]);
58 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000059
reed@google.com7a90daf2013-04-10 18:44:00 +000060 SkPaint paint;
61 paint.setStyle(SkPaint::kStroke_Style);
62 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000063
reed@google.com7a90daf2013-04-10 18:44:00 +000064 SkPath dst;
65 // Before the fix, this would infinite-recurse, and run out of stack
66 // because we would keep trying to subdivide a degenerate cubic segment.
67 paint.getFillPath(path, &dst, NULL);
68}
69
reed@google.com64d62952013-01-18 17:49:28 +000070static void build_path_170666(SkPath& path) {
71 path.moveTo(17.9459f, 21.6344f);
72 path.lineTo(139.545f, -47.8105f);
73 path.lineTo(139.545f, -47.8105f);
74 path.lineTo(131.07f, -47.3888f);
75 path.lineTo(131.07f, -47.3888f);
76 path.lineTo(122.586f, -46.9532f);
77 path.lineTo(122.586f, -46.9532f);
78 path.lineTo(18076.6f, 31390.9f);
79 path.lineTo(18076.6f, 31390.9f);
80 path.lineTo(18085.1f, 31390.5f);
81 path.lineTo(18085.1f, 31390.5f);
82 path.lineTo(18076.6f, 31390.9f);
83 path.lineTo(18076.6f, 31390.9f);
84 path.lineTo(17955, 31460.3f);
85 path.lineTo(17955, 31460.3f);
86 path.lineTo(17963.5f, 31459.9f);
87 path.lineTo(17963.5f, 31459.9f);
88 path.lineTo(17971.9f, 31459.5f);
89 path.lineTo(17971.9f, 31459.5f);
90 path.lineTo(17.9551f, 21.6205f);
91 path.lineTo(17.9551f, 21.6205f);
92 path.lineTo(9.47091f, 22.0561f);
93 path.lineTo(9.47091f, 22.0561f);
94 path.lineTo(17.9459f, 21.6344f);
95 path.lineTo(17.9459f, 21.6344f);
96 path.close();path.moveTo(0.995934f, 22.4779f);
97 path.lineTo(0.986725f, 22.4918f);
98 path.lineTo(0.986725f, 22.4918f);
99 path.lineTo(17955, 31460.4f);
100 path.lineTo(17955, 31460.4f);
101 path.lineTo(17971.9f, 31459.5f);
102 path.lineTo(17971.9f, 31459.5f);
103 path.lineTo(18093.6f, 31390.1f);
104 path.lineTo(18093.6f, 31390.1f);
105 path.lineTo(18093.6f, 31390);
106 path.lineTo(18093.6f, 31390);
107 path.lineTo(139.555f, -47.8244f);
108 path.lineTo(139.555f, -47.8244f);
109 path.lineTo(122.595f, -46.9671f);
110 path.lineTo(122.595f, -46.9671f);
111 path.lineTo(0.995934f, 22.4779f);
112 path.lineTo(0.995934f, 22.4779f);
113 path.close();
114 path.moveTo(5.43941f, 25.5223f);
115 path.lineTo(798267, -28871.1f);
116 path.lineTo(798267, -28871.1f);
117 path.lineTo(3.12512e+06f, -113102);
118 path.lineTo(3.12512e+06f, -113102);
119 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
120 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
121 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
122 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
123 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
124 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
125 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
126 path.lineTo(2.78271e+08f, -1.00733e+07f);
127 path.lineTo(2.78271e+08f, -1.00733e+07f);
128 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
129 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
130 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
131 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
132 path.lineTo(2.77473e+08f, -1.00444e+07f);
133 path.lineTo(2.77473e+08f, -1.00444e+07f);
134 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
135 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
136 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
137 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
138 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
139 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
140 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
141 path.lineTo(798284, -28872);
142 path.lineTo(798284, -28872);
143 path.lineTo(22.4044f, 24.6677f);
144 path.lineTo(22.4044f, 24.6677f);
145 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
146 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
147 path.close();
148}
149
150static void build_path_simple_170666(SkPath& path) {
151 path.moveTo(126.677f, 24.1591f);
152 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
153}
154
155// This used to assert in the SK_DEBUG build, as the clip step would fail with
156// too-few interations in our cubic-line intersection code. That code now runs
157// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000158static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000159 SkPath path;
160 SkPaint paint;
161 paint.setAntiAlias(true);
162
163 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000164
reed@google.com64d62952013-01-18 17:49:28 +0000165 build_path_simple_170666(path);
166 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000167
reed@google.com64d62952013-01-18 17:49:28 +0000168 build_path_170666(path);
169 surface->getCanvas()->drawPath(path, paint);
170}
171
reed@google.coma8790de2012-10-24 21:04:04 +0000172// Make sure we stay non-finite once we get there (unless we reset or rewind).
173static void test_addrect_isfinite(skiatest::Reporter* reporter) {
174 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000175
reed@google.coma8790de2012-10-24 21:04:04 +0000176 path.addRect(SkRect::MakeWH(50, 100));
177 REPORTER_ASSERT(reporter, path.isFinite());
178
179 path.moveTo(0, 0);
180 path.lineTo(SK_ScalarInfinity, 42);
181 REPORTER_ASSERT(reporter, !path.isFinite());
182
183 path.addRect(SkRect::MakeWH(50, 100));
184 REPORTER_ASSERT(reporter, !path.isFinite());
185
186 path.reset();
187 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000188
reed@google.coma8790de2012-10-24 21:04:04 +0000189 path.addRect(SkRect::MakeWH(50, 100));
190 REPORTER_ASSERT(reporter, path.isFinite());
191}
192
reed@google.com848148e2013-01-15 15:51:59 +0000193static void build_big_path(SkPath* path, bool reducedCase) {
194 if (reducedCase) {
195 path->moveTo(577330, 1971.72f);
196 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
197 } else {
198 path->moveTo(60.1631f, 7.70567f);
199 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
200 path->lineTo(577379, 1977.77f);
201 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
202 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
203 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
204 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
205 path->quadTo(577198, 1972, 577238, 1971.31f);
206 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
207 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
208 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
209 path->moveTo(306.718f, -32.912f);
210 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
211 }
212}
213
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000214static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000215 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
216
217 // This path used to assert, because our cubic-chopping code incorrectly
218 // moved control points after the chop. This test should be run in SK_DEBUG
219 // mode to ensure that we no long assert.
220 SkPath path;
221 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
222 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000223
reed@google.com848148e2013-01-15 15:51:59 +0000224 SkPaint paint;
225 for (int doAA = 0; doAA <= 1; ++doAA) {
226 paint.setAntiAlias(SkToBool(doAA));
227 surface->getCanvas()->drawPath(path, paint);
228 }
229 }
230}
231
reed@google.com8cae8352012-09-14 15:18:41 +0000232// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
233// which triggered an assert, from a tricky cubic. This test replicates that
234// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
235// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000236static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000237 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000238 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
239 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
240 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
241 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000242 };
243
244 SkPath path;
245 path.moveTo(pts[0]);
246 path.cubicTo(pts[1], pts[2], pts[3]);
247
248 SkPaint paint;
249 paint.setAntiAlias(true);
250
251 SkSurface* surface = new_surface(19, 130);
252 surface->getCanvas()->drawPath(path, paint);
253 surface->unref();
254}
reed@android.com3abec1d2009-03-02 05:36:20 +0000255
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000256// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
257//
258static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
259 SkPath path;
260 path.quadTo(157, 366, 286, 208);
261 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000262
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000263 SkMatrix matrix;
264 matrix.setScale(1000*1000, 1000*1000);
265
266 // Be sure that path::transform correctly updates isFinite and the bounds
267 // if the transformation overflows. The previous bug was that isFinite was
268 // set to true in this case, but the bounds were not set to empty (which
269 // they should be).
270 while (path.isFinite()) {
271 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
272 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
273 path.transform(matrix);
274 }
275 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
276
277 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
278 path.transform(matrix);
279 // we need to still be non-finite
280 REPORTER_ASSERT(reporter, !path.isFinite());
281 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
282}
283
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000284static void add_corner_arc(SkPath* path, const SkRect& rect,
285 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000286 int startAngle)
287{
288
289 SkScalar rx = SkMinScalar(rect.width(), xIn);
290 SkScalar ry = SkMinScalar(rect.height(), yIn);
291
292 SkRect arcRect;
293 arcRect.set(-rx, -ry, rx, ry);
294 switch (startAngle) {
295 case 0:
296 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
297 break;
298 case 90:
299 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
300 break;
301 case 180:
302 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
303 break;
304 case 270:
305 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
306 break;
307 default:
308 break;
309 }
310
311 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
312}
313
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000314static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000315 SkScalar xCorner, SkScalar yCorner) {
316 // we are lazy here and use the same x & y for each corner
317 add_corner_arc(path, r, xCorner, yCorner, 270);
318 add_corner_arc(path, r, xCorner, yCorner, 0);
319 add_corner_arc(path, r, xCorner, yCorner, 90);
320 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000321 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000322}
323
324// Chrome creates its own round rects with each corner possibly being different.
325// Performance will suffer if they are not convex.
326// Note: PathBench::ArbRoundRectBench performs almost exactly
327// the same test (but with drawing)
328static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000329 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000330 SkRect r;
331
332 for (int i = 0; i < 5000; ++i) {
333
robertphillips@google.com158618e2012-10-23 16:56:56 +0000334 SkScalar size = rand.nextUScalar1() * 30;
335 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000336 continue;
337 }
338 r.fLeft = rand.nextUScalar1() * 300;
339 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000340 r.fRight = r.fLeft + 2 * size;
341 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000342
343 SkPath temp;
344
345 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
346
347 REPORTER_ASSERT(reporter, temp.isConvex());
348 }
349}
350
robertphillips@google.com158618e2012-10-23 16:56:56 +0000351// Chrome will sometimes create a 0 radius round rect. The degenerate
352// quads prevent the path from being converted to a rect
353// Note: PathBench::ArbRoundRectBench performs almost exactly
354// the same test (but with drawing)
355static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000356 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000357 SkRect r;
358
359 for (int i = 0; i < 5000; ++i) {
360
361 SkScalar size = rand.nextUScalar1() * 30;
362 if (size < SK_Scalar1) {
363 continue;
364 }
365 r.fLeft = rand.nextUScalar1() * 300;
366 r.fTop = rand.nextUScalar1() * 300;
367 r.fRight = r.fLeft + 2 * size;
368 r.fBottom = r.fTop + 2 * size;
369
370 SkPath temp;
371
372 make_arb_round_rect(&temp, r, 0, 0);
373
robertphillips@google.com158618e2012-10-23 16:56:56 +0000374 SkRect result;
375 REPORTER_ASSERT(reporter, temp.isRect(&result));
376 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000377 }
378}
379
reed@google.com0bb18bb2012-07-26 15:20:36 +0000380static void test_rect_isfinite(skiatest::Reporter* reporter) {
381 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000382 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000383 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
reed@google.com0bb18bb2012-07-26 15:20:36 +0000385 SkRect r;
386 r.setEmpty();
387 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000388 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000389 REPORTER_ASSERT(reporter, !r.isFinite());
390 r.set(0, 0, nan, 0);
391 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000392
reed@google.com0bb18bb2012-07-26 15:20:36 +0000393 SkPoint pts[] = {
394 { 0, 0 },
395 { SK_Scalar1, 0 },
396 { 0, SK_Scalar1 },
397 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000398
reed@google.com0bb18bb2012-07-26 15:20:36 +0000399 bool isFine = r.setBoundsCheck(pts, 3);
400 REPORTER_ASSERT(reporter, isFine);
401 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000402
reed@google.com0bb18bb2012-07-26 15:20:36 +0000403 pts[1].set(inf, 0);
404 isFine = r.setBoundsCheck(pts, 3);
405 REPORTER_ASSERT(reporter, !isFine);
406 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000407
reed@google.com0bb18bb2012-07-26 15:20:36 +0000408 pts[1].set(nan, 0);
409 isFine = r.setBoundsCheck(pts, 3);
410 REPORTER_ASSERT(reporter, !isFine);
411 REPORTER_ASSERT(reporter, r.isEmpty());
412}
413
414static void test_path_isfinite(skiatest::Reporter* reporter) {
415 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000416 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000417 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000418
reed@google.com0bb18bb2012-07-26 15:20:36 +0000419 SkPath path;
420 REPORTER_ASSERT(reporter, path.isFinite());
421
422 path.reset();
423 REPORTER_ASSERT(reporter, path.isFinite());
424
425 path.reset();
426 path.moveTo(SK_Scalar1, 0);
427 REPORTER_ASSERT(reporter, path.isFinite());
428
429 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000430 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000431 REPORTER_ASSERT(reporter, !path.isFinite());
432
433 path.reset();
434 path.moveTo(nan, 0);
435 REPORTER_ASSERT(reporter, !path.isFinite());
436}
437
438static void test_isfinite(skiatest::Reporter* reporter) {
439 test_rect_isfinite(reporter);
440 test_path_isfinite(reporter);
441}
442
reed@google.com744faba2012-05-29 19:54:52 +0000443// assert that we always
444// start with a moveTo
445// only have 1 moveTo
446// only have Lines after that
447// end with a single close
448// only have (at most) 1 close
449//
450static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000451 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000452 SkPath::RawIter iter(path);
453 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000454
455 bool firstTime = true;
456 bool foundClose = false;
457 for (;;) {
458 switch (iter.next(pts)) {
459 case SkPath::kMove_Verb:
460 REPORTER_ASSERT(reporter, firstTime);
461 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
462 srcPts++;
463 firstTime = false;
464 break;
465 case SkPath::kLine_Verb:
466 REPORTER_ASSERT(reporter, !firstTime);
467 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
468 srcPts++;
469 break;
470 case SkPath::kQuad_Verb:
471 REPORTER_ASSERT(reporter, !"unexpected quad verb");
472 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000473 case SkPath::kConic_Verb:
474 REPORTER_ASSERT(reporter, !"unexpected conic verb");
475 break;
reed@google.com744faba2012-05-29 19:54:52 +0000476 case SkPath::kCubic_Verb:
477 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
478 break;
479 case SkPath::kClose_Verb:
480 REPORTER_ASSERT(reporter, !firstTime);
481 REPORTER_ASSERT(reporter, !foundClose);
482 REPORTER_ASSERT(reporter, expectClose);
483 foundClose = true;
484 break;
485 case SkPath::kDone_Verb:
486 goto DONE;
487 }
488 }
489DONE:
490 REPORTER_ASSERT(reporter, foundClose == expectClose);
491}
492
493static void test_addPoly(skiatest::Reporter* reporter) {
494 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000495 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000496
reed@google.com744faba2012-05-29 19:54:52 +0000497 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
498 pts[i].fX = rand.nextSScalar1();
499 pts[i].fY = rand.nextSScalar1();
500 }
501
502 for (int doClose = 0; doClose <= 1; ++doClose) {
503 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
504 SkPath path;
505 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000506 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000507 }
508 }
509}
510
reed@google.com8b06f1a2012-05-29 12:03:46 +0000511static void test_strokerec(skiatest::Reporter* reporter) {
512 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
513 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000514
reed@google.com8b06f1a2012-05-29 12:03:46 +0000515 rec.setHairlineStyle();
516 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000517
reed@google.com8b06f1a2012-05-29 12:03:46 +0000518 rec.setStrokeStyle(SK_Scalar1, false);
519 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000520
reed@google.com8b06f1a2012-05-29 12:03:46 +0000521 rec.setStrokeStyle(SK_Scalar1, true);
522 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000523
reed@google.com8b06f1a2012-05-29 12:03:46 +0000524 rec.setStrokeStyle(0, false);
525 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000526
reed@google.com8b06f1a2012-05-29 12:03:46 +0000527 rec.setStrokeStyle(0, true);
528 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
529}
530
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000531// Set this for paths that don't have a consistent direction such as a bowtie.
532// (cheapComputeDirection is not expected to catch these.)
533static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
534
535static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
536 SkPath::Direction expected) {
537 if (expected == kDontCheckDir) {
538 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000539 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000540 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
541
542 SkPath::Direction dir;
543 if (copy.cheapComputeDirection(&dir)) {
544 REPORTER_ASSERT(reporter, dir == expected);
545 } else {
546 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
547 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000548}
549
reed@google.com3e71a882012-01-10 18:44:37 +0000550static void test_direction(skiatest::Reporter* reporter) {
551 size_t i;
552 SkPath path;
553 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
554 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
555 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000556 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000557
558 static const char* gDegen[] = {
559 "M 10 10",
560 "M 10 10 M 20 20",
561 "M 10 10 L 20 20",
562 "M 10 10 L 10 10 L 10 10",
563 "M 10 10 Q 10 10 10 10",
564 "M 10 10 C 10 10 10 10 10 10",
565 };
566 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
567 path.reset();
568 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
569 REPORTER_ASSERT(reporter, valid);
570 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
571 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000572
reed@google.com3e71a882012-01-10 18:44:37 +0000573 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000574 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000575 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000576 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000577 // rect with top two corners replaced by cubics with identical middle
578 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000579 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
580 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000581 };
582 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
583 path.reset();
584 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
585 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000586 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000587 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000588
reed@google.com3e71a882012-01-10 18:44:37 +0000589 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000590 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000591 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000592 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000593 // rect with top two corners replaced by cubics with identical middle
594 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000595 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
596 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000597 };
598 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
599 path.reset();
600 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
601 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000602 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000603 }
reed@google.comac8543f2012-01-30 20:51:25 +0000604
605 // Test two donuts, each wound a different direction. Only the outer contour
606 // determines the cheap direction
607 path.reset();
608 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
609 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000610 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000611
reed@google.comac8543f2012-01-30 20:51:25 +0000612 path.reset();
613 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
614 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000615 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000616
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000617#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000618 // triangle with one point really far from the origin.
619 path.reset();
620 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000621 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
622 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
623 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000624 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000625#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000626}
627
reed@google.comffdb0182011-11-14 19:29:14 +0000628static void add_rect(SkPath* path, const SkRect& r) {
629 path->moveTo(r.fLeft, r.fTop);
630 path->lineTo(r.fRight, r.fTop);
631 path->lineTo(r.fRight, r.fBottom);
632 path->lineTo(r.fLeft, r.fBottom);
633 path->close();
634}
635
636static void test_bounds(skiatest::Reporter* reporter) {
637 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000638 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
639 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
640 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
641 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000642 };
643
644 SkPath path0, path1;
645 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
646 path0.addRect(rects[i]);
647 add_rect(&path1, rects[i]);
648 }
649
650 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
651}
652
reed@google.com55b5f4b2011-09-07 12:23:41 +0000653static void stroke_cubic(const SkPoint pts[4]) {
654 SkPath path;
655 path.moveTo(pts[0]);
656 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000657
reed@google.com55b5f4b2011-09-07 12:23:41 +0000658 SkPaint paint;
659 paint.setStyle(SkPaint::kStroke_Style);
660 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000661
reed@google.com55b5f4b2011-09-07 12:23:41 +0000662 SkPath fill;
663 paint.getFillPath(path, &fill);
664}
665
666// just ensure this can run w/o any SkASSERTS firing in the debug build
667// we used to assert due to differences in how we determine a degenerate vector
668// but that was fixed with the introduction of SkPoint::CanNormalize
669static void stroke_tiny_cubic() {
670 SkPoint p0[] = {
671 { 372.0f, 92.0f },
672 { 372.0f, 92.0f },
673 { 372.0f, 92.0f },
674 { 372.0f, 92.0f },
675 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000676
reed@google.com55b5f4b2011-09-07 12:23:41 +0000677 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000678
reed@google.com55b5f4b2011-09-07 12:23:41 +0000679 SkPoint p1[] = {
680 { 372.0f, 92.0f },
681 { 372.0007f, 92.000755f },
682 { 371.99927f, 92.003922f },
683 { 371.99826f, 92.003899f },
684 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000685
reed@google.com55b5f4b2011-09-07 12:23:41 +0000686 stroke_cubic(p1);
687}
688
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000689static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
690 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000691 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000692 SkPoint mv;
693 SkPoint pts[4];
694 SkPath::Verb v;
695 int nMT = 0;
696 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000697 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000698 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
699 switch (v) {
700 case SkPath::kMove_Verb:
701 mv = pts[0];
702 ++nMT;
703 break;
704 case SkPath::kClose_Verb:
705 REPORTER_ASSERT(reporter, mv == pts[0]);
706 ++nCL;
707 break;
708 default:
709 break;
710 }
711 }
712 // if we force a close on the interator we should have a close
713 // for every moveTo
714 REPORTER_ASSERT(reporter, !i || nMT == nCL);
715 }
716}
717
718static void test_close(skiatest::Reporter* reporter) {
719 SkPath closePt;
720 closePt.moveTo(0, 0);
721 closePt.close();
722 check_close(reporter, closePt);
723
724 SkPath openPt;
725 openPt.moveTo(0, 0);
726 check_close(reporter, openPt);
727
728 SkPath empty;
729 check_close(reporter, empty);
730 empty.close();
731 check_close(reporter, empty);
732
733 SkPath rect;
734 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
735 check_close(reporter, rect);
736 rect.close();
737 check_close(reporter, rect);
738
739 SkPath quad;
740 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
741 check_close(reporter, quad);
742 quad.close();
743 check_close(reporter, quad);
744
745 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000746 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000747 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
748 check_close(reporter, cubic);
749 cubic.close();
750 check_close(reporter, cubic);
751
752 SkPath line;
753 line.moveTo(SK_Scalar1, SK_Scalar1);
754 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
755 check_close(reporter, line);
756 line.close();
757 check_close(reporter, line);
758
759 SkPath rect2;
760 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
761 rect2.close();
762 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
763 check_close(reporter, rect2);
764 rect2.close();
765 check_close(reporter, rect2);
766
767 SkPath oval3;
768 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
769 oval3.close();
770 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
771 check_close(reporter, oval3);
772 oval3.close();
773 check_close(reporter, oval3);
774
775 SkPath moves;
776 moves.moveTo(SK_Scalar1, SK_Scalar1);
777 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
778 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
779 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
780 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000781
782 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000783}
784
reed@google.com7c424812011-05-15 04:38:34 +0000785static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
786 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000787 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
788 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000789 REPORTER_ASSERT(reporter, c == expected);
790}
791
792static void test_convexity2(skiatest::Reporter* reporter) {
793 SkPath pt;
794 pt.moveTo(0, 0);
795 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000796 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000797 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000798
reed@google.com7c424812011-05-15 04:38:34 +0000799 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000800 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
801 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000802 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000803 check_convexity(reporter, line, SkPath::kConvex_Convexity);
804 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000805
reed@google.com7c424812011-05-15 04:38:34 +0000806 SkPath triLeft;
807 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000808 triLeft.lineTo(SK_Scalar1, 0);
809 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000810 triLeft.close();
811 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000812 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000813
reed@google.com7c424812011-05-15 04:38:34 +0000814 SkPath triRight;
815 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000816 triRight.lineTo(-SK_Scalar1, 0);
817 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000818 triRight.close();
819 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000820 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000821
reed@google.com7c424812011-05-15 04:38:34 +0000822 SkPath square;
823 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000824 square.lineTo(SK_Scalar1, 0);
825 square.lineTo(SK_Scalar1, SK_Scalar1);
826 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000827 square.close();
828 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000829 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000830
reed@google.com7c424812011-05-15 04:38:34 +0000831 SkPath redundantSquare;
832 redundantSquare.moveTo(0, 0);
833 redundantSquare.lineTo(0, 0);
834 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000835 redundantSquare.lineTo(SK_Scalar1, 0);
836 redundantSquare.lineTo(SK_Scalar1, 0);
837 redundantSquare.lineTo(SK_Scalar1, 0);
838 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
839 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
840 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
841 redundantSquare.lineTo(0, SK_Scalar1);
842 redundantSquare.lineTo(0, SK_Scalar1);
843 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000844 redundantSquare.close();
845 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000846 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000847
reed@google.com7c424812011-05-15 04:38:34 +0000848 SkPath bowTie;
849 bowTie.moveTo(0, 0);
850 bowTie.lineTo(0, 0);
851 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000852 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
853 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
854 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
855 bowTie.lineTo(SK_Scalar1, 0);
856 bowTie.lineTo(SK_Scalar1, 0);
857 bowTie.lineTo(SK_Scalar1, 0);
858 bowTie.lineTo(0, SK_Scalar1);
859 bowTie.lineTo(0, SK_Scalar1);
860 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000861 bowTie.close();
862 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000863 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000864
reed@google.com7c424812011-05-15 04:38:34 +0000865 SkPath spiral;
866 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000867 spiral.lineTo(100*SK_Scalar1, 0);
868 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
869 spiral.lineTo(0, 100*SK_Scalar1);
870 spiral.lineTo(0, 50*SK_Scalar1);
871 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
872 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000873 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000874 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000875 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000876
reed@google.com7c424812011-05-15 04:38:34 +0000877 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000878 dent.moveTo(0, 0);
879 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
880 dent.lineTo(0, 100*SK_Scalar1);
881 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
882 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000883 dent.close();
884 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000885 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000886}
887
reed@android.com6b82d1a2009-06-03 02:35:01 +0000888static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
889 const SkRect& bounds) {
890 REPORTER_ASSERT(reporter, p.isConvex());
891 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000892
reed@android.com6b82d1a2009-06-03 02:35:01 +0000893 SkPath p2(p);
894 REPORTER_ASSERT(reporter, p2.isConvex());
895 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
896
897 SkPath other;
898 other.swap(p2);
899 REPORTER_ASSERT(reporter, other.isConvex());
900 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
901}
902
reed@google.com04863fa2011-05-15 04:08:24 +0000903static void setFromString(SkPath* path, const char str[]) {
904 bool first = true;
905 while (str) {
906 SkScalar x, y;
907 str = SkParse::FindScalar(str, &x);
908 if (NULL == str) {
909 break;
910 }
911 str = SkParse::FindScalar(str, &y);
912 SkASSERT(str);
913 if (first) {
914 path->moveTo(x, y);
915 first = false;
916 } else {
917 path->lineTo(x, y);
918 }
919 }
920}
921
922static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000923 SkPath path;
924
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000925 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000926 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000927 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000928 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000929 check_convexity(reporter, path, SkPath::kConcave_Convexity);
930
reed@google.com04863fa2011-05-15 04:08:24 +0000931 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000932 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000933 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000934 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000935
reed@google.com04863fa2011-05-15 04:08:24 +0000936 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000937 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000938 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000939 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000940
reed@google.com04863fa2011-05-15 04:08:24 +0000941 static const struct {
942 const char* fPathStr;
943 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000944 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000945 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000946 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
947 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
948 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
949 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
950 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
951 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
952 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
953 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000954 };
955
956 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
957 SkPath path;
958 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000959 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
960 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000961 }
962}
963
reed@google.com7e6c4d12012-05-10 14:05:43 +0000964static void test_isLine(skiatest::Reporter* reporter) {
965 SkPath path;
966 SkPoint pts[2];
967 const SkScalar value = SkIntToScalar(5);
968
969 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000970
reed@google.com7e6c4d12012-05-10 14:05:43 +0000971 // set some non-zero values
972 pts[0].set(value, value);
973 pts[1].set(value, value);
974 REPORTER_ASSERT(reporter, !path.isLine(pts));
975 // check that pts was untouched
976 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
977 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
978
979 const SkScalar moveX = SkIntToScalar(1);
980 const SkScalar moveY = SkIntToScalar(2);
981 SkASSERT(value != moveX && value != moveY);
982
983 path.moveTo(moveX, moveY);
984 REPORTER_ASSERT(reporter, !path.isLine(NULL));
985 REPORTER_ASSERT(reporter, !path.isLine(pts));
986 // check that pts was untouched
987 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
988 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
989
990 const SkScalar lineX = SkIntToScalar(2);
991 const SkScalar lineY = SkIntToScalar(2);
992 SkASSERT(value != lineX && value != lineY);
993
994 path.lineTo(lineX, lineY);
995 REPORTER_ASSERT(reporter, path.isLine(NULL));
996
997 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
998 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
999 REPORTER_ASSERT(reporter, path.isLine(pts));
1000 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1001 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1002
1003 path.lineTo(0, 0); // too many points/verbs
1004 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1005 REPORTER_ASSERT(reporter, !path.isLine(pts));
1006 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1007 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1008}
1009
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001010static void test_conservativelyContains(skiatest::Reporter* reporter) {
1011 SkPath path;
1012
1013 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1014 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1015
1016 // A circle that bounds kBaseRect (with a significant amount of slop)
1017 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1018 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1019 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1020
1021 // round-rect radii
1022 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001023
caryclark@google.com56f233a2012-11-19 13:06:06 +00001024 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001025 SkRect fQueryRect;
1026 bool fInRect;
1027 bool fInCircle;
1028 bool fInRR;
1029 } kQueries[] = {
1030 {kBaseRect, true, true, false},
1031
1032 // rect well inside of kBaseRect
1033 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1034 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1035 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1036 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1037 true, true, true},
1038
1039 // rects with edges off by one from kBaseRect's edges
1040 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1041 kBaseRect.width(), kBaseRect.height() + 1),
1042 false, true, false},
1043 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1044 kBaseRect.width() + 1, kBaseRect.height()),
1045 false, true, false},
1046 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1047 kBaseRect.width() + 1, kBaseRect.height() + 1),
1048 false, true, false},
1049 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1050 kBaseRect.width(), kBaseRect.height()),
1051 false, true, false},
1052 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1053 kBaseRect.width(), kBaseRect.height()),
1054 false, true, false},
1055 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1056 kBaseRect.width() + 2, kBaseRect.height()),
1057 false, true, false},
1058 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1059 kBaseRect.width() + 2, kBaseRect.height()),
1060 false, true, false},
1061
1062 // zero-w/h rects at each corner of kBaseRect
1063 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1064 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1065 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1066 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1067
1068 // far away rect
1069 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1070 SkIntToScalar(10), SkIntToScalar(10)),
1071 false, false, false},
1072
1073 // very large rect containing kBaseRect
1074 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1075 kBaseRect.fTop - 5 * kBaseRect.height(),
1076 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1077 false, false, false},
1078
1079 // skinny rect that spans same y-range as kBaseRect
1080 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1081 SkIntToScalar(1), kBaseRect.height()),
1082 true, true, true},
1083
1084 // short rect that spans same x-range as kBaseRect
1085 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1086 true, true, true},
1087
1088 // skinny rect that spans slightly larger y-range than kBaseRect
1089 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1090 SkIntToScalar(1), kBaseRect.height() + 1),
1091 false, true, false},
1092
1093 // short rect that spans slightly larger x-range than kBaseRect
1094 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1095 kBaseRect.width() + 1, SkScalar(1)),
1096 false, true, false},
1097 };
1098
1099 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001100 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001101 SkRect qRect = kQueries[q].fQueryRect;
1102 if (inv & 0x1) {
1103 SkTSwap(qRect.fLeft, qRect.fRight);
1104 }
1105 if (inv & 0x2) {
1106 SkTSwap(qRect.fTop, qRect.fBottom);
1107 }
1108 for (int d = 0; d < 2; ++d) {
1109 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1110 path.reset();
1111 path.addRect(kBaseRect, dir);
1112 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1113 path.conservativelyContainsRect(qRect));
1114
1115 path.reset();
1116 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1117 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1118 path.conservativelyContainsRect(qRect));
1119
1120 path.reset();
1121 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1122 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1123 path.conservativelyContainsRect(qRect));
1124 }
1125 // Slightly non-convex shape, shouldn't contain any rects.
1126 path.reset();
1127 path.moveTo(0, 0);
1128 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1129 path.lineTo(SkIntToScalar(100), 0);
1130 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1131 path.lineTo(0, SkIntToScalar(100));
1132 path.close();
1133 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1134 }
1135 }
1136
1137 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1138 path.reset();
1139 path.moveTo(0, 0);
1140 path.lineTo(SkIntToScalar(100), 0);
1141 path.lineTo(0, SkIntToScalar(100));
1142
1143 // inside, on along top edge
1144 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1145 SkIntToScalar(10),
1146 SkIntToScalar(10))));
1147 // above
1148 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1149 SkRect::MakeXYWH(SkIntToScalar(50),
1150 SkIntToScalar(-10),
1151 SkIntToScalar(10),
1152 SkIntToScalar(10))));
1153 // to the left
1154 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1155 SkIntToScalar(5),
1156 SkIntToScalar(5),
1157 SkIntToScalar(5))));
1158
1159 // outside the diagonal edge
1160 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1161 SkIntToScalar(200),
1162 SkIntToScalar(20),
1163 SkIntToScalar(5))));
1164}
1165
caryclark@google.comf1316942011-07-26 19:54:45 +00001166// Simple isRect test is inline TestPath, below.
1167// test_isRect provides more extensive testing.
1168static void test_isRect(skiatest::Reporter* reporter) {
1169 // passing tests (all moveTo / lineTo...
1170 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1171 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1172 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1173 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1174 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1175 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1176 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1177 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1178 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1179 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1180 {1, 0}, {.5f, 0}};
1181 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1182 {0, 1}, {0, .5f}};
1183 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1184 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1185 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001186 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001187
caryclark@google.comf1316942011-07-26 19:54:45 +00001188 // failing tests
1189 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1190 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1191 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1192 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1193 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1194 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1195 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1196 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001197 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1198 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1199 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001200
caryclark@google.comf1316942011-07-26 19:54:45 +00001201 // failing, no close
1202 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1203 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1204
1205 size_t testLen[] = {
1206 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1207 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001208 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001209 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001210 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001211 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001212 };
1213 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001214 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1215 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001216 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001217 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001218 SkPoint* lastPass = rf;
1219 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001220 bool fail = false;
1221 bool close = true;
1222 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1223 size_t index;
1224 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1225 SkPath path;
1226 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1227 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1228 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1229 }
1230 if (close) {
1231 path.close();
1232 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001233 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1234 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001235
caryclark@google.com56f233a2012-11-19 13:06:06 +00001236 if (!fail) {
1237 SkRect computed, expected;
1238 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1239 REPORTER_ASSERT(reporter, path.isRect(&computed));
1240 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001241
caryclark@google.comf68154a2012-11-21 15:18:06 +00001242 bool isClosed;
1243 SkPath::Direction direction, cheapDirection;
1244 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001245 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001246 REPORTER_ASSERT(reporter, isClosed == close);
1247 REPORTER_ASSERT(reporter, direction == cheapDirection);
1248 } else {
1249 SkRect computed;
1250 computed.set(123, 456, 789, 1011);
1251 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1252 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1253 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1254
1255 bool isClosed = (bool) -1;
1256 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001257 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001258 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1259 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001260 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001261
caryclark@google.comf1316942011-07-26 19:54:45 +00001262 if (tests[testIndex] == lastPass) {
1263 fail = true;
1264 }
1265 if (tests[testIndex] == lastClose) {
1266 close = false;
1267 }
1268 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001269
caryclark@google.comf1316942011-07-26 19:54:45 +00001270 // fail, close then line
1271 SkPath path1;
1272 path1.moveTo(r1[0].fX, r1[0].fY);
1273 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1274 path1.lineTo(r1[index].fX, r1[index].fY);
1275 }
1276 path1.close();
1277 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001278 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001279
caryclark@google.comf1316942011-07-26 19:54:45 +00001280 // fail, move in the middle
1281 path1.reset();
1282 path1.moveTo(r1[0].fX, r1[0].fY);
1283 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1284 if (index == 2) {
1285 path1.moveTo(1, .5f);
1286 }
1287 path1.lineTo(r1[index].fX, r1[index].fY);
1288 }
1289 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001290 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001291
1292 // fail, move on the edge
1293 path1.reset();
1294 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1295 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1296 path1.lineTo(r1[index].fX, r1[index].fY);
1297 }
1298 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001299 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001300
caryclark@google.comf1316942011-07-26 19:54:45 +00001301 // fail, quad
1302 path1.reset();
1303 path1.moveTo(r1[0].fX, r1[0].fY);
1304 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1305 if (index == 2) {
1306 path1.quadTo(1, .5f, 1, .5f);
1307 }
1308 path1.lineTo(r1[index].fX, r1[index].fY);
1309 }
1310 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001311 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001312
caryclark@google.comf1316942011-07-26 19:54:45 +00001313 // fail, cubic
1314 path1.reset();
1315 path1.moveTo(r1[0].fX, r1[0].fY);
1316 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1317 if (index == 2) {
1318 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1319 }
1320 path1.lineTo(r1[index].fX, r1[index].fY);
1321 }
1322 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001323 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001324}
1325
caryclark@google.com56f233a2012-11-19 13:06:06 +00001326static void test_isNestedRects(skiatest::Reporter* reporter) {
1327 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001328 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001329 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1330 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1331 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001332 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001333 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1334 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1335 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1336 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001337 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001338 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001339 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001340 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001341 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1342 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1343 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001344
1345 // failing tests
1346 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1347 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1348 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1349 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1350 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1351 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1352 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1353 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1354
1355 // failing, no close
1356 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1357 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1358
1359 size_t testLen[] = {
1360 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1361 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1362 sizeof(rd), sizeof(re),
1363 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1364 sizeof(f7), sizeof(f8),
1365 sizeof(c1), sizeof(c2)
1366 };
1367 SkPoint* tests[] = {
1368 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1369 f1, f2, f3, f4, f5, f6, f7, f8,
1370 c1, c2
1371 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001372 SkPath::Direction dirs[] = {
1373 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001374 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001375 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1376 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001377 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1378 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1379 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1380 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1381 };
1382 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1383
caryclark@google.com56f233a2012-11-19 13:06:06 +00001384 const SkPoint* lastPass = re;
1385 const SkPoint* lastClose = f8;
1386 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1387 size_t index;
1388 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1389 bool fail = false;
1390 bool close = true;
1391 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1392 SkPath path;
1393 if (rectFirst) {
1394 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1395 }
1396 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1397 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1398 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1399 }
1400 if (close) {
1401 path.close();
1402 }
1403 if (!rectFirst) {
1404 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1405 }
1406 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1407 if (!fail) {
1408 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001409 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001410 SkRect testBounds;
1411 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1412 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1413 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001414 if (rectFirst) {
1415 expectedDirs[0] = SkPath::kCW_Direction;
1416 } else {
1417 expectedDirs[0] = SkPath::kCCW_Direction;
1418 }
1419 expectedDirs[1] = dirs[testIndex];
1420 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001421 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1422 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001423 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1424 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001425 }
1426 if (tests[testIndex] == lastPass) {
1427 fail = true;
1428 }
1429 if (tests[testIndex] == lastClose) {
1430 close = false;
1431 }
1432 }
1433
1434 // fail, close then line
1435 SkPath path1;
1436 if (rectFirst) {
1437 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1438 }
1439 path1.moveTo(r1[0].fX, r1[0].fY);
1440 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1441 path1.lineTo(r1[index].fX, r1[index].fY);
1442 }
1443 path1.close();
1444 path1.lineTo(1, 0);
1445 if (!rectFirst) {
1446 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1447 }
1448 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1449
1450 // fail, move in the middle
1451 path1.reset();
1452 if (rectFirst) {
1453 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1454 }
1455 path1.moveTo(r1[0].fX, r1[0].fY);
1456 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1457 if (index == 2) {
1458 path1.moveTo(1, .5f);
1459 }
1460 path1.lineTo(r1[index].fX, r1[index].fY);
1461 }
1462 path1.close();
1463 if (!rectFirst) {
1464 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1465 }
1466 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1467
1468 // fail, move on the edge
1469 path1.reset();
1470 if (rectFirst) {
1471 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1472 }
1473 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1474 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1475 path1.lineTo(r1[index].fX, r1[index].fY);
1476 }
1477 path1.close();
1478 if (!rectFirst) {
1479 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1480 }
1481 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1482
1483 // fail, quad
1484 path1.reset();
1485 if (rectFirst) {
1486 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1487 }
1488 path1.moveTo(r1[0].fX, r1[0].fY);
1489 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1490 if (index == 2) {
1491 path1.quadTo(1, .5f, 1, .5f);
1492 }
1493 path1.lineTo(r1[index].fX, r1[index].fY);
1494 }
1495 path1.close();
1496 if (!rectFirst) {
1497 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1498 }
1499 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1500
1501 // fail, cubic
1502 path1.reset();
1503 if (rectFirst) {
1504 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1505 }
1506 path1.moveTo(r1[0].fX, r1[0].fY);
1507 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1508 if (index == 2) {
1509 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1510 }
1511 path1.lineTo(r1[index].fX, r1[index].fY);
1512 }
1513 path1.close();
1514 if (!rectFirst) {
1515 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1516 }
1517 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001518
caryclark@google.com56f233a2012-11-19 13:06:06 +00001519 // fail, not nested
1520 path1.reset();
1521 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1522 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1523 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1524 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001525
1526 // pass, stroke rect
1527 SkPath src, dst;
1528 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1529 SkPaint strokePaint;
1530 strokePaint.setStyle(SkPaint::kStroke_Style);
1531 strokePaint.setStrokeWidth(2);
1532 strokePaint.getFillPath(src, &dst);
1533 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001534}
1535
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001536static void write_and_read_back(skiatest::Reporter* reporter,
1537 const SkPath& p) {
1538 SkWriter32 writer(100);
1539 writer.writePath(p);
1540 size_t size = writer.size();
1541 SkAutoMalloc storage(size);
1542 writer.flatten(storage.get());
1543 SkReader32 reader(storage.get(), size);
1544
1545 SkPath readBack;
1546 REPORTER_ASSERT(reporter, readBack != p);
1547 reader.readPath(&readBack);
1548 REPORTER_ASSERT(reporter, readBack == p);
1549
rmistry@google.comd6176b02012-08-23 18:14:13 +00001550 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001551 p.getConvexityOrUnknown());
1552
1553 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1554
1555 const SkRect& origBounds = p.getBounds();
1556 const SkRect& readBackBounds = readBack.getBounds();
1557
1558 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1559}
1560
reed@google.com53effc52011-09-21 19:05:12 +00001561static void test_flattening(skiatest::Reporter* reporter) {
1562 SkPath p;
1563
1564 static const SkPoint pts[] = {
1565 { 0, 0 },
1566 { SkIntToScalar(10), SkIntToScalar(10) },
1567 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1568 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1569 };
1570 p.moveTo(pts[0]);
1571 p.lineTo(pts[1]);
1572 p.quadTo(pts[2], pts[3]);
1573 p.cubicTo(pts[4], pts[5], pts[6]);
1574
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001575 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001576
1577 // create a buffer that should be much larger than the path so we don't
1578 // kill our stack if writer goes too far.
1579 char buffer[1024];
1580 uint32_t size1 = p.writeToMemory(NULL);
1581 uint32_t size2 = p.writeToMemory(buffer);
1582 REPORTER_ASSERT(reporter, size1 == size2);
1583
1584 SkPath p2;
1585 uint32_t size3 = p2.readFromMemory(buffer);
1586 REPORTER_ASSERT(reporter, size1 == size3);
1587 REPORTER_ASSERT(reporter, p == p2);
1588
1589 char buffer2[1024];
1590 size3 = p2.writeToMemory(buffer2);
1591 REPORTER_ASSERT(reporter, size1 == size3);
1592 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001593
1594 // test persistence of the oval flag & convexity
1595 {
1596 SkPath oval;
1597 SkRect rect = SkRect::MakeWH(10, 10);
1598 oval.addOval(rect);
1599
1600 write_and_read_back(reporter, oval);
1601 }
reed@google.com53effc52011-09-21 19:05:12 +00001602}
1603
1604static void test_transform(skiatest::Reporter* reporter) {
1605 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001606
reed@google.com53effc52011-09-21 19:05:12 +00001607 static const SkPoint pts[] = {
1608 { 0, 0 },
1609 { SkIntToScalar(10), SkIntToScalar(10) },
1610 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1611 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1612 };
1613 p.moveTo(pts[0]);
1614 p.lineTo(pts[1]);
1615 p.quadTo(pts[2], pts[3]);
1616 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001617
reed@google.com53effc52011-09-21 19:05:12 +00001618 SkMatrix matrix;
1619 matrix.reset();
1620 p.transform(matrix, &p1);
1621 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001622
reed@google.com53effc52011-09-21 19:05:12 +00001623 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1624 p.transform(matrix, &p1);
1625 SkPoint pts1[7];
1626 int count = p1.getPoints(pts1, 7);
1627 REPORTER_ASSERT(reporter, 7 == count);
1628 for (int i = 0; i < count; ++i) {
1629 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1630 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1631 }
1632}
1633
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001634static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001635 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001636 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001637
caryclark@google.com56f233a2012-11-19 13:06:06 +00001638 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001639 const char* testPath;
1640 const size_t numResultPts;
1641 const SkRect resultBound;
1642 const SkPath::Verb* resultVerbs;
1643 const size_t numResultVerbs;
1644 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001645
schenney@chromium.org7e963602012-06-13 17:05:43 +00001646 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1647 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1648 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1649 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1650 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1651 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1652 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1653 static const SkPath::Verb resultVerbs8[] = {
1654 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1655 };
1656 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1657 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1658 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1659 static const SkPath::Verb resultVerbs12[] = {
1660 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1661 };
1662 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1663 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1664 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1665 static const SkPath::Verb resultVerbs16[] = {
1666 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1667 };
1668 static const struct zeroPathTestData gZeroLengthTests[] = {
1669 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001670 { "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 +00001671 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001672 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1673 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1674 { "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) },
1675 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1676 { "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) },
1677 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1678 { "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) },
1679 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1680 { "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) },
1681 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1682 { "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 +00001683 SK_ARRAY_COUNT(resultVerbs14)
1684 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001685 { "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) },
1686 { "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 +00001687 SK_ARRAY_COUNT(resultVerbs16)
1688 }
1689 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001690
schenney@chromium.org7e963602012-06-13 17:05:43 +00001691 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1692 p.reset();
1693 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1694 REPORTER_ASSERT(reporter, valid);
1695 REPORTER_ASSERT(reporter, !p.isEmpty());
1696 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1697 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1698 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1699 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1700 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1701 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001702 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001703}
1704
1705struct SegmentInfo {
1706 SkPath fPath;
1707 int fPointCount;
1708};
1709
reed@google.com10296cc2011-09-21 12:29:05 +00001710#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1711
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001712static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001713 SkPath p, p2;
1714
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001715 p.moveTo(0, 0);
1716 p.quadTo(100, 100, 200, 200);
1717 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1718 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001719 p2 = p;
1720 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001721 p.cubicTo(100, 100, 200, 200, 300, 300);
1722 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1723 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001724 p2 = p;
1725 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1726
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001727 p.reset();
1728 p.moveTo(0, 0);
1729 p.cubicTo(100, 100, 200, 200, 300, 300);
1730 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001731 p2 = p;
1732 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001733
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001734 REPORTER_ASSERT(reporter, !p.isEmpty());
1735}
1736
1737static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001738 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001739 SkPoint pts[4];
1740
1741 // Test an iterator with no path
1742 SkPath::Iter noPathIter;
1743 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001744
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001745 // Test that setting an empty path works
1746 noPathIter.setPath(p, false);
1747 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001748
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001749 // Test that close path makes no difference for an empty path
1750 noPathIter.setPath(p, true);
1751 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001752
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001753 // Test an iterator with an initial empty path
1754 SkPath::Iter iter(p, false);
1755 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1756
1757 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001758 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001759 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1760
rmistry@google.comd6176b02012-08-23 18:14:13 +00001761
schenney@chromium.org7e963602012-06-13 17:05:43 +00001762 struct iterTestData {
1763 const char* testPath;
1764 const bool forceClose;
1765 const bool consumeDegenerates;
1766 const size_t* numResultPtsPerVerb;
1767 const SkPoint* resultPts;
1768 const SkPath::Verb* resultVerbs;
1769 const size_t numResultVerbs;
1770 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001771
schenney@chromium.org7e963602012-06-13 17:05:43 +00001772 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1773 static const SkPath::Verb resultVerbs2[] = {
1774 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1775 };
1776 static const SkPath::Verb resultVerbs3[] = {
1777 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1778 };
1779 static const SkPath::Verb resultVerbs4[] = {
1780 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1781 };
1782 static const SkPath::Verb resultVerbs5[] = {
1783 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1784 };
1785 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001786 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1787 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1788 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1789 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001790 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001791 static const SkPoint resultPts2[] = {
1792 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1793 };
1794 static const SkPoint resultPts3[] = {
1795 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1796 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1797 };
1798 static const SkPoint resultPts4[] = {
1799 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1800 };
1801 static const SkPoint resultPts5[] = {
1802 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1803 };
1804 static const struct iterTestData gIterTests[] = {
1805 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001806 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1807 { "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 +00001808 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1809 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1810 { "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) },
1811 { "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 +00001812 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1813 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1814 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1815 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1816 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1817 { "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 +00001818 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001819
schenney@chromium.org7e963602012-06-13 17:05:43 +00001820 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1821 p.reset();
1822 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1823 REPORTER_ASSERT(reporter, valid);
1824 iter.setPath(p, gIterTests[i].forceClose);
1825 int j = 0, l = 0;
1826 do {
1827 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1828 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1829 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1830 }
1831 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1832 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1833 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001834
1835 // The GM degeneratesegments.cpp test is more extensive
1836}
1837
1838static void test_raw_iter(skiatest::Reporter* reporter) {
1839 SkPath p;
1840 SkPoint pts[4];
1841
1842 // Test an iterator with no path
1843 SkPath::RawIter noPathIter;
1844 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1845 // Test that setting an empty path works
1846 noPathIter.setPath(p);
1847 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001848
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001849 // Test an iterator with an initial empty path
1850 SkPath::RawIter iter(p);
1851 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1852
1853 // Test that a move-only path returns the move.
1854 p.moveTo(SK_Scalar1, 0);
1855 iter.setPath(p);
1856 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1857 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1858 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1859 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1860
1861 // No matter how many moves we add, we should get them all back
1862 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1863 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1864 iter.setPath(p);
1865 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1866 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1867 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1868 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1869 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1870 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1871 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1872 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1873 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1874 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1875
1876 // Initial close is never ever stored
1877 p.reset();
1878 p.close();
1879 iter.setPath(p);
1880 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1881
1882 // Move/close sequences
1883 p.reset();
1884 p.close(); // Not stored, no purpose
1885 p.moveTo(SK_Scalar1, 0);
1886 p.close();
1887 p.close(); // Not stored, no purpose
1888 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1889 p.close();
1890 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1891 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1892 p.close();
1893 iter.setPath(p);
1894 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_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::kClose_Verb);
1898 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1899 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1900 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_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::kClose_Verb);
1904 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1905 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1906 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1907 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1908 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1909 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_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::kClose_Verb);
1913 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1914 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1915 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1916
1917 // Generate random paths and verify
1918 SkPoint randomPts[25];
1919 for (int i = 0; i < 5; ++i) {
1920 for (int j = 0; j < 5; ++j) {
1921 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1922 }
1923 }
1924
1925 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001926 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001927 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001928 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001929 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001930
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001931 for (int i = 0; i < 500; ++i) {
1932 p.reset();
1933 bool lastWasClose = true;
1934 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001935 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001936 int numPoints = 0;
1937 int numVerbs = (rand.nextU() >> 16) % 10;
1938 int numIterVerbs = 0;
1939 for (int j = 0; j < numVerbs; ++j) {
1940 do {
1941 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1942 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001943 switch (nextVerb) {
1944 case SkPath::kMove_Verb:
1945 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1946 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001947 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001948 numPoints += 1;
1949 lastWasClose = false;
1950 haveMoveTo = true;
1951 break;
1952 case SkPath::kLine_Verb:
1953 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001954 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001955 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1956 haveMoveTo = true;
1957 }
1958 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1959 p.lineTo(expectedPts[numPoints]);
1960 numPoints += 1;
1961 lastWasClose = false;
1962 break;
1963 case SkPath::kQuad_Verb:
1964 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001965 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001966 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1967 haveMoveTo = true;
1968 }
1969 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1970 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1971 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1972 numPoints += 2;
1973 lastWasClose = false;
1974 break;
reed@google.com277c3f82013-05-31 15:17:50 +00001975 case SkPath::kConic_Verb:
1976 if (!haveMoveTo) {
1977 expectedPts[numPoints++] = lastMoveToPt;
1978 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1979 haveMoveTo = true;
1980 }
1981 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1982 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1983 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1984 rand.nextUScalar1() * 4);
1985 numPoints += 2;
1986 lastWasClose = false;
1987 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001988 case SkPath::kCubic_Verb:
1989 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001990 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001991 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1992 haveMoveTo = true;
1993 }
1994 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1995 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1996 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1997 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1998 expectedPts[numPoints + 2]);
1999 numPoints += 3;
2000 lastWasClose = false;
2001 break;
2002 case SkPath::kClose_Verb:
2003 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002004 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002005 lastWasClose = true;
2006 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002007 default:
2008 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002009 }
2010 expectedVerbs[numIterVerbs++] = nextVerb;
2011 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002012
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002013 iter.setPath(p);
2014 numVerbs = numIterVerbs;
2015 numIterVerbs = 0;
2016 int numIterPts = 0;
2017 SkPoint lastMoveTo;
2018 SkPoint lastPt;
2019 lastMoveTo.set(0, 0);
2020 lastPt.set(0, 0);
2021 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2022 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2023 numIterVerbs++;
2024 switch (nextVerb) {
2025 case SkPath::kMove_Verb:
2026 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2027 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2028 lastPt = lastMoveTo = pts[0];
2029 numIterPts += 1;
2030 break;
2031 case SkPath::kLine_Verb:
2032 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2033 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2034 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2035 lastPt = pts[1];
2036 numIterPts += 1;
2037 break;
2038 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002039 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002040 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2041 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2042 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2043 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2044 lastPt = pts[2];
2045 numIterPts += 2;
2046 break;
2047 case SkPath::kCubic_Verb:
2048 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2049 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2050 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2051 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2052 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2053 lastPt = pts[3];
2054 numIterPts += 3;
2055 break;
2056 case SkPath::kClose_Verb:
2057 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2058 lastPt = lastMoveTo;
2059 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002060 default:
2061 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002062 }
2063 }
2064 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2065 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2066 }
2067}
2068
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002069static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002070 const SkPath& path,
2071 bool expectedCircle,
2072 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002073 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002074 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2075 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002076
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002077 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002078 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2079 }
2080}
2081
2082static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002083 const SkPath& path,
2084 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002085 SkPath tmp;
2086
2087 SkMatrix m;
2088 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2089 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002090 // this matrix reverses the direction.
2091 if (SkPath::kCCW_Direction == dir) {
2092 dir = SkPath::kCW_Direction;
2093 } else {
2094 SkASSERT(SkPath::kCW_Direction == dir);
2095 dir = SkPath::kCCW_Direction;
2096 }
2097 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002098}
2099
2100static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002101 const SkPath& path,
2102 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002103 SkPath tmp;
2104
2105 // translate at small offset
2106 SkMatrix m;
2107 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2108 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002109 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002110
2111 tmp.reset();
2112 m.reset();
2113
2114 // translate at a relatively big offset
2115 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2116 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002117 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002118}
2119
2120static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002121 const SkPath& path,
2122 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002123 for (int angle = 0; angle < 360; ++angle) {
2124 SkPath tmp;
2125 SkMatrix m;
2126 m.setRotate(SkIntToScalar(angle));
2127 path.transform(m, &tmp);
2128
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002129 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002130 // degrees is not an oval anymore, this can be improved. we made this
2131 // for the simplicity of our implementation.
2132 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002133 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002134 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002135 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002136 }
2137 }
2138}
2139
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002140static void test_circle_mirror_x(skiatest::Reporter* reporter,
2141 const SkPath& path,
2142 SkPath::Direction dir) {
2143 SkPath tmp;
2144 SkMatrix m;
2145 m.reset();
2146 m.setScaleX(-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_y(skiatest::Reporter* reporter,
2160 const SkPath& path,
2161 SkPath::Direction dir) {
2162 SkPath tmp;
2163 SkMatrix m;
2164 m.reset();
2165 m.setScaleY(-SK_Scalar1);
2166 path.transform(m, &tmp);
2167
2168 if (SkPath::kCW_Direction == dir) {
2169 dir = SkPath::kCCW_Direction;
2170 } else {
2171 SkASSERT(SkPath::kCCW_Direction == dir);
2172 dir = SkPath::kCW_Direction;
2173 }
2174
2175 check_for_circle(reporter, tmp, true, dir);
2176}
2177
2178static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2179 const SkPath& path,
2180 SkPath::Direction dir) {
2181 SkPath tmp;
2182 SkMatrix m;
2183 m.reset();
2184 m.setScaleX(-SK_Scalar1);
2185 m.setScaleY(-SK_Scalar1);
2186 path.transform(m, &tmp);
2187
2188 check_for_circle(reporter, tmp, true, dir);
2189}
2190
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002191static void test_circle_with_direction(skiatest::Reporter* reporter,
2192 SkPath::Direction dir) {
2193 SkPath path;
2194
2195 // circle at origin
2196 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002197 check_for_circle(reporter, path, true, dir);
2198 test_circle_rotate(reporter, path, dir);
2199 test_circle_translate(reporter, path, dir);
2200 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002201
2202 // circle at an offset at (10, 10)
2203 path.reset();
2204 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2205 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002206 check_for_circle(reporter, path, true, dir);
2207 test_circle_rotate(reporter, path, dir);
2208 test_circle_translate(reporter, path, dir);
2209 test_circle_skew(reporter, path, dir);
2210 test_circle_mirror_x(reporter, path, dir);
2211 test_circle_mirror_y(reporter, path, dir);
2212 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002213}
2214
2215static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2216 SkPath path;
2217 SkPath circle;
2218 SkPath rect;
2219 SkPath empty;
2220
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002221 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2222 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2223
2224 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002225 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2226 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2227
2228 SkMatrix translate;
2229 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2230
2231 // For simplicity, all the path concatenation related operations
2232 // would mark it non-circle, though in theory it's still a circle.
2233
2234 // empty + circle (translate)
2235 path = empty;
2236 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002237 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002238
2239 // circle + empty (translate)
2240 path = circle;
2241 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002242 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002243
2244 // test reverseAddPath
2245 path = circle;
2246 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002247 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002248}
2249
2250static void test_circle(skiatest::Reporter* reporter) {
2251 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2252 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2253
2254 // multiple addCircle()
2255 SkPath path;
2256 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2257 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002258 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002259
2260 // some extra lineTo() would make isOval() fail
2261 path.reset();
2262 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2263 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002264 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002265
2266 // not back to the original point
2267 path.reset();
2268 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2269 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002270 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002271
2272 test_circle_with_add_paths(reporter);
2273}
2274
2275static void test_oval(skiatest::Reporter* reporter) {
2276 SkRect rect;
2277 SkMatrix m;
2278 SkPath path;
2279
2280 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2281 path.addOval(rect);
2282
2283 REPORTER_ASSERT(reporter, path.isOval(NULL));
2284
2285 m.setRotate(SkIntToScalar(90));
2286 SkPath tmp;
2287 path.transform(m, &tmp);
2288 // an oval rotated 90 degrees is still an oval.
2289 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2290
2291 m.reset();
2292 m.setRotate(SkIntToScalar(30));
2293 tmp.reset();
2294 path.transform(m, &tmp);
2295 // an oval rotated 30 degrees is not an oval anymore.
2296 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2297
2298 // since empty path being transformed.
2299 path.reset();
2300 tmp.reset();
2301 m.reset();
2302 path.transform(m, &tmp);
2303 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2304
2305 // empty path is not an oval
2306 tmp.reset();
2307 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2308
2309 // only has moveTo()s
2310 tmp.reset();
2311 tmp.moveTo(0, 0);
2312 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2313 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2314
2315 // mimic WebKit's calling convention,
2316 // call moveTo() first and then call addOval()
2317 path.reset();
2318 path.moveTo(0, 0);
2319 path.addOval(rect);
2320 REPORTER_ASSERT(reporter, path.isOval(NULL));
2321
2322 // copy path
2323 path.reset();
2324 tmp.reset();
2325 tmp.addOval(rect);
2326 path = tmp;
2327 REPORTER_ASSERT(reporter, path.isOval(NULL));
2328}
2329
bungeman@google.coma5809a32013-06-21 15:13:34 +00002330static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2331 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002332
reed@android.com3abec1d2009-03-02 05:36:20 +00002333 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002334 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002335 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002336 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002337 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002338 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2339 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002340 REPORTER_ASSERT(reporter, p == empty);
2341 REPORTER_ASSERT(reporter, !(p != empty));
2342}
2343
2344static void TestPath(skiatest::Reporter* reporter) {
2345 SkTSize<SkScalar>::Make(3,4);
2346
2347 SkPath p, empty;
2348 SkRect bounds, bounds2;
2349 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002350
reed@android.comd252db02009-04-01 18:31:44 +00002351 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002352
reed@android.com3abec1d2009-03-02 05:36:20 +00002353 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002354
reed@android.com6b82d1a2009-06-03 02:35:01 +00002355 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2356 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002357 // we have quads or cubics
2358 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002359 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002360
reed@android.com6b82d1a2009-06-03 02:35:01 +00002361 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002362 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002363
reed@android.com6b82d1a2009-06-03 02:35:01 +00002364 p.addOval(bounds);
2365 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002366 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002367
bungeman@google.coma5809a32013-06-21 15:13:34 +00002368 p.rewind();
2369 test_empty(reporter, p);
2370
reed@android.com3abec1d2009-03-02 05:36:20 +00002371 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002372 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002373 // we have only lines
2374 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002375 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002376
bungeman@google.coma5809a32013-06-21 15:13:34 +00002377 REPORTER_ASSERT(reporter, p != empty);
2378 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002379
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002380 // do getPoints and getVerbs return the right result
2381 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2382 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002383 SkPoint pts[4];
2384 int count = p.getPoints(pts, 4);
2385 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002386 uint8_t verbs[6];
2387 verbs[5] = 0xff;
2388 p.getVerbs(verbs, 5);
2389 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2390 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2391 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2392 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2393 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2394 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002395 bounds2.set(pts, 4);
2396 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002397
reed@android.com3abec1d2009-03-02 05:36:20 +00002398 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2399 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002400 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002401
reed@android.com3abec1d2009-03-02 05:36:20 +00002402 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002403 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002404 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2405 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002406
reed@android.com3abec1d2009-03-02 05:36:20 +00002407 // now force p to not be a rect
2408 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2409 p.addRect(bounds);
2410 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002411
reed@google.com7e6c4d12012-05-10 14:05:43 +00002412 test_isLine(reporter);
2413 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002414 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002415 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002416 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002417 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002418 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002419 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002420 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002421 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002422 test_flattening(reporter);
2423 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002424 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002425 test_iter(reporter);
2426 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002427 test_circle(reporter);
2428 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002429 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002430 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002431 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002432 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002433 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002434 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002435 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002436 test_tricky_cubic();
2437 test_clipped_cubic();
2438 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002439 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002440 test_bad_cubic_crbug234190();
reed@android.com3abec1d2009-03-02 05:36:20 +00002441}
2442
2443#include "TestClassDef.h"
2444DEFINE_TESTCLASS("Path", PathTestClass, TestPath)