blob: e698c7cda5fe4c777e1cbb75a064e398d64c8783 [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.com8cae8352012-09-14 15:18:41 +000018#include "SkSurface.h"
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000019#include "SkTypes.h"
20#include "SkWriter32.h"
reed@google.com8cae8352012-09-14 15:18:41 +000021
caryclark@google.com56f233a2012-11-19 13:06:06 +000022#if defined(WIN32)
23 #define SUPPRESS_VISIBILITY_WARNING
24#else
25 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
26#endif
27
reed@google.com8cae8352012-09-14 15:18:41 +000028static SkSurface* new_surface(int w, int h) {
29 SkImage::Info info = {
30 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
31 };
mike@reedtribe.orgb9476252012-11-15 02:37:45 +000032 return SkSurface::NewRaster(info);
reed@google.com8cae8352012-09-14 15:18:41 +000033}
34
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000035static void test_android_specific_behavior(skiatest::Reporter* reporter) {
36#ifdef SK_BUILD_FOR_ANDROID
37 // Copy constructor should preserve generation ID, but assignment shouldn't.
38 SkPath original;
39 original.moveTo(0, 0);
40 original.lineTo(1, 1);
41 REPORTER_ASSERT(reporter, original.getGenerationID() > 0);
42
43 const SkPath copy(original);
44 REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
45
46 SkPath assign;
47 assign = original;
48 REPORTER_ASSERT(reporter, assign.getGenerationID() != original.getGenerationID());
49#endif
50}
51
reed@google.com3eff3592013-05-08 21:08:21 +000052// This used to assert in the debug build, as the edges did not all line-up.
53static void test_bad_cubic_crbug234190() {
54 SkPath path;
55 path.moveTo(13.8509f, 3.16858f);
56 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
57 -2.38991e+08f, -4.26573e+08f,
58 -2.41016e+08f, -4.30188e+08f);
59
60 SkPaint paint;
61 paint.setAntiAlias(true);
62 SkAutoTUnref<SkSurface> surface(new_surface(84, 88));
63 surface->getCanvas()->drawPath(path, paint);
64}
65
reed@google.com7a90daf2013-04-10 18:44:00 +000066static void test_bad_cubic_crbug229478() {
67 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000068 { 4595.91064f, -11596.9873f },
69 { 4597.2168f, -11595.9414f },
70 { 4598.52344f, -11594.8955f },
71 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +000072 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000073
reed@google.com7a90daf2013-04-10 18:44:00 +000074 SkPath path;
75 path.moveTo(pts[0]);
76 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000077
reed@google.com7a90daf2013-04-10 18:44:00 +000078 SkPaint paint;
79 paint.setStyle(SkPaint::kStroke_Style);
80 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +000081
reed@google.com7a90daf2013-04-10 18:44:00 +000082 SkPath dst;
83 // Before the fix, this would infinite-recurse, and run out of stack
84 // because we would keep trying to subdivide a degenerate cubic segment.
85 paint.getFillPath(path, &dst, NULL);
86}
87
reed@google.com64d62952013-01-18 17:49:28 +000088static void build_path_170666(SkPath& path) {
89 path.moveTo(17.9459f, 21.6344f);
90 path.lineTo(139.545f, -47.8105f);
91 path.lineTo(139.545f, -47.8105f);
92 path.lineTo(131.07f, -47.3888f);
93 path.lineTo(131.07f, -47.3888f);
94 path.lineTo(122.586f, -46.9532f);
95 path.lineTo(122.586f, -46.9532f);
96 path.lineTo(18076.6f, 31390.9f);
97 path.lineTo(18076.6f, 31390.9f);
98 path.lineTo(18085.1f, 31390.5f);
99 path.lineTo(18085.1f, 31390.5f);
100 path.lineTo(18076.6f, 31390.9f);
101 path.lineTo(18076.6f, 31390.9f);
102 path.lineTo(17955, 31460.3f);
103 path.lineTo(17955, 31460.3f);
104 path.lineTo(17963.5f, 31459.9f);
105 path.lineTo(17963.5f, 31459.9f);
106 path.lineTo(17971.9f, 31459.5f);
107 path.lineTo(17971.9f, 31459.5f);
108 path.lineTo(17.9551f, 21.6205f);
109 path.lineTo(17.9551f, 21.6205f);
110 path.lineTo(9.47091f, 22.0561f);
111 path.lineTo(9.47091f, 22.0561f);
112 path.lineTo(17.9459f, 21.6344f);
113 path.lineTo(17.9459f, 21.6344f);
114 path.close();path.moveTo(0.995934f, 22.4779f);
115 path.lineTo(0.986725f, 22.4918f);
116 path.lineTo(0.986725f, 22.4918f);
117 path.lineTo(17955, 31460.4f);
118 path.lineTo(17955, 31460.4f);
119 path.lineTo(17971.9f, 31459.5f);
120 path.lineTo(17971.9f, 31459.5f);
121 path.lineTo(18093.6f, 31390.1f);
122 path.lineTo(18093.6f, 31390.1f);
123 path.lineTo(18093.6f, 31390);
124 path.lineTo(18093.6f, 31390);
125 path.lineTo(139.555f, -47.8244f);
126 path.lineTo(139.555f, -47.8244f);
127 path.lineTo(122.595f, -46.9671f);
128 path.lineTo(122.595f, -46.9671f);
129 path.lineTo(0.995934f, 22.4779f);
130 path.lineTo(0.995934f, 22.4779f);
131 path.close();
132 path.moveTo(5.43941f, 25.5223f);
133 path.lineTo(798267, -28871.1f);
134 path.lineTo(798267, -28871.1f);
135 path.lineTo(3.12512e+06f, -113102);
136 path.lineTo(3.12512e+06f, -113102);
137 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
138 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
139 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
140 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
141 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
142 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
143 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
144 path.lineTo(2.78271e+08f, -1.00733e+07f);
145 path.lineTo(2.78271e+08f, -1.00733e+07f);
146 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
147 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
148 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
149 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
150 path.lineTo(2.77473e+08f, -1.00444e+07f);
151 path.lineTo(2.77473e+08f, -1.00444e+07f);
152 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
153 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
154 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
155 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
156 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
157 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
158 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
159 path.lineTo(798284, -28872);
160 path.lineTo(798284, -28872);
161 path.lineTo(22.4044f, 24.6677f);
162 path.lineTo(22.4044f, 24.6677f);
163 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
164 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
165 path.close();
166}
167
168static void build_path_simple_170666(SkPath& path) {
169 path.moveTo(126.677f, 24.1591f);
170 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
171}
172
173// This used to assert in the SK_DEBUG build, as the clip step would fail with
174// too-few interations in our cubic-line intersection code. That code now runs
175// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000176static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000177 SkPath path;
178 SkPaint paint;
179 paint.setAntiAlias(true);
180
181 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000182
reed@google.com64d62952013-01-18 17:49:28 +0000183 build_path_simple_170666(path);
184 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000185
reed@google.com64d62952013-01-18 17:49:28 +0000186 build_path_170666(path);
187 surface->getCanvas()->drawPath(path, paint);
188}
189
reed@google.coma8790de2012-10-24 21:04:04 +0000190// Make sure we stay non-finite once we get there (unless we reset or rewind).
191static void test_addrect_isfinite(skiatest::Reporter* reporter) {
192 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000193
reed@google.coma8790de2012-10-24 21:04:04 +0000194 path.addRect(SkRect::MakeWH(50, 100));
195 REPORTER_ASSERT(reporter, path.isFinite());
196
197 path.moveTo(0, 0);
198 path.lineTo(SK_ScalarInfinity, 42);
199 REPORTER_ASSERT(reporter, !path.isFinite());
200
201 path.addRect(SkRect::MakeWH(50, 100));
202 REPORTER_ASSERT(reporter, !path.isFinite());
203
204 path.reset();
205 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000206
reed@google.coma8790de2012-10-24 21:04:04 +0000207 path.addRect(SkRect::MakeWH(50, 100));
208 REPORTER_ASSERT(reporter, path.isFinite());
209}
210
reed@google.com848148e2013-01-15 15:51:59 +0000211static void build_big_path(SkPath* path, bool reducedCase) {
212 if (reducedCase) {
213 path->moveTo(577330, 1971.72f);
214 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
215 } else {
216 path->moveTo(60.1631f, 7.70567f);
217 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
218 path->lineTo(577379, 1977.77f);
219 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
220 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
221 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
222 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
223 path->quadTo(577198, 1972, 577238, 1971.31f);
224 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
225 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
226 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
227 path->moveTo(306.718f, -32.912f);
228 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
229 }
230}
231
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000232static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000233 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
234
235 // This path used to assert, because our cubic-chopping code incorrectly
236 // moved control points after the chop. This test should be run in SK_DEBUG
237 // mode to ensure that we no long assert.
238 SkPath path;
239 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
240 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000241
reed@google.com848148e2013-01-15 15:51:59 +0000242 SkPaint paint;
243 for (int doAA = 0; doAA <= 1; ++doAA) {
244 paint.setAntiAlias(SkToBool(doAA));
245 surface->getCanvas()->drawPath(path, paint);
246 }
247 }
248}
249
reed@google.com8cae8352012-09-14 15:18:41 +0000250// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
251// which triggered an assert, from a tricky cubic. This test replicates that
252// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
253// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000254static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000255 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000256 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
257 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
258 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
259 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000260 };
261
262 SkPath path;
263 path.moveTo(pts[0]);
264 path.cubicTo(pts[1], pts[2], pts[3]);
265
266 SkPaint paint;
267 paint.setAntiAlias(true);
268
269 SkSurface* surface = new_surface(19, 130);
270 surface->getCanvas()->drawPath(path, paint);
271 surface->unref();
272}
reed@android.com3abec1d2009-03-02 05:36:20 +0000273
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000274// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
275//
276static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
277 SkPath path;
278 path.quadTo(157, 366, 286, 208);
279 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000280
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000281 SkMatrix matrix;
282 matrix.setScale(1000*1000, 1000*1000);
283
284 // Be sure that path::transform correctly updates isFinite and the bounds
285 // if the transformation overflows. The previous bug was that isFinite was
286 // set to true in this case, but the bounds were not set to empty (which
287 // they should be).
288 while (path.isFinite()) {
289 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
290 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
291 path.transform(matrix);
292 }
293 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
294
295 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
296 path.transform(matrix);
297 // we need to still be non-finite
298 REPORTER_ASSERT(reporter, !path.isFinite());
299 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
300}
301
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000302static void add_corner_arc(SkPath* path, const SkRect& rect,
303 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000304 int startAngle)
305{
306
307 SkScalar rx = SkMinScalar(rect.width(), xIn);
308 SkScalar ry = SkMinScalar(rect.height(), yIn);
309
310 SkRect arcRect;
311 arcRect.set(-rx, -ry, rx, ry);
312 switch (startAngle) {
313 case 0:
314 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
315 break;
316 case 90:
317 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
318 break;
319 case 180:
320 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
321 break;
322 case 270:
323 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
324 break;
325 default:
326 break;
327 }
328
329 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
330}
331
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000332static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000333 SkScalar xCorner, SkScalar yCorner) {
334 // we are lazy here and use the same x & y for each corner
335 add_corner_arc(path, r, xCorner, yCorner, 270);
336 add_corner_arc(path, r, xCorner, yCorner, 0);
337 add_corner_arc(path, r, xCorner, yCorner, 90);
338 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000339 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000340}
341
342// Chrome creates its own round rects with each corner possibly being different.
343// Performance will suffer if they are not convex.
344// Note: PathBench::ArbRoundRectBench performs almost exactly
345// the same test (but with drawing)
346static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000347 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000348 SkRect r;
349
350 for (int i = 0; i < 5000; ++i) {
351
robertphillips@google.com158618e2012-10-23 16:56:56 +0000352 SkScalar size = rand.nextUScalar1() * 30;
353 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000354 continue;
355 }
356 r.fLeft = rand.nextUScalar1() * 300;
357 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000358 r.fRight = r.fLeft + 2 * size;
359 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000360
361 SkPath temp;
362
363 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
364
365 REPORTER_ASSERT(reporter, temp.isConvex());
366 }
367}
368
robertphillips@google.com158618e2012-10-23 16:56:56 +0000369// Chrome will sometimes create a 0 radius round rect. The degenerate
370// quads prevent the path from being converted to a rect
371// Note: PathBench::ArbRoundRectBench performs almost exactly
372// the same test (but with drawing)
373static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000374 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000375 SkRect r;
376
377 for (int i = 0; i < 5000; ++i) {
378
379 SkScalar size = rand.nextUScalar1() * 30;
380 if (size < SK_Scalar1) {
381 continue;
382 }
383 r.fLeft = rand.nextUScalar1() * 300;
384 r.fTop = rand.nextUScalar1() * 300;
385 r.fRight = r.fLeft + 2 * size;
386 r.fBottom = r.fTop + 2 * size;
387
388 SkPath temp;
389
390 make_arb_round_rect(&temp, r, 0, 0);
391
robertphillips@google.com158618e2012-10-23 16:56:56 +0000392 SkRect result;
393 REPORTER_ASSERT(reporter, temp.isRect(&result));
394 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000395 }
396}
397
reed@google.com0bb18bb2012-07-26 15:20:36 +0000398static void test_rect_isfinite(skiatest::Reporter* reporter) {
399 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000400 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000401 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000402
reed@google.com0bb18bb2012-07-26 15:20:36 +0000403 SkRect r;
404 r.setEmpty();
405 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000406 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000407 REPORTER_ASSERT(reporter, !r.isFinite());
408 r.set(0, 0, nan, 0);
409 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000410
reed@google.com0bb18bb2012-07-26 15:20:36 +0000411 SkPoint pts[] = {
412 { 0, 0 },
413 { SK_Scalar1, 0 },
414 { 0, SK_Scalar1 },
415 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000416
reed@google.com0bb18bb2012-07-26 15:20:36 +0000417 bool isFine = r.setBoundsCheck(pts, 3);
418 REPORTER_ASSERT(reporter, isFine);
419 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000420
reed@google.com0bb18bb2012-07-26 15:20:36 +0000421 pts[1].set(inf, 0);
422 isFine = r.setBoundsCheck(pts, 3);
423 REPORTER_ASSERT(reporter, !isFine);
424 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000425
reed@google.com0bb18bb2012-07-26 15:20:36 +0000426 pts[1].set(nan, 0);
427 isFine = r.setBoundsCheck(pts, 3);
428 REPORTER_ASSERT(reporter, !isFine);
429 REPORTER_ASSERT(reporter, r.isEmpty());
430}
431
432static void test_path_isfinite(skiatest::Reporter* reporter) {
433 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000434 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000435 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000436
reed@google.com0bb18bb2012-07-26 15:20:36 +0000437 SkPath path;
438 REPORTER_ASSERT(reporter, path.isFinite());
439
440 path.reset();
441 REPORTER_ASSERT(reporter, path.isFinite());
442
443 path.reset();
444 path.moveTo(SK_Scalar1, 0);
445 REPORTER_ASSERT(reporter, path.isFinite());
446
447 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000448 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000449 REPORTER_ASSERT(reporter, !path.isFinite());
450
451 path.reset();
452 path.moveTo(nan, 0);
453 REPORTER_ASSERT(reporter, !path.isFinite());
454}
455
456static void test_isfinite(skiatest::Reporter* reporter) {
457 test_rect_isfinite(reporter);
458 test_path_isfinite(reporter);
459}
460
reed@google.com744faba2012-05-29 19:54:52 +0000461// assert that we always
462// start with a moveTo
463// only have 1 moveTo
464// only have Lines after that
465// end with a single close
466// only have (at most) 1 close
467//
468static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000469 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000470 SkPath::RawIter iter(path);
471 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000472
473 bool firstTime = true;
474 bool foundClose = false;
475 for (;;) {
476 switch (iter.next(pts)) {
477 case SkPath::kMove_Verb:
478 REPORTER_ASSERT(reporter, firstTime);
479 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
480 srcPts++;
481 firstTime = false;
482 break;
483 case SkPath::kLine_Verb:
484 REPORTER_ASSERT(reporter, !firstTime);
485 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
486 srcPts++;
487 break;
488 case SkPath::kQuad_Verb:
489 REPORTER_ASSERT(reporter, !"unexpected quad verb");
490 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000491 case SkPath::kConic_Verb:
492 REPORTER_ASSERT(reporter, !"unexpected conic verb");
493 break;
reed@google.com744faba2012-05-29 19:54:52 +0000494 case SkPath::kCubic_Verb:
495 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
496 break;
497 case SkPath::kClose_Verb:
498 REPORTER_ASSERT(reporter, !firstTime);
499 REPORTER_ASSERT(reporter, !foundClose);
500 REPORTER_ASSERT(reporter, expectClose);
501 foundClose = true;
502 break;
503 case SkPath::kDone_Verb:
504 goto DONE;
505 }
506 }
507DONE:
508 REPORTER_ASSERT(reporter, foundClose == expectClose);
509}
510
511static void test_addPoly(skiatest::Reporter* reporter) {
512 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000513 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000514
reed@google.com744faba2012-05-29 19:54:52 +0000515 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
516 pts[i].fX = rand.nextSScalar1();
517 pts[i].fY = rand.nextSScalar1();
518 }
519
520 for (int doClose = 0; doClose <= 1; ++doClose) {
521 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
522 SkPath path;
523 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000524 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000525 }
526 }
527}
528
reed@google.com8b06f1a2012-05-29 12:03:46 +0000529static void test_strokerec(skiatest::Reporter* reporter) {
530 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
531 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000532
reed@google.com8b06f1a2012-05-29 12:03:46 +0000533 rec.setHairlineStyle();
534 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000535
reed@google.com8b06f1a2012-05-29 12:03:46 +0000536 rec.setStrokeStyle(SK_Scalar1, false);
537 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000538
reed@google.com8b06f1a2012-05-29 12:03:46 +0000539 rec.setStrokeStyle(SK_Scalar1, true);
540 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000541
reed@google.com8b06f1a2012-05-29 12:03:46 +0000542 rec.setStrokeStyle(0, false);
543 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000544
reed@google.com8b06f1a2012-05-29 12:03:46 +0000545 rec.setStrokeStyle(0, true);
546 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
547}
548
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000549// Set this for paths that don't have a consistent direction such as a bowtie.
550// (cheapComputeDirection is not expected to catch these.)
551static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
552
553static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
554 SkPath::Direction expected) {
555 if (expected == kDontCheckDir) {
556 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000557 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000558 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
559
560 SkPath::Direction dir;
561 if (copy.cheapComputeDirection(&dir)) {
562 REPORTER_ASSERT(reporter, dir == expected);
563 } else {
564 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
565 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000566}
567
reed@google.com3e71a882012-01-10 18:44:37 +0000568static void test_direction(skiatest::Reporter* reporter) {
569 size_t i;
570 SkPath path;
571 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
572 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
573 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000574 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000575
576 static const char* gDegen[] = {
577 "M 10 10",
578 "M 10 10 M 20 20",
579 "M 10 10 L 20 20",
580 "M 10 10 L 10 10 L 10 10",
581 "M 10 10 Q 10 10 10 10",
582 "M 10 10 C 10 10 10 10 10 10",
583 };
584 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
585 path.reset();
586 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
587 REPORTER_ASSERT(reporter, valid);
588 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
589 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000590
reed@google.com3e71a882012-01-10 18:44:37 +0000591 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000592 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000593 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000594 "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 +0000595 // rect with top two corners replaced by cubics with identical middle
596 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000597 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
598 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000599 };
600 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
601 path.reset();
602 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
603 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000604 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000605 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000606
reed@google.com3e71a882012-01-10 18:44:37 +0000607 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000608 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000609 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000610 "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 +0000611 // rect with top two corners replaced by cubics with identical middle
612 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000613 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
614 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000615 };
616 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
617 path.reset();
618 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
619 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000620 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000621 }
reed@google.comac8543f2012-01-30 20:51:25 +0000622
623 // Test two donuts, each wound a different direction. Only the outer contour
624 // determines the cheap direction
625 path.reset();
626 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
627 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000628 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000629
reed@google.comac8543f2012-01-30 20:51:25 +0000630 path.reset();
631 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
632 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000633 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000634
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000635#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000636 // triangle with one point really far from the origin.
637 path.reset();
638 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000639 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
640 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
641 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000642 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000643#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000644}
645
reed@google.comffdb0182011-11-14 19:29:14 +0000646static void add_rect(SkPath* path, const SkRect& r) {
647 path->moveTo(r.fLeft, r.fTop);
648 path->lineTo(r.fRight, r.fTop);
649 path->lineTo(r.fRight, r.fBottom);
650 path->lineTo(r.fLeft, r.fBottom);
651 path->close();
652}
653
654static void test_bounds(skiatest::Reporter* reporter) {
655 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000656 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
657 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
658 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
659 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000660 };
661
662 SkPath path0, path1;
663 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
664 path0.addRect(rects[i]);
665 add_rect(&path1, rects[i]);
666 }
667
668 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
669}
670
reed@google.com55b5f4b2011-09-07 12:23:41 +0000671static void stroke_cubic(const SkPoint pts[4]) {
672 SkPath path;
673 path.moveTo(pts[0]);
674 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000675
reed@google.com55b5f4b2011-09-07 12:23:41 +0000676 SkPaint paint;
677 paint.setStyle(SkPaint::kStroke_Style);
678 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000679
reed@google.com55b5f4b2011-09-07 12:23:41 +0000680 SkPath fill;
681 paint.getFillPath(path, &fill);
682}
683
684// just ensure this can run w/o any SkASSERTS firing in the debug build
685// we used to assert due to differences in how we determine a degenerate vector
686// but that was fixed with the introduction of SkPoint::CanNormalize
687static void stroke_tiny_cubic() {
688 SkPoint p0[] = {
689 { 372.0f, 92.0f },
690 { 372.0f, 92.0f },
691 { 372.0f, 92.0f },
692 { 372.0f, 92.0f },
693 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000694
reed@google.com55b5f4b2011-09-07 12:23:41 +0000695 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000696
reed@google.com55b5f4b2011-09-07 12:23:41 +0000697 SkPoint p1[] = {
698 { 372.0f, 92.0f },
699 { 372.0007f, 92.000755f },
700 { 371.99927f, 92.003922f },
701 { 371.99826f, 92.003899f },
702 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000703
reed@google.com55b5f4b2011-09-07 12:23:41 +0000704 stroke_cubic(p1);
705}
706
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000707static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
708 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000709 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000710 SkPoint mv;
711 SkPoint pts[4];
712 SkPath::Verb v;
713 int nMT = 0;
714 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000715 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000716 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
717 switch (v) {
718 case SkPath::kMove_Verb:
719 mv = pts[0];
720 ++nMT;
721 break;
722 case SkPath::kClose_Verb:
723 REPORTER_ASSERT(reporter, mv == pts[0]);
724 ++nCL;
725 break;
726 default:
727 break;
728 }
729 }
730 // if we force a close on the interator we should have a close
731 // for every moveTo
732 REPORTER_ASSERT(reporter, !i || nMT == nCL);
733 }
734}
735
736static void test_close(skiatest::Reporter* reporter) {
737 SkPath closePt;
738 closePt.moveTo(0, 0);
739 closePt.close();
740 check_close(reporter, closePt);
741
742 SkPath openPt;
743 openPt.moveTo(0, 0);
744 check_close(reporter, openPt);
745
746 SkPath empty;
747 check_close(reporter, empty);
748 empty.close();
749 check_close(reporter, empty);
750
751 SkPath rect;
752 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
753 check_close(reporter, rect);
754 rect.close();
755 check_close(reporter, rect);
756
757 SkPath quad;
758 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
759 check_close(reporter, quad);
760 quad.close();
761 check_close(reporter, quad);
762
763 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000764 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000765 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
766 check_close(reporter, cubic);
767 cubic.close();
768 check_close(reporter, cubic);
769
770 SkPath line;
771 line.moveTo(SK_Scalar1, SK_Scalar1);
772 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
773 check_close(reporter, line);
774 line.close();
775 check_close(reporter, line);
776
777 SkPath rect2;
778 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
779 rect2.close();
780 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
781 check_close(reporter, rect2);
782 rect2.close();
783 check_close(reporter, rect2);
784
785 SkPath oval3;
786 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
787 oval3.close();
788 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
789 check_close(reporter, oval3);
790 oval3.close();
791 check_close(reporter, oval3);
792
793 SkPath moves;
794 moves.moveTo(SK_Scalar1, SK_Scalar1);
795 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
796 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
797 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
798 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000799
800 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000801}
802
reed@google.com7c424812011-05-15 04:38:34 +0000803static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
804 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000805 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
806 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000807 REPORTER_ASSERT(reporter, c == expected);
808}
809
810static void test_convexity2(skiatest::Reporter* reporter) {
811 SkPath pt;
812 pt.moveTo(0, 0);
813 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000814 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000815 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000816
reed@google.com7c424812011-05-15 04:38:34 +0000817 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000818 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
819 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000820 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000821 check_convexity(reporter, line, SkPath::kConvex_Convexity);
822 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000823
reed@google.com7c424812011-05-15 04:38:34 +0000824 SkPath triLeft;
825 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000826 triLeft.lineTo(SK_Scalar1, 0);
827 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000828 triLeft.close();
829 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000830 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000831
reed@google.com7c424812011-05-15 04:38:34 +0000832 SkPath triRight;
833 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000834 triRight.lineTo(-SK_Scalar1, 0);
835 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000836 triRight.close();
837 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000838 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000839
reed@google.com7c424812011-05-15 04:38:34 +0000840 SkPath square;
841 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000842 square.lineTo(SK_Scalar1, 0);
843 square.lineTo(SK_Scalar1, SK_Scalar1);
844 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000845 square.close();
846 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000847 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000848
reed@google.com7c424812011-05-15 04:38:34 +0000849 SkPath redundantSquare;
850 redundantSquare.moveTo(0, 0);
851 redundantSquare.lineTo(0, 0);
852 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000853 redundantSquare.lineTo(SK_Scalar1, 0);
854 redundantSquare.lineTo(SK_Scalar1, 0);
855 redundantSquare.lineTo(SK_Scalar1, 0);
856 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
857 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
858 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
859 redundantSquare.lineTo(0, SK_Scalar1);
860 redundantSquare.lineTo(0, SK_Scalar1);
861 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000862 redundantSquare.close();
863 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000864 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000865
reed@google.com7c424812011-05-15 04:38:34 +0000866 SkPath bowTie;
867 bowTie.moveTo(0, 0);
868 bowTie.lineTo(0, 0);
869 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000870 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
871 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
872 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
873 bowTie.lineTo(SK_Scalar1, 0);
874 bowTie.lineTo(SK_Scalar1, 0);
875 bowTie.lineTo(SK_Scalar1, 0);
876 bowTie.lineTo(0, SK_Scalar1);
877 bowTie.lineTo(0, SK_Scalar1);
878 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000879 bowTie.close();
880 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000881 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000882
reed@google.com7c424812011-05-15 04:38:34 +0000883 SkPath spiral;
884 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000885 spiral.lineTo(100*SK_Scalar1, 0);
886 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
887 spiral.lineTo(0, 100*SK_Scalar1);
888 spiral.lineTo(0, 50*SK_Scalar1);
889 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
890 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000891 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000892 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000893 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000894
reed@google.com7c424812011-05-15 04:38:34 +0000895 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000896 dent.moveTo(0, 0);
897 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
898 dent.lineTo(0, 100*SK_Scalar1);
899 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
900 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000901 dent.close();
902 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000903 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000904}
905
reed@android.com6b82d1a2009-06-03 02:35:01 +0000906static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
907 const SkRect& bounds) {
908 REPORTER_ASSERT(reporter, p.isConvex());
909 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000910
reed@android.com6b82d1a2009-06-03 02:35:01 +0000911 SkPath p2(p);
912 REPORTER_ASSERT(reporter, p2.isConvex());
913 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
914
915 SkPath other;
916 other.swap(p2);
917 REPORTER_ASSERT(reporter, other.isConvex());
918 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
919}
920
reed@google.com04863fa2011-05-15 04:08:24 +0000921static void setFromString(SkPath* path, const char str[]) {
922 bool first = true;
923 while (str) {
924 SkScalar x, y;
925 str = SkParse::FindScalar(str, &x);
926 if (NULL == str) {
927 break;
928 }
929 str = SkParse::FindScalar(str, &y);
930 SkASSERT(str);
931 if (first) {
932 path->moveTo(x, y);
933 first = false;
934 } else {
935 path->lineTo(x, y);
936 }
937 }
938}
939
940static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000941 SkPath path;
942
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000943 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000944 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000945 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000946 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000947 check_convexity(reporter, path, SkPath::kConcave_Convexity);
948
reed@google.com04863fa2011-05-15 04:08:24 +0000949 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000950 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000951 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000952 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000953
reed@google.com04863fa2011-05-15 04:08:24 +0000954 path.reset();
reed@google.come3543972012-01-10 18:59:22 +0000955 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000956 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +0000957 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000958
reed@google.com04863fa2011-05-15 04:08:24 +0000959 static const struct {
960 const char* fPathStr;
961 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000962 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +0000963 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000964 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
965 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
966 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
967 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
968 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
969 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
970 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
971 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +0000972 };
973
974 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
975 SkPath path;
976 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000977 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
978 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +0000979 }
980}
981
reed@google.com7e6c4d12012-05-10 14:05:43 +0000982static void test_isLine(skiatest::Reporter* reporter) {
983 SkPath path;
984 SkPoint pts[2];
985 const SkScalar value = SkIntToScalar(5);
986
987 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000988
reed@google.com7e6c4d12012-05-10 14:05:43 +0000989 // set some non-zero values
990 pts[0].set(value, value);
991 pts[1].set(value, value);
992 REPORTER_ASSERT(reporter, !path.isLine(pts));
993 // check that pts was untouched
994 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
995 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
996
997 const SkScalar moveX = SkIntToScalar(1);
998 const SkScalar moveY = SkIntToScalar(2);
999 SkASSERT(value != moveX && value != moveY);
1000
1001 path.moveTo(moveX, moveY);
1002 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1003 REPORTER_ASSERT(reporter, !path.isLine(pts));
1004 // check that pts was untouched
1005 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1006 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1007
1008 const SkScalar lineX = SkIntToScalar(2);
1009 const SkScalar lineY = SkIntToScalar(2);
1010 SkASSERT(value != lineX && value != lineY);
1011
1012 path.lineTo(lineX, lineY);
1013 REPORTER_ASSERT(reporter, path.isLine(NULL));
1014
1015 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1016 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1017 REPORTER_ASSERT(reporter, path.isLine(pts));
1018 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1019 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1020
1021 path.lineTo(0, 0); // too many points/verbs
1022 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1023 REPORTER_ASSERT(reporter, !path.isLine(pts));
1024 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1025 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1026}
1027
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001028static void test_conservativelyContains(skiatest::Reporter* reporter) {
1029 SkPath path;
1030
1031 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1032 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1033
1034 // A circle that bounds kBaseRect (with a significant amount of slop)
1035 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1036 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1037 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1038
1039 // round-rect radii
1040 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001041
caryclark@google.com56f233a2012-11-19 13:06:06 +00001042 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001043 SkRect fQueryRect;
1044 bool fInRect;
1045 bool fInCircle;
1046 bool fInRR;
1047 } kQueries[] = {
1048 {kBaseRect, true, true, false},
1049
1050 // rect well inside of kBaseRect
1051 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1052 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1053 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1054 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1055 true, true, true},
1056
1057 // rects with edges off by one from kBaseRect's edges
1058 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1059 kBaseRect.width(), kBaseRect.height() + 1),
1060 false, true, false},
1061 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1062 kBaseRect.width() + 1, kBaseRect.height()),
1063 false, true, false},
1064 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1065 kBaseRect.width() + 1, kBaseRect.height() + 1),
1066 false, true, false},
1067 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1068 kBaseRect.width(), kBaseRect.height()),
1069 false, true, false},
1070 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1071 kBaseRect.width(), kBaseRect.height()),
1072 false, true, false},
1073 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1074 kBaseRect.width() + 2, kBaseRect.height()),
1075 false, true, false},
1076 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1077 kBaseRect.width() + 2, kBaseRect.height()),
1078 false, true, false},
1079
1080 // zero-w/h rects at each corner of kBaseRect
1081 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1082 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1083 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1084 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1085
1086 // far away rect
1087 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1088 SkIntToScalar(10), SkIntToScalar(10)),
1089 false, false, false},
1090
1091 // very large rect containing kBaseRect
1092 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1093 kBaseRect.fTop - 5 * kBaseRect.height(),
1094 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1095 false, false, false},
1096
1097 // skinny rect that spans same y-range as kBaseRect
1098 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1099 SkIntToScalar(1), kBaseRect.height()),
1100 true, true, true},
1101
1102 // short rect that spans same x-range as kBaseRect
1103 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1104 true, true, true},
1105
1106 // skinny rect that spans slightly larger y-range than kBaseRect
1107 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1108 SkIntToScalar(1), kBaseRect.height() + 1),
1109 false, true, false},
1110
1111 // short rect that spans slightly larger x-range than kBaseRect
1112 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1113 kBaseRect.width() + 1, SkScalar(1)),
1114 false, true, false},
1115 };
1116
1117 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001118 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001119 SkRect qRect = kQueries[q].fQueryRect;
1120 if (inv & 0x1) {
1121 SkTSwap(qRect.fLeft, qRect.fRight);
1122 }
1123 if (inv & 0x2) {
1124 SkTSwap(qRect.fTop, qRect.fBottom);
1125 }
1126 for (int d = 0; d < 2; ++d) {
1127 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1128 path.reset();
1129 path.addRect(kBaseRect, dir);
1130 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1131 path.conservativelyContainsRect(qRect));
1132
1133 path.reset();
1134 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1135 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1136 path.conservativelyContainsRect(qRect));
1137
1138 path.reset();
1139 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1140 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1141 path.conservativelyContainsRect(qRect));
1142 }
1143 // Slightly non-convex shape, shouldn't contain any rects.
1144 path.reset();
1145 path.moveTo(0, 0);
1146 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1147 path.lineTo(SkIntToScalar(100), 0);
1148 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1149 path.lineTo(0, SkIntToScalar(100));
1150 path.close();
1151 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1152 }
1153 }
1154
1155 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1156 path.reset();
1157 path.moveTo(0, 0);
1158 path.lineTo(SkIntToScalar(100), 0);
1159 path.lineTo(0, SkIntToScalar(100));
1160
1161 // inside, on along top edge
1162 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1163 SkIntToScalar(10),
1164 SkIntToScalar(10))));
1165 // above
1166 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1167 SkRect::MakeXYWH(SkIntToScalar(50),
1168 SkIntToScalar(-10),
1169 SkIntToScalar(10),
1170 SkIntToScalar(10))));
1171 // to the left
1172 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1173 SkIntToScalar(5),
1174 SkIntToScalar(5),
1175 SkIntToScalar(5))));
1176
1177 // outside the diagonal edge
1178 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1179 SkIntToScalar(200),
1180 SkIntToScalar(20),
1181 SkIntToScalar(5))));
commit-bot@chromium.org62df5262013-08-01 15:35:06 +00001182
1183 // same as above path and first test but with an extra moveTo.
1184 path.reset();
1185 path.moveTo(100, 100);
1186 path.moveTo(0, 0);
1187 path.lineTo(SkIntToScalar(100), 0);
1188 path.lineTo(0, SkIntToScalar(100));
1189
1190 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1191 SkIntToScalar(10),
1192 SkIntToScalar(10))));
1193
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001194}
1195
caryclark@google.comf1316942011-07-26 19:54:45 +00001196// Simple isRect test is inline TestPath, below.
1197// test_isRect provides more extensive testing.
1198static void test_isRect(skiatest::Reporter* reporter) {
1199 // passing tests (all moveTo / lineTo...
1200 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1201 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1202 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1203 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1204 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1205 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1206 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1207 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1208 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1209 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1210 {1, 0}, {.5f, 0}};
1211 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1212 {0, 1}, {0, .5f}};
1213 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1214 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1215 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001216 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001217
caryclark@google.comf1316942011-07-26 19:54:45 +00001218 // failing tests
1219 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1220 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1221 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1222 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1223 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1224 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1225 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1226 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001227 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1228 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1229 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001230
caryclark@google.comf1316942011-07-26 19:54:45 +00001231 // failing, no close
1232 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1233 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1234
1235 size_t testLen[] = {
1236 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1237 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001238 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001239 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001240 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001241 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001242 };
1243 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001244 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1245 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001246 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001247 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001248 SkPoint* lastPass = rf;
1249 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001250 bool fail = false;
1251 bool close = true;
1252 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1253 size_t index;
1254 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1255 SkPath path;
1256 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1257 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1258 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1259 }
1260 if (close) {
1261 path.close();
1262 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001263 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1264 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001265
caryclark@google.com56f233a2012-11-19 13:06:06 +00001266 if (!fail) {
1267 SkRect computed, expected;
1268 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1269 REPORTER_ASSERT(reporter, path.isRect(&computed));
1270 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001271
caryclark@google.comf68154a2012-11-21 15:18:06 +00001272 bool isClosed;
1273 SkPath::Direction direction, cheapDirection;
1274 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001275 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001276 REPORTER_ASSERT(reporter, isClosed == close);
1277 REPORTER_ASSERT(reporter, direction == cheapDirection);
1278 } else {
1279 SkRect computed;
1280 computed.set(123, 456, 789, 1011);
1281 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1282 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1283 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1284
1285 bool isClosed = (bool) -1;
1286 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001287 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001288 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1289 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001290 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001291
caryclark@google.comf1316942011-07-26 19:54:45 +00001292 if (tests[testIndex] == lastPass) {
1293 fail = true;
1294 }
1295 if (tests[testIndex] == lastClose) {
1296 close = false;
1297 }
1298 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001299
caryclark@google.comf1316942011-07-26 19:54:45 +00001300 // fail, close then line
1301 SkPath path1;
1302 path1.moveTo(r1[0].fX, r1[0].fY);
1303 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1304 path1.lineTo(r1[index].fX, r1[index].fY);
1305 }
1306 path1.close();
1307 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001308 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001309
caryclark@google.comf1316942011-07-26 19:54:45 +00001310 // fail, move in the middle
1311 path1.reset();
1312 path1.moveTo(r1[0].fX, r1[0].fY);
1313 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1314 if (index == 2) {
1315 path1.moveTo(1, .5f);
1316 }
1317 path1.lineTo(r1[index].fX, r1[index].fY);
1318 }
1319 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001320 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001321
1322 // fail, move on the edge
1323 path1.reset();
1324 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1325 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1326 path1.lineTo(r1[index].fX, r1[index].fY);
1327 }
1328 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001329 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001330
caryclark@google.comf1316942011-07-26 19:54:45 +00001331 // fail, quad
1332 path1.reset();
1333 path1.moveTo(r1[0].fX, r1[0].fY);
1334 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1335 if (index == 2) {
1336 path1.quadTo(1, .5f, 1, .5f);
1337 }
1338 path1.lineTo(r1[index].fX, r1[index].fY);
1339 }
1340 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001341 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001342
caryclark@google.comf1316942011-07-26 19:54:45 +00001343 // fail, cubic
1344 path1.reset();
1345 path1.moveTo(r1[0].fX, r1[0].fY);
1346 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1347 if (index == 2) {
1348 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1349 }
1350 path1.lineTo(r1[index].fX, r1[index].fY);
1351 }
1352 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001353 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001354}
1355
caryclark@google.com56f233a2012-11-19 13:06:06 +00001356static void test_isNestedRects(skiatest::Reporter* reporter) {
1357 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001358 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001359 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1360 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1361 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001362 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001363 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1364 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1365 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1366 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001367 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001368 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001369 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001370 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001371 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1372 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1373 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001374
1375 // failing tests
1376 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1377 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1378 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1379 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1380 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1381 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1382 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1383 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1384
1385 // failing, no close
1386 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1387 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1388
1389 size_t testLen[] = {
1390 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1391 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1392 sizeof(rd), sizeof(re),
1393 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1394 sizeof(f7), sizeof(f8),
1395 sizeof(c1), sizeof(c2)
1396 };
1397 SkPoint* tests[] = {
1398 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1399 f1, f2, f3, f4, f5, f6, f7, f8,
1400 c1, c2
1401 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001402 SkPath::Direction dirs[] = {
1403 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001404 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001405 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1406 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001407 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1408 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1409 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1410 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1411 };
1412 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1413
caryclark@google.com56f233a2012-11-19 13:06:06 +00001414 const SkPoint* lastPass = re;
1415 const SkPoint* lastClose = f8;
1416 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1417 size_t index;
1418 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1419 bool fail = false;
1420 bool close = true;
1421 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1422 SkPath path;
1423 if (rectFirst) {
1424 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1425 }
1426 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1427 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1428 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1429 }
1430 if (close) {
1431 path.close();
1432 }
1433 if (!rectFirst) {
1434 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1435 }
1436 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1437 if (!fail) {
1438 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001439 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001440 SkRect testBounds;
1441 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1442 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1443 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001444 if (rectFirst) {
1445 expectedDirs[0] = SkPath::kCW_Direction;
1446 } else {
1447 expectedDirs[0] = SkPath::kCCW_Direction;
1448 }
1449 expectedDirs[1] = dirs[testIndex];
1450 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001451 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1452 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001453 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1454 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001455 }
1456 if (tests[testIndex] == lastPass) {
1457 fail = true;
1458 }
1459 if (tests[testIndex] == lastClose) {
1460 close = false;
1461 }
1462 }
1463
1464 // fail, close then line
1465 SkPath path1;
1466 if (rectFirst) {
1467 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1468 }
1469 path1.moveTo(r1[0].fX, r1[0].fY);
1470 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1471 path1.lineTo(r1[index].fX, r1[index].fY);
1472 }
1473 path1.close();
1474 path1.lineTo(1, 0);
1475 if (!rectFirst) {
1476 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1477 }
1478 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1479
1480 // fail, move in the middle
1481 path1.reset();
1482 if (rectFirst) {
1483 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1484 }
1485 path1.moveTo(r1[0].fX, r1[0].fY);
1486 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1487 if (index == 2) {
1488 path1.moveTo(1, .5f);
1489 }
1490 path1.lineTo(r1[index].fX, r1[index].fY);
1491 }
1492 path1.close();
1493 if (!rectFirst) {
1494 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1495 }
1496 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1497
1498 // fail, move on the edge
1499 path1.reset();
1500 if (rectFirst) {
1501 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1502 }
1503 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1504 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1505 path1.lineTo(r1[index].fX, r1[index].fY);
1506 }
1507 path1.close();
1508 if (!rectFirst) {
1509 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1510 }
1511 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1512
1513 // fail, quad
1514 path1.reset();
1515 if (rectFirst) {
1516 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1517 }
1518 path1.moveTo(r1[0].fX, r1[0].fY);
1519 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1520 if (index == 2) {
1521 path1.quadTo(1, .5f, 1, .5f);
1522 }
1523 path1.lineTo(r1[index].fX, r1[index].fY);
1524 }
1525 path1.close();
1526 if (!rectFirst) {
1527 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1528 }
1529 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1530
1531 // fail, cubic
1532 path1.reset();
1533 if (rectFirst) {
1534 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1535 }
1536 path1.moveTo(r1[0].fX, r1[0].fY);
1537 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1538 if (index == 2) {
1539 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1540 }
1541 path1.lineTo(r1[index].fX, r1[index].fY);
1542 }
1543 path1.close();
1544 if (!rectFirst) {
1545 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1546 }
1547 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001548
caryclark@google.com56f233a2012-11-19 13:06:06 +00001549 // fail, not nested
1550 path1.reset();
1551 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1552 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1553 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1554 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001555
1556 // pass, stroke rect
1557 SkPath src, dst;
1558 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1559 SkPaint strokePaint;
1560 strokePaint.setStyle(SkPaint::kStroke_Style);
1561 strokePaint.setStrokeWidth(2);
1562 strokePaint.getFillPath(src, &dst);
1563 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001564}
1565
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001566static void write_and_read_back(skiatest::Reporter* reporter,
1567 const SkPath& p) {
1568 SkWriter32 writer(100);
1569 writer.writePath(p);
1570 size_t size = writer.size();
1571 SkAutoMalloc storage(size);
1572 writer.flatten(storage.get());
1573 SkReader32 reader(storage.get(), size);
1574
1575 SkPath readBack;
1576 REPORTER_ASSERT(reporter, readBack != p);
1577 reader.readPath(&readBack);
1578 REPORTER_ASSERT(reporter, readBack == p);
1579
rmistry@google.comd6176b02012-08-23 18:14:13 +00001580 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001581 p.getConvexityOrUnknown());
1582
1583 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1584
1585 const SkRect& origBounds = p.getBounds();
1586 const SkRect& readBackBounds = readBack.getBounds();
1587
1588 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1589}
1590
reed@google.com53effc52011-09-21 19:05:12 +00001591static void test_flattening(skiatest::Reporter* reporter) {
1592 SkPath p;
1593
1594 static const SkPoint pts[] = {
1595 { 0, 0 },
1596 { SkIntToScalar(10), SkIntToScalar(10) },
1597 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1598 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1599 };
1600 p.moveTo(pts[0]);
1601 p.lineTo(pts[1]);
1602 p.quadTo(pts[2], pts[3]);
1603 p.cubicTo(pts[4], pts[5], pts[6]);
1604
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001605 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001606
1607 // create a buffer that should be much larger than the path so we don't
1608 // kill our stack if writer goes too far.
1609 char buffer[1024];
1610 uint32_t size1 = p.writeToMemory(NULL);
1611 uint32_t size2 = p.writeToMemory(buffer);
1612 REPORTER_ASSERT(reporter, size1 == size2);
1613
1614 SkPath p2;
1615 uint32_t size3 = p2.readFromMemory(buffer);
1616 REPORTER_ASSERT(reporter, size1 == size3);
1617 REPORTER_ASSERT(reporter, p == p2);
1618
1619 char buffer2[1024];
1620 size3 = p2.writeToMemory(buffer2);
1621 REPORTER_ASSERT(reporter, size1 == size3);
1622 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001623
1624 // test persistence of the oval flag & convexity
1625 {
1626 SkPath oval;
1627 SkRect rect = SkRect::MakeWH(10, 10);
1628 oval.addOval(rect);
1629
1630 write_and_read_back(reporter, oval);
1631 }
reed@google.com53effc52011-09-21 19:05:12 +00001632}
1633
1634static void test_transform(skiatest::Reporter* reporter) {
1635 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001636
reed@google.com53effc52011-09-21 19:05:12 +00001637 static const SkPoint pts[] = {
1638 { 0, 0 },
1639 { SkIntToScalar(10), SkIntToScalar(10) },
1640 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1641 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1642 };
1643 p.moveTo(pts[0]);
1644 p.lineTo(pts[1]);
1645 p.quadTo(pts[2], pts[3]);
1646 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001647
reed@google.com53effc52011-09-21 19:05:12 +00001648 SkMatrix matrix;
1649 matrix.reset();
1650 p.transform(matrix, &p1);
1651 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001652
reed@google.com53effc52011-09-21 19:05:12 +00001653 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1654 p.transform(matrix, &p1);
1655 SkPoint pts1[7];
1656 int count = p1.getPoints(pts1, 7);
1657 REPORTER_ASSERT(reporter, 7 == count);
1658 for (int i = 0; i < count; ++i) {
1659 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1660 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1661 }
1662}
1663
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001664static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001665 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001666 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001667
caryclark@google.com56f233a2012-11-19 13:06:06 +00001668 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001669 const char* testPath;
1670 const size_t numResultPts;
1671 const SkRect resultBound;
1672 const SkPath::Verb* resultVerbs;
1673 const size_t numResultVerbs;
1674 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001675
schenney@chromium.org7e963602012-06-13 17:05:43 +00001676 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1677 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1678 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1679 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1680 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1681 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1682 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1683 static const SkPath::Verb resultVerbs8[] = {
1684 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1685 };
1686 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1687 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1688 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1689 static const SkPath::Verb resultVerbs12[] = {
1690 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1691 };
1692 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1693 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1694 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1695 static const SkPath::Verb resultVerbs16[] = {
1696 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1697 };
1698 static const struct zeroPathTestData gZeroLengthTests[] = {
1699 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001700 { "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 +00001701 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001702 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1703 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1704 { "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) },
1705 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1706 { "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) },
1707 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1708 { "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) },
1709 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1710 { "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) },
1711 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1712 { "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 +00001713 SK_ARRAY_COUNT(resultVerbs14)
1714 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001715 { "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) },
1716 { "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 +00001717 SK_ARRAY_COUNT(resultVerbs16)
1718 }
1719 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001720
schenney@chromium.org7e963602012-06-13 17:05:43 +00001721 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1722 p.reset();
1723 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1724 REPORTER_ASSERT(reporter, valid);
1725 REPORTER_ASSERT(reporter, !p.isEmpty());
1726 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1727 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1728 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1729 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1730 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1731 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001732 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001733}
1734
1735struct SegmentInfo {
1736 SkPath fPath;
1737 int fPointCount;
1738};
1739
reed@google.com10296cc2011-09-21 12:29:05 +00001740#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1741
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001742static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001743 SkPath p, p2;
1744
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001745 p.moveTo(0, 0);
1746 p.quadTo(100, 100, 200, 200);
1747 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1748 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001749 p2 = p;
1750 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001751 p.cubicTo(100, 100, 200, 200, 300, 300);
1752 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1753 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001754 p2 = p;
1755 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1756
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001757 p.reset();
1758 p.moveTo(0, 0);
1759 p.cubicTo(100, 100, 200, 200, 300, 300);
1760 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001761 p2 = p;
1762 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001763
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001764 REPORTER_ASSERT(reporter, !p.isEmpty());
1765}
1766
1767static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001768 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001769 SkPoint pts[4];
1770
1771 // Test an iterator with no path
1772 SkPath::Iter noPathIter;
1773 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001774
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001775 // Test that setting an empty path works
1776 noPathIter.setPath(p, false);
1777 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001778
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001779 // Test that close path makes no difference for an empty path
1780 noPathIter.setPath(p, true);
1781 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001782
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001783 // Test an iterator with an initial empty path
1784 SkPath::Iter iter(p, false);
1785 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1786
1787 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001788 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001789 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1790
rmistry@google.comd6176b02012-08-23 18:14:13 +00001791
schenney@chromium.org7e963602012-06-13 17:05:43 +00001792 struct iterTestData {
1793 const char* testPath;
1794 const bool forceClose;
1795 const bool consumeDegenerates;
1796 const size_t* numResultPtsPerVerb;
1797 const SkPoint* resultPts;
1798 const SkPath::Verb* resultVerbs;
1799 const size_t numResultVerbs;
1800 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001801
schenney@chromium.org7e963602012-06-13 17:05:43 +00001802 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1803 static const SkPath::Verb resultVerbs2[] = {
1804 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1805 };
1806 static const SkPath::Verb resultVerbs3[] = {
1807 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1808 };
1809 static const SkPath::Verb resultVerbs4[] = {
1810 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1811 };
1812 static const SkPath::Verb resultVerbs5[] = {
1813 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1814 };
1815 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001816 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1817 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1818 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1819 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001820 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001821 static const SkPoint resultPts2[] = {
1822 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1823 };
1824 static const SkPoint resultPts3[] = {
1825 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1826 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1827 };
1828 static const SkPoint resultPts4[] = {
1829 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1830 };
1831 static const SkPoint resultPts5[] = {
1832 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1833 };
1834 static const struct iterTestData gIterTests[] = {
1835 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001836 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1837 { "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 +00001838 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1839 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1840 { "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) },
1841 { "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 +00001842 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1843 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1844 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1845 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1846 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1847 { "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 +00001848 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001849
schenney@chromium.org7e963602012-06-13 17:05:43 +00001850 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1851 p.reset();
1852 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1853 REPORTER_ASSERT(reporter, valid);
1854 iter.setPath(p, gIterTests[i].forceClose);
1855 int j = 0, l = 0;
1856 do {
1857 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1858 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1859 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1860 }
1861 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1862 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1863 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001864
1865 // The GM degeneratesegments.cpp test is more extensive
1866}
1867
1868static void test_raw_iter(skiatest::Reporter* reporter) {
1869 SkPath p;
1870 SkPoint pts[4];
1871
1872 // Test an iterator with no path
1873 SkPath::RawIter noPathIter;
1874 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1875 // Test that setting an empty path works
1876 noPathIter.setPath(p);
1877 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001878
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001879 // Test an iterator with an initial empty path
1880 SkPath::RawIter iter(p);
1881 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1882
1883 // Test that a move-only path returns the move.
1884 p.moveTo(SK_Scalar1, 0);
1885 iter.setPath(p);
1886 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1887 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1888 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1889 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1890
1891 // No matter how many moves we add, we should get them all back
1892 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1893 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1894 iter.setPath(p);
1895 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1896 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1897 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1898 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1899 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1900 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1901 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1902 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1903 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1904 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1905
1906 // Initial close is never ever stored
1907 p.reset();
1908 p.close();
1909 iter.setPath(p);
1910 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1911
1912 // Move/close sequences
1913 p.reset();
1914 p.close(); // Not stored, no purpose
1915 p.moveTo(SK_Scalar1, 0);
1916 p.close();
1917 p.close(); // Not stored, no purpose
1918 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1919 p.close();
1920 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1921 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1922 p.close();
1923 iter.setPath(p);
1924 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1925 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1926 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1927 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1928 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1929 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1930 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1931 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1932 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1933 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1934 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1935 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1936 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1937 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1938 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1939 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1940 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1941 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1942 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1943 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1944 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1945 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1946
1947 // Generate random paths and verify
1948 SkPoint randomPts[25];
1949 for (int i = 0; i < 5; ++i) {
1950 for (int j = 0; j < 5; ++j) {
1951 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1952 }
1953 }
1954
1955 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00001956 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001957 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00001958 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001959 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00001960
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001961 for (int i = 0; i < 500; ++i) {
1962 p.reset();
1963 bool lastWasClose = true;
1964 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00001965 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001966 int numPoints = 0;
1967 int numVerbs = (rand.nextU() >> 16) % 10;
1968 int numIterVerbs = 0;
1969 for (int j = 0; j < numVerbs; ++j) {
1970 do {
1971 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1972 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001973 switch (nextVerb) {
1974 case SkPath::kMove_Verb:
1975 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1976 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00001977 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001978 numPoints += 1;
1979 lastWasClose = false;
1980 haveMoveTo = true;
1981 break;
1982 case SkPath::kLine_Verb:
1983 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001984 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001985 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1986 haveMoveTo = true;
1987 }
1988 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1989 p.lineTo(expectedPts[numPoints]);
1990 numPoints += 1;
1991 lastWasClose = false;
1992 break;
1993 case SkPath::kQuad_Verb:
1994 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00001995 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001996 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1997 haveMoveTo = true;
1998 }
1999 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2000 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2001 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
2002 numPoints += 2;
2003 lastWasClose = false;
2004 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002005 case SkPath::kConic_Verb:
2006 if (!haveMoveTo) {
2007 expectedPts[numPoints++] = lastMoveToPt;
2008 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2009 haveMoveTo = true;
2010 }
2011 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2012 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2013 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2014 rand.nextUScalar1() * 4);
2015 numPoints += 2;
2016 lastWasClose = false;
2017 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002018 case SkPath::kCubic_Verb:
2019 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002020 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002021 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2022 haveMoveTo = true;
2023 }
2024 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2025 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2026 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2027 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2028 expectedPts[numPoints + 2]);
2029 numPoints += 3;
2030 lastWasClose = false;
2031 break;
2032 case SkPath::kClose_Verb:
2033 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002034 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002035 lastWasClose = true;
2036 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002037 default:
2038 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002039 }
2040 expectedVerbs[numIterVerbs++] = nextVerb;
2041 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002042
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002043 iter.setPath(p);
2044 numVerbs = numIterVerbs;
2045 numIterVerbs = 0;
2046 int numIterPts = 0;
2047 SkPoint lastMoveTo;
2048 SkPoint lastPt;
2049 lastMoveTo.set(0, 0);
2050 lastPt.set(0, 0);
2051 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2052 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2053 numIterVerbs++;
2054 switch (nextVerb) {
2055 case SkPath::kMove_Verb:
2056 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2057 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2058 lastPt = lastMoveTo = pts[0];
2059 numIterPts += 1;
2060 break;
2061 case SkPath::kLine_Verb:
2062 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2063 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2064 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2065 lastPt = pts[1];
2066 numIterPts += 1;
2067 break;
2068 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002069 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002070 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2071 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2072 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2073 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2074 lastPt = pts[2];
2075 numIterPts += 2;
2076 break;
2077 case SkPath::kCubic_Verb:
2078 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2079 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2080 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2081 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2082 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2083 lastPt = pts[3];
2084 numIterPts += 3;
2085 break;
2086 case SkPath::kClose_Verb:
2087 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2088 lastPt = lastMoveTo;
2089 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002090 default:
2091 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002092 }
2093 }
2094 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2095 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2096 }
2097}
2098
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002099static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002100 const SkPath& path,
2101 bool expectedCircle,
2102 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002103 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002104 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2105 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002106
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002107 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002108 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2109 }
2110}
2111
2112static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002113 const SkPath& path,
2114 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002115 SkPath tmp;
2116
2117 SkMatrix m;
2118 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2119 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002120 // this matrix reverses the direction.
2121 if (SkPath::kCCW_Direction == dir) {
2122 dir = SkPath::kCW_Direction;
2123 } else {
2124 SkASSERT(SkPath::kCW_Direction == dir);
2125 dir = SkPath::kCCW_Direction;
2126 }
2127 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002128}
2129
2130static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002131 const SkPath& path,
2132 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002133 SkPath tmp;
2134
2135 // translate at small offset
2136 SkMatrix m;
2137 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2138 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002139 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002140
2141 tmp.reset();
2142 m.reset();
2143
2144 // translate at a relatively big offset
2145 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2146 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002147 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002148}
2149
2150static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002151 const SkPath& path,
2152 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002153 for (int angle = 0; angle < 360; ++angle) {
2154 SkPath tmp;
2155 SkMatrix m;
2156 m.setRotate(SkIntToScalar(angle));
2157 path.transform(m, &tmp);
2158
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002159 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002160 // degrees is not an oval anymore, this can be improved. we made this
2161 // for the simplicity of our implementation.
2162 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002163 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002164 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002165 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002166 }
2167 }
2168}
2169
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002170static void test_circle_mirror_x(skiatest::Reporter* reporter,
2171 const SkPath& path,
2172 SkPath::Direction dir) {
2173 SkPath tmp;
2174 SkMatrix m;
2175 m.reset();
2176 m.setScaleX(-SK_Scalar1);
2177 path.transform(m, &tmp);
2178
2179 if (SkPath::kCW_Direction == dir) {
2180 dir = SkPath::kCCW_Direction;
2181 } else {
2182 SkASSERT(SkPath::kCCW_Direction == dir);
2183 dir = SkPath::kCW_Direction;
2184 }
2185
2186 check_for_circle(reporter, tmp, true, dir);
2187}
2188
2189static void test_circle_mirror_y(skiatest::Reporter* reporter,
2190 const SkPath& path,
2191 SkPath::Direction dir) {
2192 SkPath tmp;
2193 SkMatrix m;
2194 m.reset();
2195 m.setScaleY(-SK_Scalar1);
2196 path.transform(m, &tmp);
2197
2198 if (SkPath::kCW_Direction == dir) {
2199 dir = SkPath::kCCW_Direction;
2200 } else {
2201 SkASSERT(SkPath::kCCW_Direction == dir);
2202 dir = SkPath::kCW_Direction;
2203 }
2204
2205 check_for_circle(reporter, tmp, true, dir);
2206}
2207
2208static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2209 const SkPath& path,
2210 SkPath::Direction dir) {
2211 SkPath tmp;
2212 SkMatrix m;
2213 m.reset();
2214 m.setScaleX(-SK_Scalar1);
2215 m.setScaleY(-SK_Scalar1);
2216 path.transform(m, &tmp);
2217
2218 check_for_circle(reporter, tmp, true, dir);
2219}
2220
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002221static void test_circle_with_direction(skiatest::Reporter* reporter,
2222 SkPath::Direction dir) {
2223 SkPath path;
2224
2225 // circle at origin
2226 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002227 check_for_circle(reporter, path, true, dir);
2228 test_circle_rotate(reporter, path, dir);
2229 test_circle_translate(reporter, path, dir);
2230 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002231
2232 // circle at an offset at (10, 10)
2233 path.reset();
2234 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2235 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002236 check_for_circle(reporter, path, true, dir);
2237 test_circle_rotate(reporter, path, dir);
2238 test_circle_translate(reporter, path, dir);
2239 test_circle_skew(reporter, path, dir);
2240 test_circle_mirror_x(reporter, path, dir);
2241 test_circle_mirror_y(reporter, path, dir);
2242 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002243}
2244
2245static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2246 SkPath path;
2247 SkPath circle;
2248 SkPath rect;
2249 SkPath empty;
2250
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002251 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2252 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2253
2254 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002255 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2256 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2257
2258 SkMatrix translate;
2259 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2260
2261 // For simplicity, all the path concatenation related operations
2262 // would mark it non-circle, though in theory it's still a circle.
2263
2264 // empty + circle (translate)
2265 path = empty;
2266 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002267 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002268
2269 // circle + empty (translate)
2270 path = circle;
2271 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002272 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002273
2274 // test reverseAddPath
2275 path = circle;
2276 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002277 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002278}
2279
2280static void test_circle(skiatest::Reporter* reporter) {
2281 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2282 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2283
2284 // multiple addCircle()
2285 SkPath path;
2286 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2287 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002288 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002289
2290 // some extra lineTo() would make isOval() fail
2291 path.reset();
2292 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2293 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002294 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002295
2296 // not back to the original point
2297 path.reset();
2298 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2299 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002300 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002301
2302 test_circle_with_add_paths(reporter);
2303}
2304
2305static void test_oval(skiatest::Reporter* reporter) {
2306 SkRect rect;
2307 SkMatrix m;
2308 SkPath path;
2309
2310 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2311 path.addOval(rect);
2312
2313 REPORTER_ASSERT(reporter, path.isOval(NULL));
2314
2315 m.setRotate(SkIntToScalar(90));
2316 SkPath tmp;
2317 path.transform(m, &tmp);
2318 // an oval rotated 90 degrees is still an oval.
2319 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2320
2321 m.reset();
2322 m.setRotate(SkIntToScalar(30));
2323 tmp.reset();
2324 path.transform(m, &tmp);
2325 // an oval rotated 30 degrees is not an oval anymore.
2326 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2327
2328 // since empty path being transformed.
2329 path.reset();
2330 tmp.reset();
2331 m.reset();
2332 path.transform(m, &tmp);
2333 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2334
2335 // empty path is not an oval
2336 tmp.reset();
2337 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2338
2339 // only has moveTo()s
2340 tmp.reset();
2341 tmp.moveTo(0, 0);
2342 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2343 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2344
2345 // mimic WebKit's calling convention,
2346 // call moveTo() first and then call addOval()
2347 path.reset();
2348 path.moveTo(0, 0);
2349 path.addOval(rect);
2350 REPORTER_ASSERT(reporter, path.isOval(NULL));
2351
2352 // copy path
2353 path.reset();
2354 tmp.reset();
2355 tmp.addOval(rect);
2356 path = tmp;
2357 REPORTER_ASSERT(reporter, path.isOval(NULL));
2358}
2359
bungeman@google.coma5809a32013-06-21 15:13:34 +00002360static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2361 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002362
reed@android.com3abec1d2009-03-02 05:36:20 +00002363 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002364 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002365 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002366 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002367 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002368 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2369 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002370 REPORTER_ASSERT(reporter, p == empty);
2371 REPORTER_ASSERT(reporter, !(p != empty));
2372}
2373
2374static void TestPath(skiatest::Reporter* reporter) {
2375 SkTSize<SkScalar>::Make(3,4);
2376
2377 SkPath p, empty;
2378 SkRect bounds, bounds2;
2379 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002380
reed@android.comd252db02009-04-01 18:31:44 +00002381 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002382
reed@android.com3abec1d2009-03-02 05:36:20 +00002383 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002384
reed@android.com6b82d1a2009-06-03 02:35:01 +00002385 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2386 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002387 // we have quads or cubics
2388 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002389 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002390
reed@android.com6b82d1a2009-06-03 02:35:01 +00002391 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002392 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002393
reed@android.com6b82d1a2009-06-03 02:35:01 +00002394 p.addOval(bounds);
2395 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002396 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002397
bungeman@google.coma5809a32013-06-21 15:13:34 +00002398 p.rewind();
2399 test_empty(reporter, p);
2400
reed@android.com3abec1d2009-03-02 05:36:20 +00002401 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002402 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002403 // we have only lines
2404 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002405 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002406
bungeman@google.coma5809a32013-06-21 15:13:34 +00002407 REPORTER_ASSERT(reporter, p != empty);
2408 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002409
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002410 // do getPoints and getVerbs return the right result
2411 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2412 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002413 SkPoint pts[4];
2414 int count = p.getPoints(pts, 4);
2415 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002416 uint8_t verbs[6];
2417 verbs[5] = 0xff;
2418 p.getVerbs(verbs, 5);
2419 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2420 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2421 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2422 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2423 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2424 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002425 bounds2.set(pts, 4);
2426 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002427
reed@android.com3abec1d2009-03-02 05:36:20 +00002428 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2429 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002430 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002431
reed@android.com3abec1d2009-03-02 05:36:20 +00002432 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002433 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002434 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2435 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002436
reed@android.com3abec1d2009-03-02 05:36:20 +00002437 // now force p to not be a rect
2438 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2439 p.addRect(bounds);
2440 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002441
reed@google.com7e6c4d12012-05-10 14:05:43 +00002442 test_isLine(reporter);
2443 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002444 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002445 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002446 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002447 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002448 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002449 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002450 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002451 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002452 test_flattening(reporter);
2453 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002454 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002455 test_iter(reporter);
2456 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002457 test_circle(reporter);
2458 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002459 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002460 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002461 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002462 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002463 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002464 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002465 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002466 test_tricky_cubic();
2467 test_clipped_cubic();
2468 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002469 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002470 test_bad_cubic_crbug234190();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +00002471 test_android_specific_behavior(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002472}
2473
2474#include "TestClassDef.h"
2475DEFINE_TESTCLASS("Path", PathTestClass, TestPath)