blob: 41151a54bc62e02c4e1f1c68655f1d2562f7926c [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 }
reed@google.comda2b21f2013-06-21 17:32:32 +00001233 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001234
caryclark@google.com56f233a2012-11-19 13:06:06 +00001235 if (!fail) {
1236 SkRect computed, expected;
1237 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1238 REPORTER_ASSERT(reporter, path.isRect(&computed));
1239 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001240
caryclark@google.comf68154a2012-11-21 15:18:06 +00001241 bool isClosed;
1242 SkPath::Direction direction, cheapDirection;
1243 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
reed@google.comda2b21f2013-06-21 17:32:32 +00001244 REPORTER_ASSERT(reporter, path.isRect(NULL, &isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001245 REPORTER_ASSERT(reporter, isClosed == close);
1246 REPORTER_ASSERT(reporter, direction == cheapDirection);
1247 } else {
1248 SkRect computed;
1249 computed.set(123, 456, 789, 1011);
1250 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1251 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1252 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1253
1254 bool isClosed = (bool) -1;
1255 SkPath::Direction direction = (SkPath::Direction) -1;
reed@google.comda2b21f2013-06-21 17:32:32 +00001256 REPORTER_ASSERT(reporter, !path.isRect(NULL, &isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001257 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1258 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001259 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001260
caryclark@google.comf1316942011-07-26 19:54:45 +00001261 if (tests[testIndex] == lastPass) {
1262 fail = true;
1263 }
1264 if (tests[testIndex] == lastClose) {
1265 close = false;
1266 }
1267 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001268
caryclark@google.comf1316942011-07-26 19:54:45 +00001269 // fail, close then line
1270 SkPath path1;
1271 path1.moveTo(r1[0].fX, r1[0].fY);
1272 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1273 path1.lineTo(r1[index].fX, r1[index].fY);
1274 }
1275 path1.close();
1276 path1.lineTo(1, 0);
reed@google.comda2b21f2013-06-21 17:32:32 +00001277 REPORTER_ASSERT(reporter, fail ^ path1.isRect(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001278
caryclark@google.comf1316942011-07-26 19:54:45 +00001279 // fail, move in the middle
1280 path1.reset();
1281 path1.moveTo(r1[0].fX, r1[0].fY);
1282 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1283 if (index == 2) {
1284 path1.moveTo(1, .5f);
1285 }
1286 path1.lineTo(r1[index].fX, r1[index].fY);
1287 }
1288 path1.close();
reed@google.comda2b21f2013-06-21 17:32:32 +00001289 REPORTER_ASSERT(reporter, fail ^ path1.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001290
1291 // fail, move on the edge
1292 path1.reset();
1293 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1294 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1295 path1.lineTo(r1[index].fX, r1[index].fY);
1296 }
1297 path1.close();
reed@google.comda2b21f2013-06-21 17:32:32 +00001298 REPORTER_ASSERT(reporter, fail ^ path1.isRect(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001299
caryclark@google.comf1316942011-07-26 19:54:45 +00001300 // fail, quad
1301 path1.reset();
1302 path1.moveTo(r1[0].fX, r1[0].fY);
1303 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1304 if (index == 2) {
1305 path1.quadTo(1, .5f, 1, .5f);
1306 }
1307 path1.lineTo(r1[index].fX, r1[index].fY);
1308 }
1309 path1.close();
reed@google.comda2b21f2013-06-21 17:32:32 +00001310 REPORTER_ASSERT(reporter, fail ^ path1.isRect(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001311
caryclark@google.comf1316942011-07-26 19:54:45 +00001312 // fail, cubic
1313 path1.reset();
1314 path1.moveTo(r1[0].fX, r1[0].fY);
1315 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1316 if (index == 2) {
1317 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1318 }
1319 path1.lineTo(r1[index].fX, r1[index].fY);
1320 }
1321 path1.close();
reed@google.comda2b21f2013-06-21 17:32:32 +00001322 REPORTER_ASSERT(reporter, fail ^ path1.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00001323}
1324
caryclark@google.com56f233a2012-11-19 13:06:06 +00001325static void test_isNestedRects(skiatest::Reporter* reporter) {
1326 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001327 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001328 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1329 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1330 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001331 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001332 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1333 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1334 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1335 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001336 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001337 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001338 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001339 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001340 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1341 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1342 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001343
1344 // failing tests
1345 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1346 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1347 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1348 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1349 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1350 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1351 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1352 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1353
1354 // failing, no close
1355 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1356 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1357
1358 size_t testLen[] = {
1359 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1360 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1361 sizeof(rd), sizeof(re),
1362 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1363 sizeof(f7), sizeof(f8),
1364 sizeof(c1), sizeof(c2)
1365 };
1366 SkPoint* tests[] = {
1367 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1368 f1, f2, f3, f4, f5, f6, f7, f8,
1369 c1, c2
1370 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001371 SkPath::Direction dirs[] = {
1372 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001373 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001374 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1375 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001376 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1377 SkPath::kUnknown_Direction, SkPath::kUnknown_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 };
1381 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1382
caryclark@google.com56f233a2012-11-19 13:06:06 +00001383 const SkPoint* lastPass = re;
1384 const SkPoint* lastClose = f8;
1385 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1386 size_t index;
1387 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1388 bool fail = false;
1389 bool close = true;
1390 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1391 SkPath path;
1392 if (rectFirst) {
1393 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1394 }
1395 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1396 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1397 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1398 }
1399 if (close) {
1400 path.close();
1401 }
1402 if (!rectFirst) {
1403 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1404 }
1405 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1406 if (!fail) {
1407 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001408 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001409 SkRect testBounds;
1410 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1411 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1412 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001413 if (rectFirst) {
1414 expectedDirs[0] = SkPath::kCW_Direction;
1415 } else {
1416 expectedDirs[0] = SkPath::kCCW_Direction;
1417 }
1418 expectedDirs[1] = dirs[testIndex];
1419 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001420 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1421 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001422 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1423 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001424 }
1425 if (tests[testIndex] == lastPass) {
1426 fail = true;
1427 }
1428 if (tests[testIndex] == lastClose) {
1429 close = false;
1430 }
1431 }
1432
1433 // fail, close then line
1434 SkPath path1;
1435 if (rectFirst) {
1436 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1437 }
1438 path1.moveTo(r1[0].fX, r1[0].fY);
1439 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1440 path1.lineTo(r1[index].fX, r1[index].fY);
1441 }
1442 path1.close();
1443 path1.lineTo(1, 0);
1444 if (!rectFirst) {
1445 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1446 }
1447 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1448
1449 // fail, move in the middle
1450 path1.reset();
1451 if (rectFirst) {
1452 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1453 }
1454 path1.moveTo(r1[0].fX, r1[0].fY);
1455 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1456 if (index == 2) {
1457 path1.moveTo(1, .5f);
1458 }
1459 path1.lineTo(r1[index].fX, r1[index].fY);
1460 }
1461 path1.close();
1462 if (!rectFirst) {
1463 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1464 }
1465 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1466
1467 // fail, move on the edge
1468 path1.reset();
1469 if (rectFirst) {
1470 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1471 }
1472 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1473 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1474 path1.lineTo(r1[index].fX, r1[index].fY);
1475 }
1476 path1.close();
1477 if (!rectFirst) {
1478 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1479 }
1480 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1481
1482 // fail, quad
1483 path1.reset();
1484 if (rectFirst) {
1485 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1486 }
1487 path1.moveTo(r1[0].fX, r1[0].fY);
1488 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1489 if (index == 2) {
1490 path1.quadTo(1, .5f, 1, .5f);
1491 }
1492 path1.lineTo(r1[index].fX, r1[index].fY);
1493 }
1494 path1.close();
1495 if (!rectFirst) {
1496 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1497 }
1498 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1499
1500 // fail, cubic
1501 path1.reset();
1502 if (rectFirst) {
1503 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1504 }
1505 path1.moveTo(r1[0].fX, r1[0].fY);
1506 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1507 if (index == 2) {
1508 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1509 }
1510 path1.lineTo(r1[index].fX, r1[index].fY);
1511 }
1512 path1.close();
1513 if (!rectFirst) {
1514 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1515 }
1516 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001517
caryclark@google.com56f233a2012-11-19 13:06:06 +00001518 // fail, not nested
1519 path1.reset();
1520 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1521 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1522 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1523 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001524
1525 // pass, stroke rect
1526 SkPath src, dst;
1527 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1528 SkPaint strokePaint;
1529 strokePaint.setStyle(SkPaint::kStroke_Style);
1530 strokePaint.setStrokeWidth(2);
1531 strokePaint.getFillPath(src, &dst);
1532 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001533}
1534
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001535static void write_and_read_back(skiatest::Reporter* reporter,
1536 const SkPath& p) {
1537 SkWriter32 writer(100);
1538 writer.writePath(p);
1539 size_t size = writer.size();
1540 SkAutoMalloc storage(size);
1541 writer.flatten(storage.get());
1542 SkReader32 reader(storage.get(), size);
1543
1544 SkPath readBack;
1545 REPORTER_ASSERT(reporter, readBack != p);
1546 reader.readPath(&readBack);
1547 REPORTER_ASSERT(reporter, readBack == p);
1548
rmistry@google.comd6176b02012-08-23 18:14:13 +00001549 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001550 p.getConvexityOrUnknown());
1551
1552 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1553
1554 const SkRect& origBounds = p.getBounds();
1555 const SkRect& readBackBounds = readBack.getBounds();
1556
1557 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1558}
1559
reed@google.com53effc52011-09-21 19:05:12 +00001560static void test_flattening(skiatest::Reporter* reporter) {
1561 SkPath p;
1562
1563 static const SkPoint pts[] = {
1564 { 0, 0 },
1565 { SkIntToScalar(10), SkIntToScalar(10) },
1566 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1567 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1568 };
1569 p.moveTo(pts[0]);
1570 p.lineTo(pts[1]);
1571 p.quadTo(pts[2], pts[3]);
1572 p.cubicTo(pts[4], pts[5], pts[6]);
1573
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001574 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001575
1576 // create a buffer that should be much larger than the path so we don't
1577 // kill our stack if writer goes too far.
1578 char buffer[1024];
1579 uint32_t size1 = p.writeToMemory(NULL);
1580 uint32_t size2 = p.writeToMemory(buffer);
1581 REPORTER_ASSERT(reporter, size1 == size2);
1582
1583 SkPath p2;
1584 uint32_t size3 = p2.readFromMemory(buffer);
1585 REPORTER_ASSERT(reporter, size1 == size3);
1586 REPORTER_ASSERT(reporter, p == p2);
1587
1588 char buffer2[1024];
1589 size3 = p2.writeToMemory(buffer2);
1590 REPORTER_ASSERT(reporter, size1 == size3);
1591 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001592
1593 // test persistence of the oval flag & convexity
1594 {
1595 SkPath oval;
1596 SkRect rect = SkRect::MakeWH(10, 10);
1597 oval.addOval(rect);
1598
1599 write_and_read_back(reporter, oval);
1600 }
reed@google.com53effc52011-09-21 19:05:12 +00001601}
1602
1603static void test_transform(skiatest::Reporter* reporter) {
1604 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001605
reed@google.com53effc52011-09-21 19:05:12 +00001606 static const SkPoint pts[] = {
1607 { 0, 0 },
1608 { SkIntToScalar(10), SkIntToScalar(10) },
1609 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1610 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1611 };
1612 p.moveTo(pts[0]);
1613 p.lineTo(pts[1]);
1614 p.quadTo(pts[2], pts[3]);
1615 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001616
reed@google.com53effc52011-09-21 19:05:12 +00001617 SkMatrix matrix;
1618 matrix.reset();
1619 p.transform(matrix, &p1);
1620 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001621
reed@google.com53effc52011-09-21 19:05:12 +00001622 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1623 p.transform(matrix, &p1);
1624 SkPoint pts1[7];
1625 int count = p1.getPoints(pts1, 7);
1626 REPORTER_ASSERT(reporter, 7 == count);
1627 for (int i = 0; i < count; ++i) {
1628 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1629 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1630 }
1631}
1632
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001633static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001634 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001635 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001636
caryclark@google.com56f233a2012-11-19 13:06:06 +00001637 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001638 const char* testPath;
1639 const size_t numResultPts;
1640 const SkRect resultBound;
1641 const SkPath::Verb* resultVerbs;
1642 const size_t numResultVerbs;
1643 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001644
schenney@chromium.org7e963602012-06-13 17:05:43 +00001645 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1646 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1647 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1648 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1649 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1650 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1651 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1652 static const SkPath::Verb resultVerbs8[] = {
1653 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1654 };
1655 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1656 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1657 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1658 static const SkPath::Verb resultVerbs12[] = {
1659 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1660 };
1661 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1662 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1663 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1664 static const SkPath::Verb resultVerbs16[] = {
1665 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1666 };
1667 static const struct zeroPathTestData gZeroLengthTests[] = {
1668 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001669 { "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 +00001670 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001671 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1672 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1673 { "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) },
1674 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1675 { "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) },
1676 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1677 { "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) },
1678 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1679 { "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) },
1680 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1681 { "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 +00001682 SK_ARRAY_COUNT(resultVerbs14)
1683 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001684 { "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) },
1685 { "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 +00001686 SK_ARRAY_COUNT(resultVerbs16)
1687 }
1688 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001689
schenney@chromium.org7e963602012-06-13 17:05:43 +00001690 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1691 p.reset();
1692 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1693 REPORTER_ASSERT(reporter, valid);
1694 REPORTER_ASSERT(reporter, !p.isEmpty());
1695 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1696 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1697 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1698 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1699 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1700 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001701 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001702}
1703
1704struct SegmentInfo {
1705 SkPath fPath;
1706 int fPointCount;
1707};
1708
reed@google.com10296cc2011-09-21 12:29:05 +00001709#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1710
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001711static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001712 SkPath p, p2;
1713
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001714 p.moveTo(0, 0);
1715 p.quadTo(100, 100, 200, 200);
1716 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1717 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001718 p2 = p;
1719 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001720 p.cubicTo(100, 100, 200, 200, 300, 300);
1721 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1722 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001723 p2 = p;
1724 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1725
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001726 p.reset();
1727 p.moveTo(0, 0);
1728 p.cubicTo(100, 100, 200, 200, 300, 300);
1729 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001730 p2 = p;
1731 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001732
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001733 REPORTER_ASSERT(reporter, !p.isEmpty());
1734}
1735
1736static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001737 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001738 SkPoint pts[4];
1739
1740 // Test an iterator with no path
1741 SkPath::Iter noPathIter;
1742 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001743
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001744 // Test that setting an empty path works
1745 noPathIter.setPath(p, false);
1746 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001747
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001748 // Test that close path makes no difference for an empty path
1749 noPathIter.setPath(p, true);
1750 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001751
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001752 // Test an iterator with an initial empty path
1753 SkPath::Iter iter(p, false);
1754 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1755
1756 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001757 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001758 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1759
rmistry@google.comd6176b02012-08-23 18:14:13 +00001760
schenney@chromium.org7e963602012-06-13 17:05:43 +00001761 struct iterTestData {
1762 const char* testPath;
1763 const bool forceClose;
1764 const bool consumeDegenerates;
1765 const size_t* numResultPtsPerVerb;
1766 const SkPoint* resultPts;
1767 const SkPath::Verb* resultVerbs;
1768 const size_t numResultVerbs;
1769 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001770
schenney@chromium.org7e963602012-06-13 17:05:43 +00001771 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1772 static const SkPath::Verb resultVerbs2[] = {
1773 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1774 };
1775 static const SkPath::Verb resultVerbs3[] = {
1776 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1777 };
1778 static const SkPath::Verb resultVerbs4[] = {
1779 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1780 };
1781 static const SkPath::Verb resultVerbs5[] = {
1782 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1783 };
1784 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001785 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1786 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1787 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1788 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001789 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001790 static const SkPoint resultPts2[] = {
1791 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1792 };
1793 static const SkPoint resultPts3[] = {
1794 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1795 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1796 };
1797 static const SkPoint resultPts4[] = {
1798 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1799 };
1800 static const SkPoint resultPts5[] = {
1801 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1802 };
1803 static const struct iterTestData gIterTests[] = {
1804 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001805 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1806 { "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 +00001807 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1808 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1809 { "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) },
1810 { "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 +00001811 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1812 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1813 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1814 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1815 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1816 { "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 +00001817 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001818
schenney@chromium.org7e963602012-06-13 17:05:43 +00001819 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1820 p.reset();
1821 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1822 REPORTER_ASSERT(reporter, valid);
1823 iter.setPath(p, gIterTests[i].forceClose);
1824 int j = 0, l = 0;
1825 do {
1826 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1827 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1828 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1829 }
1830 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1831 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1832 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001833
1834 // The GM degeneratesegments.cpp test is more extensive
1835}
1836
1837static void test_raw_iter(skiatest::Reporter* reporter) {
1838 SkPath p;
1839 SkPoint pts[4];
1840
1841 // Test an iterator with no path
1842 SkPath::RawIter noPathIter;
1843 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1844 // Test that setting an empty path works
1845 noPathIter.setPath(p);
1846 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001847
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001848 // Test an iterator with an initial empty path
1849 SkPath::RawIter iter(p);
1850 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1851
1852 // Test that a move-only path returns the move.
1853 p.moveTo(SK_Scalar1, 0);
1854 iter.setPath(p);
1855 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1856 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1857 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1859
1860 // No matter how many moves we add, we should get them all back
1861 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1862 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1863 iter.setPath(p);
1864 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1865 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1866 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1867 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1868 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1869 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1870 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1871 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1872 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1873 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1874
1875 // Initial close is never ever stored
1876 p.reset();
1877 p.close();
1878 iter.setPath(p);
1879 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1880
1881 // Move/close sequences
1882 p.reset();
1883 p.close(); // Not stored, no purpose
1884 p.moveTo(SK_Scalar1, 0);
1885 p.close();
1886 p.close(); // Not stored, no purpose
1887 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1888 p.close();
1889 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1890 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1891 p.close();
1892 iter.setPath(p);
1893 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1894 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1895 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1896 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1897 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1898 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1899 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1900 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1901 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1902 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1903 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1904 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1905 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1906 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1907 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1908 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1909 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1910 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1911 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1912 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1913 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1914 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1915
1916 // Generate random paths and verify
1917 SkPoint randomPts[25];
1918 for (int i = 0; i < 5; ++i) {
1919 for (int j = 0; j < 5; ++j) {
1920 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1921 }
1922 }
1923
1924 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001925 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001926 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001927 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001928 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001929
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001930 for (int i = 0; i < 500; ++i) {
1931 p.reset();
1932 bool lastWasClose = true;
1933 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001934 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001935 int numPoints = 0;
1936 int numVerbs = (rand.nextU() >> 16) % 10;
1937 int numIterVerbs = 0;
1938 for (int j = 0; j < numVerbs; ++j) {
1939 do {
1940 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1941 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001942 switch (nextVerb) {
1943 case SkPath::kMove_Verb:
1944 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1945 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001946 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001947 numPoints += 1;
1948 lastWasClose = false;
1949 haveMoveTo = true;
1950 break;
1951 case SkPath::kLine_Verb:
1952 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001953 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001954 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1955 haveMoveTo = true;
1956 }
1957 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1958 p.lineTo(expectedPts[numPoints]);
1959 numPoints += 1;
1960 lastWasClose = false;
1961 break;
1962 case SkPath::kQuad_Verb:
1963 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001964 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001965 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1966 haveMoveTo = true;
1967 }
1968 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1969 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1970 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1971 numPoints += 2;
1972 lastWasClose = false;
1973 break;
reed@google.com277c3f82013-05-31 15:17:50 +00001974 case SkPath::kConic_Verb:
1975 if (!haveMoveTo) {
1976 expectedPts[numPoints++] = lastMoveToPt;
1977 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1978 haveMoveTo = true;
1979 }
1980 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1981 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1982 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1983 rand.nextUScalar1() * 4);
1984 numPoints += 2;
1985 lastWasClose = false;
1986 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001987 case SkPath::kCubic_Verb:
1988 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001989 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001990 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1991 haveMoveTo = true;
1992 }
1993 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1994 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1995 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1996 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1997 expectedPts[numPoints + 2]);
1998 numPoints += 3;
1999 lastWasClose = false;
2000 break;
2001 case SkPath::kClose_Verb:
2002 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002003 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002004 lastWasClose = true;
2005 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002006 default:
2007 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002008 }
2009 expectedVerbs[numIterVerbs++] = nextVerb;
2010 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002011
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002012 iter.setPath(p);
2013 numVerbs = numIterVerbs;
2014 numIterVerbs = 0;
2015 int numIterPts = 0;
2016 SkPoint lastMoveTo;
2017 SkPoint lastPt;
2018 lastMoveTo.set(0, 0);
2019 lastPt.set(0, 0);
2020 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2021 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2022 numIterVerbs++;
2023 switch (nextVerb) {
2024 case SkPath::kMove_Verb:
2025 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2026 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2027 lastPt = lastMoveTo = pts[0];
2028 numIterPts += 1;
2029 break;
2030 case SkPath::kLine_Verb:
2031 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2032 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2033 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2034 lastPt = pts[1];
2035 numIterPts += 1;
2036 break;
2037 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002038 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002039 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2040 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2041 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2042 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2043 lastPt = pts[2];
2044 numIterPts += 2;
2045 break;
2046 case SkPath::kCubic_Verb:
2047 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2048 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2049 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2050 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2051 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2052 lastPt = pts[3];
2053 numIterPts += 3;
2054 break;
2055 case SkPath::kClose_Verb:
2056 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2057 lastPt = lastMoveTo;
2058 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002059 default:
2060 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002061 }
2062 }
2063 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2064 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2065 }
2066}
2067
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002068static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002069 const SkPath& path,
2070 bool expectedCircle,
2071 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002072 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002073 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2074 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002075
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002076 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002077 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2078 }
2079}
2080
2081static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002082 const SkPath& path,
2083 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002084 SkPath tmp;
2085
2086 SkMatrix m;
2087 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2088 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002089 // this matrix reverses the direction.
2090 if (SkPath::kCCW_Direction == dir) {
2091 dir = SkPath::kCW_Direction;
2092 } else {
2093 SkASSERT(SkPath::kCW_Direction == dir);
2094 dir = SkPath::kCCW_Direction;
2095 }
2096 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002097}
2098
2099static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002100 const SkPath& path,
2101 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002102 SkPath tmp;
2103
2104 // translate at small offset
2105 SkMatrix m;
2106 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2107 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002108 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002109
2110 tmp.reset();
2111 m.reset();
2112
2113 // translate at a relatively big offset
2114 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2115 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002116 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002117}
2118
2119static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002120 const SkPath& path,
2121 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002122 for (int angle = 0; angle < 360; ++angle) {
2123 SkPath tmp;
2124 SkMatrix m;
2125 m.setRotate(SkIntToScalar(angle));
2126 path.transform(m, &tmp);
2127
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002128 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002129 // degrees is not an oval anymore, this can be improved. we made this
2130 // for the simplicity of our implementation.
2131 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002132 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002133 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002134 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002135 }
2136 }
2137}
2138
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002139static void test_circle_mirror_x(skiatest::Reporter* reporter,
2140 const SkPath& path,
2141 SkPath::Direction dir) {
2142 SkPath tmp;
2143 SkMatrix m;
2144 m.reset();
2145 m.setScaleX(-SK_Scalar1);
2146 path.transform(m, &tmp);
2147
2148 if (SkPath::kCW_Direction == dir) {
2149 dir = SkPath::kCCW_Direction;
2150 } else {
2151 SkASSERT(SkPath::kCCW_Direction == dir);
2152 dir = SkPath::kCW_Direction;
2153 }
2154
2155 check_for_circle(reporter, tmp, true, dir);
2156}
2157
2158static void test_circle_mirror_y(skiatest::Reporter* reporter,
2159 const SkPath& path,
2160 SkPath::Direction dir) {
2161 SkPath tmp;
2162 SkMatrix m;
2163 m.reset();
2164 m.setScaleY(-SK_Scalar1);
2165 path.transform(m, &tmp);
2166
2167 if (SkPath::kCW_Direction == dir) {
2168 dir = SkPath::kCCW_Direction;
2169 } else {
2170 SkASSERT(SkPath::kCCW_Direction == dir);
2171 dir = SkPath::kCW_Direction;
2172 }
2173
2174 check_for_circle(reporter, tmp, true, dir);
2175}
2176
2177static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2178 const SkPath& path,
2179 SkPath::Direction dir) {
2180 SkPath tmp;
2181 SkMatrix m;
2182 m.reset();
2183 m.setScaleX(-SK_Scalar1);
2184 m.setScaleY(-SK_Scalar1);
2185 path.transform(m, &tmp);
2186
2187 check_for_circle(reporter, tmp, true, dir);
2188}
2189
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002190static void test_circle_with_direction(skiatest::Reporter* reporter,
2191 SkPath::Direction dir) {
2192 SkPath path;
2193
2194 // circle at origin
2195 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002196 check_for_circle(reporter, path, true, dir);
2197 test_circle_rotate(reporter, path, dir);
2198 test_circle_translate(reporter, path, dir);
2199 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002200
2201 // circle at an offset at (10, 10)
2202 path.reset();
2203 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2204 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002205 check_for_circle(reporter, path, true, dir);
2206 test_circle_rotate(reporter, path, dir);
2207 test_circle_translate(reporter, path, dir);
2208 test_circle_skew(reporter, path, dir);
2209 test_circle_mirror_x(reporter, path, dir);
2210 test_circle_mirror_y(reporter, path, dir);
2211 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002212}
2213
2214static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2215 SkPath path;
2216 SkPath circle;
2217 SkPath rect;
2218 SkPath empty;
2219
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002220 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2221 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2222
2223 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002224 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2225 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2226
2227 SkMatrix translate;
2228 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2229
2230 // For simplicity, all the path concatenation related operations
2231 // would mark it non-circle, though in theory it's still a circle.
2232
2233 // empty + circle (translate)
2234 path = empty;
2235 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002236 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002237
2238 // circle + empty (translate)
2239 path = circle;
2240 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002241 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002242
2243 // test reverseAddPath
2244 path = circle;
2245 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002246 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002247}
2248
2249static void test_circle(skiatest::Reporter* reporter) {
2250 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2251 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2252
2253 // multiple addCircle()
2254 SkPath path;
2255 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2256 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002257 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002258
2259 // some extra lineTo() would make isOval() fail
2260 path.reset();
2261 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2262 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002263 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002264
2265 // not back to the original point
2266 path.reset();
2267 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2268 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002269 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002270
2271 test_circle_with_add_paths(reporter);
2272}
2273
2274static void test_oval(skiatest::Reporter* reporter) {
2275 SkRect rect;
2276 SkMatrix m;
2277 SkPath path;
2278
2279 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2280 path.addOval(rect);
2281
2282 REPORTER_ASSERT(reporter, path.isOval(NULL));
2283
2284 m.setRotate(SkIntToScalar(90));
2285 SkPath tmp;
2286 path.transform(m, &tmp);
2287 // an oval rotated 90 degrees is still an oval.
2288 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2289
2290 m.reset();
2291 m.setRotate(SkIntToScalar(30));
2292 tmp.reset();
2293 path.transform(m, &tmp);
2294 // an oval rotated 30 degrees is not an oval anymore.
2295 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2296
2297 // since empty path being transformed.
2298 path.reset();
2299 tmp.reset();
2300 m.reset();
2301 path.transform(m, &tmp);
2302 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2303
2304 // empty path is not an oval
2305 tmp.reset();
2306 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2307
2308 // only has moveTo()s
2309 tmp.reset();
2310 tmp.moveTo(0, 0);
2311 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2312 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2313
2314 // mimic WebKit's calling convention,
2315 // call moveTo() first and then call addOval()
2316 path.reset();
2317 path.moveTo(0, 0);
2318 path.addOval(rect);
2319 REPORTER_ASSERT(reporter, path.isOval(NULL));
2320
2321 // copy path
2322 path.reset();
2323 tmp.reset();
2324 tmp.addOval(rect);
2325 path = tmp;
2326 REPORTER_ASSERT(reporter, path.isOval(NULL));
2327}
2328
bungeman@google.coma5809a32013-06-21 15:13:34 +00002329static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2330 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002331
reed@android.com3abec1d2009-03-02 05:36:20 +00002332 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002333 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002334 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002335 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002336 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002337 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2338 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002339 REPORTER_ASSERT(reporter, p == empty);
2340 REPORTER_ASSERT(reporter, !(p != empty));
2341}
2342
2343static void TestPath(skiatest::Reporter* reporter) {
2344 SkTSize<SkScalar>::Make(3,4);
2345
2346 SkPath p, empty;
2347 SkRect bounds, bounds2;
2348 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002349
reed@android.comd252db02009-04-01 18:31:44 +00002350 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002351
reed@android.com3abec1d2009-03-02 05:36:20 +00002352 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002353
reed@android.com6b82d1a2009-06-03 02:35:01 +00002354 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2355 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002356 // we have quads or cubics
2357 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002358 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002359
reed@android.com6b82d1a2009-06-03 02:35:01 +00002360 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002361 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002362
reed@android.com6b82d1a2009-06-03 02:35:01 +00002363 p.addOval(bounds);
2364 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002365 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002366
bungeman@google.coma5809a32013-06-21 15:13:34 +00002367 p.rewind();
2368 test_empty(reporter, p);
2369
reed@android.com3abec1d2009-03-02 05:36:20 +00002370 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002371 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002372 // we have only lines
2373 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002374 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002375
bungeman@google.coma5809a32013-06-21 15:13:34 +00002376 REPORTER_ASSERT(reporter, p != empty);
2377 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002378
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002379 // do getPoints and getVerbs return the right result
2380 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2381 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002382 SkPoint pts[4];
2383 int count = p.getPoints(pts, 4);
2384 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002385 uint8_t verbs[6];
2386 verbs[5] = 0xff;
2387 p.getVerbs(verbs, 5);
2388 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2389 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2390 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2391 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2392 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2393 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002394 bounds2.set(pts, 4);
2395 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002396
reed@android.com3abec1d2009-03-02 05:36:20 +00002397 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2398 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002399 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002400
reed@android.com3abec1d2009-03-02 05:36:20 +00002401 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002402 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002403 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2404 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002405
reed@android.com3abec1d2009-03-02 05:36:20 +00002406 // now force p to not be a rect
2407 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2408 p.addRect(bounds);
2409 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002410
reed@google.comda2b21f2013-06-21 17:32:32 +00002411 // test isRect for a trailing moveTo
2412 {
2413 SkRect r;
2414 p.reset();
2415 p.addRect(bounds);
2416 REPORTER_ASSERT(reporter, p.isRect(&r));
2417 REPORTER_ASSERT(reporter, r == bounds);
2418 // add a moveTo outside of our bounds
2419 p.moveTo(bounds.fLeft + 10, bounds.fBottom + 10);
2420 REPORTER_ASSERT(reporter, p.isRect(&r));
2421 REPORTER_ASSERT(reporter, r == bounds);
2422 }
2423
reed@google.com7e6c4d12012-05-10 14:05:43 +00002424 test_isLine(reporter);
2425 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002426 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002427 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002428 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002429 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002430 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002431 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002432 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002433 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002434 test_flattening(reporter);
2435 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002436 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002437 test_iter(reporter);
2438 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002439 test_circle(reporter);
2440 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002441 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002442 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002443 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002444 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002445 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002446 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002447 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002448 test_tricky_cubic();
2449 test_clipped_cubic();
2450 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002451 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002452 test_bad_cubic_crbug234190();
reed@android.com3abec1d2009-03-02 05:36:20 +00002453}
2454
2455#include "TestClassDef.h"
2456DEFINE_TESTCLASS("Path", PathTestClass, TestPath)