blob: 4637e9218b8533762850e76cbffc4edd9d178f48 [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
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +000035static void test_path_close_issue1474(skiatest::Reporter* reporter) {
36 // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
37 // are relative to the point we close to, not relative to the point we close from.
38 SkPath path;
39 SkPoint last;
40
41 // Test rLineTo().
42 path.rLineTo(0, 100);
43 path.rLineTo(100, 0);
44 path.close(); // Returns us back to 0,0.
45 path.rLineTo(50, 50); // This should go to 50,50.
46
47 path.getLastPt(&last);
48 REPORTER_ASSERT(reporter, 50 == last.fX);
49 REPORTER_ASSERT(reporter, 50 == last.fY);
50
51 // Test rQuadTo().
52 path.rewind();
53 path.rLineTo(0, 100);
54 path.rLineTo(100, 0);
55 path.close();
56 path.rQuadTo(50, 50, 75, 75);
57
58 path.getLastPt(&last);
59 REPORTER_ASSERT(reporter, 75 == last.fX);
60 REPORTER_ASSERT(reporter, 75 == last.fY);
61
62 // Test rConicTo().
63 path.rewind();
64 path.rLineTo(0, 100);
65 path.rLineTo(100, 0);
66 path.close();
67 path.rConicTo(50, 50, 85, 85, 2);
68
69 path.getLastPt(&last);
70 REPORTER_ASSERT(reporter, 85 == last.fX);
71 REPORTER_ASSERT(reporter, 85 == last.fY);
72
73 // Test rCubicTo().
74 path.rewind();
75 path.rLineTo(0, 100);
76 path.rLineTo(100, 0);
77 path.close();
78 path.rCubicTo(50, 50, 85, 85, 95, 95);
79
80 path.getLastPt(&last);
81 REPORTER_ASSERT(reporter, 95 == last.fX);
82 REPORTER_ASSERT(reporter, 95 == last.fY);
83}
84
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000085static void test_android_specific_behavior(skiatest::Reporter* reporter) {
86#ifdef SK_BUILD_FOR_ANDROID
87 // Copy constructor should preserve generation ID, but assignment shouldn't.
88 SkPath original;
89 original.moveTo(0, 0);
90 original.lineTo(1, 1);
91 REPORTER_ASSERT(reporter, original.getGenerationID() > 0);
92
93 const SkPath copy(original);
94 REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
95
96 SkPath assign;
97 assign = original;
98 REPORTER_ASSERT(reporter, assign.getGenerationID() != original.getGenerationID());
99#endif
100}
101
reed@google.com3eff3592013-05-08 21:08:21 +0000102// This used to assert in the debug build, as the edges did not all line-up.
103static void test_bad_cubic_crbug234190() {
104 SkPath path;
105 path.moveTo(13.8509f, 3.16858f);
106 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
107 -2.38991e+08f, -4.26573e+08f,
108 -2.41016e+08f, -4.30188e+08f);
109
110 SkPaint paint;
111 paint.setAntiAlias(true);
112 SkAutoTUnref<SkSurface> surface(new_surface(84, 88));
113 surface->getCanvas()->drawPath(path, paint);
114}
115
reed@google.com7a90daf2013-04-10 18:44:00 +0000116static void test_bad_cubic_crbug229478() {
117 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000118 { 4595.91064f, -11596.9873f },
119 { 4597.2168f, -11595.9414f },
120 { 4598.52344f, -11594.8955f },
121 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +0000122 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000123
reed@google.com7a90daf2013-04-10 18:44:00 +0000124 SkPath path;
125 path.moveTo(pts[0]);
126 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000127
reed@google.com7a90daf2013-04-10 18:44:00 +0000128 SkPaint paint;
129 paint.setStyle(SkPaint::kStroke_Style);
130 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000131
reed@google.com7a90daf2013-04-10 18:44:00 +0000132 SkPath dst;
133 // Before the fix, this would infinite-recurse, and run out of stack
134 // because we would keep trying to subdivide a degenerate cubic segment.
135 paint.getFillPath(path, &dst, NULL);
136}
137
reed@google.com64d62952013-01-18 17:49:28 +0000138static void build_path_170666(SkPath& path) {
139 path.moveTo(17.9459f, 21.6344f);
140 path.lineTo(139.545f, -47.8105f);
141 path.lineTo(139.545f, -47.8105f);
142 path.lineTo(131.07f, -47.3888f);
143 path.lineTo(131.07f, -47.3888f);
144 path.lineTo(122.586f, -46.9532f);
145 path.lineTo(122.586f, -46.9532f);
146 path.lineTo(18076.6f, 31390.9f);
147 path.lineTo(18076.6f, 31390.9f);
148 path.lineTo(18085.1f, 31390.5f);
149 path.lineTo(18085.1f, 31390.5f);
150 path.lineTo(18076.6f, 31390.9f);
151 path.lineTo(18076.6f, 31390.9f);
152 path.lineTo(17955, 31460.3f);
153 path.lineTo(17955, 31460.3f);
154 path.lineTo(17963.5f, 31459.9f);
155 path.lineTo(17963.5f, 31459.9f);
156 path.lineTo(17971.9f, 31459.5f);
157 path.lineTo(17971.9f, 31459.5f);
158 path.lineTo(17.9551f, 21.6205f);
159 path.lineTo(17.9551f, 21.6205f);
160 path.lineTo(9.47091f, 22.0561f);
161 path.lineTo(9.47091f, 22.0561f);
162 path.lineTo(17.9459f, 21.6344f);
163 path.lineTo(17.9459f, 21.6344f);
164 path.close();path.moveTo(0.995934f, 22.4779f);
165 path.lineTo(0.986725f, 22.4918f);
166 path.lineTo(0.986725f, 22.4918f);
167 path.lineTo(17955, 31460.4f);
168 path.lineTo(17955, 31460.4f);
169 path.lineTo(17971.9f, 31459.5f);
170 path.lineTo(17971.9f, 31459.5f);
171 path.lineTo(18093.6f, 31390.1f);
172 path.lineTo(18093.6f, 31390.1f);
173 path.lineTo(18093.6f, 31390);
174 path.lineTo(18093.6f, 31390);
175 path.lineTo(139.555f, -47.8244f);
176 path.lineTo(139.555f, -47.8244f);
177 path.lineTo(122.595f, -46.9671f);
178 path.lineTo(122.595f, -46.9671f);
179 path.lineTo(0.995934f, 22.4779f);
180 path.lineTo(0.995934f, 22.4779f);
181 path.close();
182 path.moveTo(5.43941f, 25.5223f);
183 path.lineTo(798267, -28871.1f);
184 path.lineTo(798267, -28871.1f);
185 path.lineTo(3.12512e+06f, -113102);
186 path.lineTo(3.12512e+06f, -113102);
187 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
188 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
189 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
190 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
191 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
192 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
193 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
194 path.lineTo(2.78271e+08f, -1.00733e+07f);
195 path.lineTo(2.78271e+08f, -1.00733e+07f);
196 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
197 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
198 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
199 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
200 path.lineTo(2.77473e+08f, -1.00444e+07f);
201 path.lineTo(2.77473e+08f, -1.00444e+07f);
202 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
203 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
204 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
205 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
206 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
207 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
208 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
209 path.lineTo(798284, -28872);
210 path.lineTo(798284, -28872);
211 path.lineTo(22.4044f, 24.6677f);
212 path.lineTo(22.4044f, 24.6677f);
213 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
214 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
215 path.close();
216}
217
218static void build_path_simple_170666(SkPath& path) {
219 path.moveTo(126.677f, 24.1591f);
220 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
221}
222
223// This used to assert in the SK_DEBUG build, as the clip step would fail with
224// too-few interations in our cubic-line intersection code. That code now runs
225// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000226static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000227 SkPath path;
228 SkPaint paint;
229 paint.setAntiAlias(true);
230
231 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000232
reed@google.com64d62952013-01-18 17:49:28 +0000233 build_path_simple_170666(path);
234 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000235
reed@google.com64d62952013-01-18 17:49:28 +0000236 build_path_170666(path);
237 surface->getCanvas()->drawPath(path, paint);
238}
239
reed@google.coma8790de2012-10-24 21:04:04 +0000240// Make sure we stay non-finite once we get there (unless we reset or rewind).
241static void test_addrect_isfinite(skiatest::Reporter* reporter) {
242 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000243
reed@google.coma8790de2012-10-24 21:04:04 +0000244 path.addRect(SkRect::MakeWH(50, 100));
245 REPORTER_ASSERT(reporter, path.isFinite());
246
247 path.moveTo(0, 0);
248 path.lineTo(SK_ScalarInfinity, 42);
249 REPORTER_ASSERT(reporter, !path.isFinite());
250
251 path.addRect(SkRect::MakeWH(50, 100));
252 REPORTER_ASSERT(reporter, !path.isFinite());
253
254 path.reset();
255 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000256
reed@google.coma8790de2012-10-24 21:04:04 +0000257 path.addRect(SkRect::MakeWH(50, 100));
258 REPORTER_ASSERT(reporter, path.isFinite());
259}
260
reed@google.com848148e2013-01-15 15:51:59 +0000261static void build_big_path(SkPath* path, bool reducedCase) {
262 if (reducedCase) {
263 path->moveTo(577330, 1971.72f);
264 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
265 } else {
266 path->moveTo(60.1631f, 7.70567f);
267 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
268 path->lineTo(577379, 1977.77f);
269 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
270 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
271 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
272 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
273 path->quadTo(577198, 1972, 577238, 1971.31f);
274 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
275 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
276 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
277 path->moveTo(306.718f, -32.912f);
278 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
279 }
280}
281
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000282static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000283 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
284
285 // This path used to assert, because our cubic-chopping code incorrectly
286 // moved control points after the chop. This test should be run in SK_DEBUG
287 // mode to ensure that we no long assert.
288 SkPath path;
289 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
290 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000291
reed@google.com848148e2013-01-15 15:51:59 +0000292 SkPaint paint;
293 for (int doAA = 0; doAA <= 1; ++doAA) {
294 paint.setAntiAlias(SkToBool(doAA));
295 surface->getCanvas()->drawPath(path, paint);
296 }
297 }
298}
299
reed@google.com8cae8352012-09-14 15:18:41 +0000300// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
301// which triggered an assert, from a tricky cubic. This test replicates that
302// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
303// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000304static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000305 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000306 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
307 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
308 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
309 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000310 };
311
312 SkPath path;
313 path.moveTo(pts[0]);
314 path.cubicTo(pts[1], pts[2], pts[3]);
315
316 SkPaint paint;
317 paint.setAntiAlias(true);
318
319 SkSurface* surface = new_surface(19, 130);
320 surface->getCanvas()->drawPath(path, paint);
321 surface->unref();
322}
reed@android.com3abec1d2009-03-02 05:36:20 +0000323
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000324// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
325//
326static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
327 SkPath path;
328 path.quadTo(157, 366, 286, 208);
329 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000330
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000331 SkMatrix matrix;
332 matrix.setScale(1000*1000, 1000*1000);
333
334 // Be sure that path::transform correctly updates isFinite and the bounds
335 // if the transformation overflows. The previous bug was that isFinite was
336 // set to true in this case, but the bounds were not set to empty (which
337 // they should be).
338 while (path.isFinite()) {
339 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
340 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
341 path.transform(matrix);
342 }
343 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
344
345 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
346 path.transform(matrix);
347 // we need to still be non-finite
348 REPORTER_ASSERT(reporter, !path.isFinite());
349 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
350}
351
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000352static void add_corner_arc(SkPath* path, const SkRect& rect,
353 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000354 int startAngle)
355{
356
357 SkScalar rx = SkMinScalar(rect.width(), xIn);
358 SkScalar ry = SkMinScalar(rect.height(), yIn);
359
360 SkRect arcRect;
361 arcRect.set(-rx, -ry, rx, ry);
362 switch (startAngle) {
363 case 0:
364 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
365 break;
366 case 90:
367 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
368 break;
369 case 180:
370 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
371 break;
372 case 270:
373 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
374 break;
375 default:
376 break;
377 }
378
379 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
380}
381
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000382static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000383 SkScalar xCorner, SkScalar yCorner) {
384 // we are lazy here and use the same x & y for each corner
385 add_corner_arc(path, r, xCorner, yCorner, 270);
386 add_corner_arc(path, r, xCorner, yCorner, 0);
387 add_corner_arc(path, r, xCorner, yCorner, 90);
388 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000389 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000390}
391
392// Chrome creates its own round rects with each corner possibly being different.
393// Performance will suffer if they are not convex.
394// Note: PathBench::ArbRoundRectBench performs almost exactly
395// the same test (but with drawing)
396static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000397 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000398 SkRect r;
399
400 for (int i = 0; i < 5000; ++i) {
401
robertphillips@google.com158618e2012-10-23 16:56:56 +0000402 SkScalar size = rand.nextUScalar1() * 30;
403 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000404 continue;
405 }
406 r.fLeft = rand.nextUScalar1() * 300;
407 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000408 r.fRight = r.fLeft + 2 * size;
409 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000410
411 SkPath temp;
412
413 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
414
415 REPORTER_ASSERT(reporter, temp.isConvex());
416 }
417}
418
robertphillips@google.com158618e2012-10-23 16:56:56 +0000419// Chrome will sometimes create a 0 radius round rect. The degenerate
420// quads prevent the path from being converted to a rect
421// Note: PathBench::ArbRoundRectBench performs almost exactly
422// the same test (but with drawing)
423static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000424 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000425 SkRect r;
426
427 for (int i = 0; i < 5000; ++i) {
428
429 SkScalar size = rand.nextUScalar1() * 30;
430 if (size < SK_Scalar1) {
431 continue;
432 }
433 r.fLeft = rand.nextUScalar1() * 300;
434 r.fTop = rand.nextUScalar1() * 300;
435 r.fRight = r.fLeft + 2 * size;
436 r.fBottom = r.fTop + 2 * size;
437
438 SkPath temp;
439
440 make_arb_round_rect(&temp, r, 0, 0);
441
robertphillips@google.com158618e2012-10-23 16:56:56 +0000442 SkRect result;
443 REPORTER_ASSERT(reporter, temp.isRect(&result));
444 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000445 }
446}
447
reed@google.com0bb18bb2012-07-26 15:20:36 +0000448static void test_rect_isfinite(skiatest::Reporter* reporter) {
449 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000450 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000451 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000452
reed@google.com0bb18bb2012-07-26 15:20:36 +0000453 SkRect r;
454 r.setEmpty();
455 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000456 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000457 REPORTER_ASSERT(reporter, !r.isFinite());
458 r.set(0, 0, nan, 0);
459 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000460
reed@google.com0bb18bb2012-07-26 15:20:36 +0000461 SkPoint pts[] = {
462 { 0, 0 },
463 { SK_Scalar1, 0 },
464 { 0, SK_Scalar1 },
465 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000466
reed@google.com0bb18bb2012-07-26 15:20:36 +0000467 bool isFine = r.setBoundsCheck(pts, 3);
468 REPORTER_ASSERT(reporter, isFine);
469 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000470
reed@google.com0bb18bb2012-07-26 15:20:36 +0000471 pts[1].set(inf, 0);
472 isFine = r.setBoundsCheck(pts, 3);
473 REPORTER_ASSERT(reporter, !isFine);
474 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000475
reed@google.com0bb18bb2012-07-26 15:20:36 +0000476 pts[1].set(nan, 0);
477 isFine = r.setBoundsCheck(pts, 3);
478 REPORTER_ASSERT(reporter, !isFine);
479 REPORTER_ASSERT(reporter, r.isEmpty());
480}
481
482static void test_path_isfinite(skiatest::Reporter* reporter) {
483 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000484 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000485 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000486
reed@google.com0bb18bb2012-07-26 15:20:36 +0000487 SkPath path;
488 REPORTER_ASSERT(reporter, path.isFinite());
489
490 path.reset();
491 REPORTER_ASSERT(reporter, path.isFinite());
492
493 path.reset();
494 path.moveTo(SK_Scalar1, 0);
495 REPORTER_ASSERT(reporter, path.isFinite());
496
497 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000498 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000499 REPORTER_ASSERT(reporter, !path.isFinite());
500
501 path.reset();
502 path.moveTo(nan, 0);
503 REPORTER_ASSERT(reporter, !path.isFinite());
504}
505
506static void test_isfinite(skiatest::Reporter* reporter) {
507 test_rect_isfinite(reporter);
508 test_path_isfinite(reporter);
509}
510
reed@google.com744faba2012-05-29 19:54:52 +0000511// assert that we always
512// start with a moveTo
513// only have 1 moveTo
514// only have Lines after that
515// end with a single close
516// only have (at most) 1 close
517//
518static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000519 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000520 SkPath::RawIter iter(path);
521 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000522
523 bool firstTime = true;
524 bool foundClose = false;
525 for (;;) {
526 switch (iter.next(pts)) {
527 case SkPath::kMove_Verb:
528 REPORTER_ASSERT(reporter, firstTime);
529 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
530 srcPts++;
531 firstTime = false;
532 break;
533 case SkPath::kLine_Verb:
534 REPORTER_ASSERT(reporter, !firstTime);
535 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
536 srcPts++;
537 break;
538 case SkPath::kQuad_Verb:
539 REPORTER_ASSERT(reporter, !"unexpected quad verb");
540 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000541 case SkPath::kConic_Verb:
542 REPORTER_ASSERT(reporter, !"unexpected conic verb");
543 break;
reed@google.com744faba2012-05-29 19:54:52 +0000544 case SkPath::kCubic_Verb:
545 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
546 break;
547 case SkPath::kClose_Verb:
548 REPORTER_ASSERT(reporter, !firstTime);
549 REPORTER_ASSERT(reporter, !foundClose);
550 REPORTER_ASSERT(reporter, expectClose);
551 foundClose = true;
552 break;
553 case SkPath::kDone_Verb:
554 goto DONE;
555 }
556 }
557DONE:
558 REPORTER_ASSERT(reporter, foundClose == expectClose);
559}
560
561static void test_addPoly(skiatest::Reporter* reporter) {
562 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000563 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000564
reed@google.com744faba2012-05-29 19:54:52 +0000565 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
566 pts[i].fX = rand.nextSScalar1();
567 pts[i].fY = rand.nextSScalar1();
568 }
569
570 for (int doClose = 0; doClose <= 1; ++doClose) {
571 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
572 SkPath path;
573 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000574 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000575 }
576 }
577}
578
reed@google.com8b06f1a2012-05-29 12:03:46 +0000579static void test_strokerec(skiatest::Reporter* reporter) {
580 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
581 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000582
reed@google.com8b06f1a2012-05-29 12:03:46 +0000583 rec.setHairlineStyle();
584 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000585
reed@google.com8b06f1a2012-05-29 12:03:46 +0000586 rec.setStrokeStyle(SK_Scalar1, false);
587 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000588
reed@google.com8b06f1a2012-05-29 12:03:46 +0000589 rec.setStrokeStyle(SK_Scalar1, true);
590 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000591
reed@google.com8b06f1a2012-05-29 12:03:46 +0000592 rec.setStrokeStyle(0, false);
593 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000594
reed@google.com8b06f1a2012-05-29 12:03:46 +0000595 rec.setStrokeStyle(0, true);
596 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
597}
598
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000599// Set this for paths that don't have a consistent direction such as a bowtie.
600// (cheapComputeDirection is not expected to catch these.)
601static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
602
603static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
604 SkPath::Direction expected) {
605 if (expected == kDontCheckDir) {
606 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000607 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000608 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
609
610 SkPath::Direction dir;
611 if (copy.cheapComputeDirection(&dir)) {
612 REPORTER_ASSERT(reporter, dir == expected);
613 } else {
614 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
615 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000616}
617
reed@google.com3e71a882012-01-10 18:44:37 +0000618static void test_direction(skiatest::Reporter* reporter) {
619 size_t i;
620 SkPath path;
621 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
622 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
623 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000624 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000625
626 static const char* gDegen[] = {
627 "M 10 10",
628 "M 10 10 M 20 20",
629 "M 10 10 L 20 20",
630 "M 10 10 L 10 10 L 10 10",
631 "M 10 10 Q 10 10 10 10",
632 "M 10 10 C 10 10 10 10 10 10",
633 };
634 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
635 path.reset();
636 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
637 REPORTER_ASSERT(reporter, valid);
638 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
639 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000640
reed@google.com3e71a882012-01-10 18:44:37 +0000641 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000642 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000643 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000644 "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 +0000645 // rect with top two corners replaced by cubics with identical middle
646 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000647 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
648 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000649 };
650 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
651 path.reset();
652 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
653 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000654 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000655 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000656
reed@google.com3e71a882012-01-10 18:44:37 +0000657 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000658 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000659 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000660 "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 +0000661 // rect with top two corners replaced by cubics with identical middle
662 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000663 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
664 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000665 };
666 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
667 path.reset();
668 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
669 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000670 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000671 }
reed@google.comac8543f2012-01-30 20:51:25 +0000672
673 // Test two donuts, each wound a different direction. Only the outer contour
674 // determines the cheap direction
675 path.reset();
676 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
677 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000678 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000679
reed@google.comac8543f2012-01-30 20:51:25 +0000680 path.reset();
681 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
682 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000683 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000684
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000685#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000686 // triangle with one point really far from the origin.
687 path.reset();
688 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000689 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
690 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
691 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000692 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000693#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000694}
695
reed@google.comffdb0182011-11-14 19:29:14 +0000696static void add_rect(SkPath* path, const SkRect& r) {
697 path->moveTo(r.fLeft, r.fTop);
698 path->lineTo(r.fRight, r.fTop);
699 path->lineTo(r.fRight, r.fBottom);
700 path->lineTo(r.fLeft, r.fBottom);
701 path->close();
702}
703
704static void test_bounds(skiatest::Reporter* reporter) {
705 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000706 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
707 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
708 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
709 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000710 };
711
712 SkPath path0, path1;
713 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
714 path0.addRect(rects[i]);
715 add_rect(&path1, rects[i]);
716 }
717
718 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
719}
720
reed@google.com55b5f4b2011-09-07 12:23:41 +0000721static void stroke_cubic(const SkPoint pts[4]) {
722 SkPath path;
723 path.moveTo(pts[0]);
724 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000725
reed@google.com55b5f4b2011-09-07 12:23:41 +0000726 SkPaint paint;
727 paint.setStyle(SkPaint::kStroke_Style);
728 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000729
reed@google.com55b5f4b2011-09-07 12:23:41 +0000730 SkPath fill;
731 paint.getFillPath(path, &fill);
732}
733
734// just ensure this can run w/o any SkASSERTS firing in the debug build
735// we used to assert due to differences in how we determine a degenerate vector
736// but that was fixed with the introduction of SkPoint::CanNormalize
737static void stroke_tiny_cubic() {
738 SkPoint p0[] = {
739 { 372.0f, 92.0f },
740 { 372.0f, 92.0f },
741 { 372.0f, 92.0f },
742 { 372.0f, 92.0f },
743 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000744
reed@google.com55b5f4b2011-09-07 12:23:41 +0000745 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000746
reed@google.com55b5f4b2011-09-07 12:23:41 +0000747 SkPoint p1[] = {
748 { 372.0f, 92.0f },
749 { 372.0007f, 92.000755f },
750 { 371.99927f, 92.003922f },
751 { 371.99826f, 92.003899f },
752 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000753
reed@google.com55b5f4b2011-09-07 12:23:41 +0000754 stroke_cubic(p1);
755}
756
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000757static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
758 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000759 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000760 SkPoint mv;
761 SkPoint pts[4];
762 SkPath::Verb v;
763 int nMT = 0;
764 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000765 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000766 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
767 switch (v) {
768 case SkPath::kMove_Verb:
769 mv = pts[0];
770 ++nMT;
771 break;
772 case SkPath::kClose_Verb:
773 REPORTER_ASSERT(reporter, mv == pts[0]);
774 ++nCL;
775 break;
776 default:
777 break;
778 }
779 }
780 // if we force a close on the interator we should have a close
781 // for every moveTo
782 REPORTER_ASSERT(reporter, !i || nMT == nCL);
783 }
784}
785
786static void test_close(skiatest::Reporter* reporter) {
787 SkPath closePt;
788 closePt.moveTo(0, 0);
789 closePt.close();
790 check_close(reporter, closePt);
791
792 SkPath openPt;
793 openPt.moveTo(0, 0);
794 check_close(reporter, openPt);
795
796 SkPath empty;
797 check_close(reporter, empty);
798 empty.close();
799 check_close(reporter, empty);
800
801 SkPath rect;
802 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
803 check_close(reporter, rect);
804 rect.close();
805 check_close(reporter, rect);
806
807 SkPath quad;
808 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
809 check_close(reporter, quad);
810 quad.close();
811 check_close(reporter, quad);
812
813 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000814 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000815 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
816 check_close(reporter, cubic);
817 cubic.close();
818 check_close(reporter, cubic);
819
820 SkPath line;
821 line.moveTo(SK_Scalar1, SK_Scalar1);
822 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
823 check_close(reporter, line);
824 line.close();
825 check_close(reporter, line);
826
827 SkPath rect2;
828 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
829 rect2.close();
830 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
831 check_close(reporter, rect2);
832 rect2.close();
833 check_close(reporter, rect2);
834
835 SkPath oval3;
836 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
837 oval3.close();
838 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
839 check_close(reporter, oval3);
840 oval3.close();
841 check_close(reporter, oval3);
842
843 SkPath moves;
844 moves.moveTo(SK_Scalar1, SK_Scalar1);
845 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
846 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
847 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
848 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000849
850 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000851}
852
reed@google.com7c424812011-05-15 04:38:34 +0000853static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
854 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000855 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
856 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000857 REPORTER_ASSERT(reporter, c == expected);
858}
859
860static void test_convexity2(skiatest::Reporter* reporter) {
861 SkPath pt;
862 pt.moveTo(0, 0);
863 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000864 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000865 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000866
reed@google.com7c424812011-05-15 04:38:34 +0000867 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000868 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
869 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000870 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000871 check_convexity(reporter, line, SkPath::kConvex_Convexity);
872 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000873
reed@google.com7c424812011-05-15 04:38:34 +0000874 SkPath triLeft;
875 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000876 triLeft.lineTo(SK_Scalar1, 0);
877 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000878 triLeft.close();
879 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000880 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000881
reed@google.com7c424812011-05-15 04:38:34 +0000882 SkPath triRight;
883 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000884 triRight.lineTo(-SK_Scalar1, 0);
885 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000886 triRight.close();
887 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000888 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000889
reed@google.com7c424812011-05-15 04:38:34 +0000890 SkPath square;
891 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000892 square.lineTo(SK_Scalar1, 0);
893 square.lineTo(SK_Scalar1, SK_Scalar1);
894 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000895 square.close();
896 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000897 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000898
reed@google.com7c424812011-05-15 04:38:34 +0000899 SkPath redundantSquare;
900 redundantSquare.moveTo(0, 0);
901 redundantSquare.lineTo(0, 0);
902 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000903 redundantSquare.lineTo(SK_Scalar1, 0);
904 redundantSquare.lineTo(SK_Scalar1, 0);
905 redundantSquare.lineTo(SK_Scalar1, 0);
906 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
907 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
908 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
909 redundantSquare.lineTo(0, SK_Scalar1);
910 redundantSquare.lineTo(0, SK_Scalar1);
911 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000912 redundantSquare.close();
913 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000914 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000915
reed@google.com7c424812011-05-15 04:38:34 +0000916 SkPath bowTie;
917 bowTie.moveTo(0, 0);
918 bowTie.lineTo(0, 0);
919 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000920 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
921 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
922 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
923 bowTie.lineTo(SK_Scalar1, 0);
924 bowTie.lineTo(SK_Scalar1, 0);
925 bowTie.lineTo(SK_Scalar1, 0);
926 bowTie.lineTo(0, SK_Scalar1);
927 bowTie.lineTo(0, SK_Scalar1);
928 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000929 bowTie.close();
930 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000931 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000932
reed@google.com7c424812011-05-15 04:38:34 +0000933 SkPath spiral;
934 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000935 spiral.lineTo(100*SK_Scalar1, 0);
936 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
937 spiral.lineTo(0, 100*SK_Scalar1);
938 spiral.lineTo(0, 50*SK_Scalar1);
939 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
940 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000941 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000942 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000943 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000944
reed@google.com7c424812011-05-15 04:38:34 +0000945 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000946 dent.moveTo(0, 0);
947 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
948 dent.lineTo(0, 100*SK_Scalar1);
949 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
950 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000951 dent.close();
952 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000953 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000954}
955
reed@android.com6b82d1a2009-06-03 02:35:01 +0000956static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
957 const SkRect& bounds) {
958 REPORTER_ASSERT(reporter, p.isConvex());
959 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000960
reed@android.com6b82d1a2009-06-03 02:35:01 +0000961 SkPath p2(p);
962 REPORTER_ASSERT(reporter, p2.isConvex());
963 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
964
965 SkPath other;
966 other.swap(p2);
967 REPORTER_ASSERT(reporter, other.isConvex());
968 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
969}
970
reed@google.com04863fa2011-05-15 04:08:24 +0000971static void setFromString(SkPath* path, const char str[]) {
972 bool first = true;
973 while (str) {
974 SkScalar x, y;
975 str = SkParse::FindScalar(str, &x);
976 if (NULL == str) {
977 break;
978 }
979 str = SkParse::FindScalar(str, &y);
980 SkASSERT(str);
981 if (first) {
982 path->moveTo(x, y);
983 first = false;
984 } else {
985 path->lineTo(x, y);
986 }
987 }
988}
989
990static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +0000991 SkPath path;
992
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000993 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000994 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000995 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +0000996 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000997 check_convexity(reporter, path, SkPath::kConcave_Convexity);
998
reed@google.com04863fa2011-05-15 04:08:24 +0000999 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001000 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001001 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001002 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001003
reed@google.com04863fa2011-05-15 04:08:24 +00001004 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001005 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001006 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001007 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001008
reed@google.com04863fa2011-05-15 04:08:24 +00001009 static const struct {
1010 const char* fPathStr;
1011 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001012 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +00001013 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001014 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1015 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1016 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1017 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
1018 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
1019 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
1020 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
1021 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +00001022 };
1023
1024 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1025 SkPath path;
1026 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001027 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
1028 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +00001029 }
1030}
1031
reed@google.com7e6c4d12012-05-10 14:05:43 +00001032static void test_isLine(skiatest::Reporter* reporter) {
1033 SkPath path;
1034 SkPoint pts[2];
1035 const SkScalar value = SkIntToScalar(5);
1036
1037 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001038
reed@google.com7e6c4d12012-05-10 14:05:43 +00001039 // set some non-zero values
1040 pts[0].set(value, value);
1041 pts[1].set(value, value);
1042 REPORTER_ASSERT(reporter, !path.isLine(pts));
1043 // check that pts was untouched
1044 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1045 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1046
1047 const SkScalar moveX = SkIntToScalar(1);
1048 const SkScalar moveY = SkIntToScalar(2);
1049 SkASSERT(value != moveX && value != moveY);
1050
1051 path.moveTo(moveX, moveY);
1052 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1053 REPORTER_ASSERT(reporter, !path.isLine(pts));
1054 // check that pts was untouched
1055 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1056 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1057
1058 const SkScalar lineX = SkIntToScalar(2);
1059 const SkScalar lineY = SkIntToScalar(2);
1060 SkASSERT(value != lineX && value != lineY);
1061
1062 path.lineTo(lineX, lineY);
1063 REPORTER_ASSERT(reporter, path.isLine(NULL));
1064
1065 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1066 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1067 REPORTER_ASSERT(reporter, path.isLine(pts));
1068 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1069 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1070
1071 path.lineTo(0, 0); // too many points/verbs
1072 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1073 REPORTER_ASSERT(reporter, !path.isLine(pts));
1074 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1075 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1076}
1077
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001078static void test_conservativelyContains(skiatest::Reporter* reporter) {
1079 SkPath path;
1080
1081 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1082 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1083
1084 // A circle that bounds kBaseRect (with a significant amount of slop)
1085 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1086 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1087 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1088
1089 // round-rect radii
1090 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001091
caryclark@google.com56f233a2012-11-19 13:06:06 +00001092 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001093 SkRect fQueryRect;
1094 bool fInRect;
1095 bool fInCircle;
1096 bool fInRR;
1097 } kQueries[] = {
1098 {kBaseRect, true, true, false},
1099
1100 // rect well inside of kBaseRect
1101 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1102 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1103 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1104 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1105 true, true, true},
1106
1107 // rects with edges off by one from kBaseRect's edges
1108 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1109 kBaseRect.width(), kBaseRect.height() + 1),
1110 false, true, false},
1111 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1112 kBaseRect.width() + 1, kBaseRect.height()),
1113 false, true, false},
1114 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1115 kBaseRect.width() + 1, kBaseRect.height() + 1),
1116 false, true, false},
1117 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1118 kBaseRect.width(), kBaseRect.height()),
1119 false, true, false},
1120 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1121 kBaseRect.width(), kBaseRect.height()),
1122 false, true, false},
1123 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1124 kBaseRect.width() + 2, kBaseRect.height()),
1125 false, true, false},
1126 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1127 kBaseRect.width() + 2, kBaseRect.height()),
1128 false, true, false},
1129
1130 // zero-w/h rects at each corner of kBaseRect
1131 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1132 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1133 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1134 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1135
1136 // far away rect
1137 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1138 SkIntToScalar(10), SkIntToScalar(10)),
1139 false, false, false},
1140
1141 // very large rect containing kBaseRect
1142 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1143 kBaseRect.fTop - 5 * kBaseRect.height(),
1144 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1145 false, false, false},
1146
1147 // skinny rect that spans same y-range as kBaseRect
1148 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1149 SkIntToScalar(1), kBaseRect.height()),
1150 true, true, true},
1151
1152 // short rect that spans same x-range as kBaseRect
1153 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1154 true, true, true},
1155
1156 // skinny rect that spans slightly larger y-range than kBaseRect
1157 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1158 SkIntToScalar(1), kBaseRect.height() + 1),
1159 false, true, false},
1160
1161 // short rect that spans slightly larger x-range than kBaseRect
1162 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1163 kBaseRect.width() + 1, SkScalar(1)),
1164 false, true, false},
1165 };
1166
1167 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001168 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001169 SkRect qRect = kQueries[q].fQueryRect;
1170 if (inv & 0x1) {
1171 SkTSwap(qRect.fLeft, qRect.fRight);
1172 }
1173 if (inv & 0x2) {
1174 SkTSwap(qRect.fTop, qRect.fBottom);
1175 }
1176 for (int d = 0; d < 2; ++d) {
1177 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1178 path.reset();
1179 path.addRect(kBaseRect, dir);
1180 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1181 path.conservativelyContainsRect(qRect));
1182
1183 path.reset();
1184 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1185 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1186 path.conservativelyContainsRect(qRect));
1187
1188 path.reset();
1189 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1190 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1191 path.conservativelyContainsRect(qRect));
1192 }
1193 // Slightly non-convex shape, shouldn't contain any rects.
1194 path.reset();
1195 path.moveTo(0, 0);
1196 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1197 path.lineTo(SkIntToScalar(100), 0);
1198 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1199 path.lineTo(0, SkIntToScalar(100));
1200 path.close();
1201 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1202 }
1203 }
1204
1205 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1206 path.reset();
1207 path.moveTo(0, 0);
1208 path.lineTo(SkIntToScalar(100), 0);
1209 path.lineTo(0, SkIntToScalar(100));
1210
1211 // inside, on along top edge
1212 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1213 SkIntToScalar(10),
1214 SkIntToScalar(10))));
1215 // above
1216 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1217 SkRect::MakeXYWH(SkIntToScalar(50),
1218 SkIntToScalar(-10),
1219 SkIntToScalar(10),
1220 SkIntToScalar(10))));
1221 // to the left
1222 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1223 SkIntToScalar(5),
1224 SkIntToScalar(5),
1225 SkIntToScalar(5))));
1226
1227 // outside the diagonal edge
1228 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1229 SkIntToScalar(200),
1230 SkIntToScalar(20),
1231 SkIntToScalar(5))));
commit-bot@chromium.org62df5262013-08-01 15:35:06 +00001232
1233 // same as above path and first test but with an extra moveTo.
1234 path.reset();
1235 path.moveTo(100, 100);
1236 path.moveTo(0, 0);
1237 path.lineTo(SkIntToScalar(100), 0);
1238 path.lineTo(0, SkIntToScalar(100));
1239
1240 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1241 SkIntToScalar(10),
1242 SkIntToScalar(10))));
1243
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001244}
1245
caryclark@google.comf1316942011-07-26 19:54:45 +00001246// Simple isRect test is inline TestPath, below.
1247// test_isRect provides more extensive testing.
1248static void test_isRect(skiatest::Reporter* reporter) {
1249 // passing tests (all moveTo / lineTo...
1250 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1251 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1252 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1253 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1254 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1255 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1256 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1257 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1258 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1259 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1260 {1, 0}, {.5f, 0}};
1261 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1262 {0, 1}, {0, .5f}};
1263 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1264 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1265 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001266 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001267
caryclark@google.comf1316942011-07-26 19:54:45 +00001268 // failing tests
1269 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1270 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1271 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1272 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1273 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1274 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1275 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1276 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001277 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1278 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1279 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001280
caryclark@google.comf1316942011-07-26 19:54:45 +00001281 // failing, no close
1282 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1283 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1284
1285 size_t testLen[] = {
1286 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1287 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001288 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001289 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001290 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001291 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001292 };
1293 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001294 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1295 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001296 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001297 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001298 SkPoint* lastPass = rf;
1299 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001300 bool fail = false;
1301 bool close = true;
1302 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1303 size_t index;
1304 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1305 SkPath path;
1306 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1307 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1308 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1309 }
1310 if (close) {
1311 path.close();
1312 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001313 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1314 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001315
caryclark@google.com56f233a2012-11-19 13:06:06 +00001316 if (!fail) {
1317 SkRect computed, expected;
1318 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1319 REPORTER_ASSERT(reporter, path.isRect(&computed));
1320 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001321
caryclark@google.comf68154a2012-11-21 15:18:06 +00001322 bool isClosed;
1323 SkPath::Direction direction, cheapDirection;
1324 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001325 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001326 REPORTER_ASSERT(reporter, isClosed == close);
1327 REPORTER_ASSERT(reporter, direction == cheapDirection);
1328 } else {
1329 SkRect computed;
1330 computed.set(123, 456, 789, 1011);
1331 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1332 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1333 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1334
1335 bool isClosed = (bool) -1;
1336 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001337 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001338 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1339 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001340 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001341
caryclark@google.comf1316942011-07-26 19:54:45 +00001342 if (tests[testIndex] == lastPass) {
1343 fail = true;
1344 }
1345 if (tests[testIndex] == lastClose) {
1346 close = false;
1347 }
1348 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001349
caryclark@google.comf1316942011-07-26 19:54:45 +00001350 // fail, close then line
1351 SkPath path1;
1352 path1.moveTo(r1[0].fX, r1[0].fY);
1353 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1354 path1.lineTo(r1[index].fX, r1[index].fY);
1355 }
1356 path1.close();
1357 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001358 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001359
caryclark@google.comf1316942011-07-26 19:54:45 +00001360 // fail, move in the middle
1361 path1.reset();
1362 path1.moveTo(r1[0].fX, r1[0].fY);
1363 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1364 if (index == 2) {
1365 path1.moveTo(1, .5f);
1366 }
1367 path1.lineTo(r1[index].fX, r1[index].fY);
1368 }
1369 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001370 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001371
1372 // fail, move on the edge
1373 path1.reset();
1374 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1375 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1376 path1.lineTo(r1[index].fX, r1[index].fY);
1377 }
1378 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001379 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001380
caryclark@google.comf1316942011-07-26 19:54:45 +00001381 // fail, quad
1382 path1.reset();
1383 path1.moveTo(r1[0].fX, r1[0].fY);
1384 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1385 if (index == 2) {
1386 path1.quadTo(1, .5f, 1, .5f);
1387 }
1388 path1.lineTo(r1[index].fX, r1[index].fY);
1389 }
1390 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001391 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001392
caryclark@google.comf1316942011-07-26 19:54:45 +00001393 // fail, cubic
1394 path1.reset();
1395 path1.moveTo(r1[0].fX, r1[0].fY);
1396 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1397 if (index == 2) {
1398 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1399 }
1400 path1.lineTo(r1[index].fX, r1[index].fY);
1401 }
1402 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001403 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001404}
1405
caryclark@google.com56f233a2012-11-19 13:06:06 +00001406static void test_isNestedRects(skiatest::Reporter* reporter) {
1407 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001408 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001409 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1410 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1411 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001412 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001413 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1414 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1415 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1416 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001417 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001418 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001419 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001420 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001421 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1422 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1423 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001424
1425 // failing tests
1426 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1427 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1428 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1429 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1430 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1431 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1432 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1433 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1434
1435 // failing, no close
1436 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1437 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1438
1439 size_t testLen[] = {
1440 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1441 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1442 sizeof(rd), sizeof(re),
1443 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1444 sizeof(f7), sizeof(f8),
1445 sizeof(c1), sizeof(c2)
1446 };
1447 SkPoint* tests[] = {
1448 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1449 f1, f2, f3, f4, f5, f6, f7, f8,
1450 c1, c2
1451 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001452 SkPath::Direction dirs[] = {
1453 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001454 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001455 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1456 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001457 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1458 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1459 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1460 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1461 };
1462 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1463
caryclark@google.com56f233a2012-11-19 13:06:06 +00001464 const SkPoint* lastPass = re;
1465 const SkPoint* lastClose = f8;
1466 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1467 size_t index;
1468 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1469 bool fail = false;
1470 bool close = true;
1471 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1472 SkPath path;
1473 if (rectFirst) {
1474 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1475 }
1476 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1477 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1478 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1479 }
1480 if (close) {
1481 path.close();
1482 }
1483 if (!rectFirst) {
1484 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1485 }
1486 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1487 if (!fail) {
1488 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001489 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001490 SkRect testBounds;
1491 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1492 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1493 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001494 if (rectFirst) {
1495 expectedDirs[0] = SkPath::kCW_Direction;
1496 } else {
1497 expectedDirs[0] = SkPath::kCCW_Direction;
1498 }
1499 expectedDirs[1] = dirs[testIndex];
1500 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001501 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1502 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001503 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1504 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001505 }
1506 if (tests[testIndex] == lastPass) {
1507 fail = true;
1508 }
1509 if (tests[testIndex] == lastClose) {
1510 close = false;
1511 }
1512 }
1513
1514 // fail, close then line
1515 SkPath path1;
1516 if (rectFirst) {
1517 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1518 }
1519 path1.moveTo(r1[0].fX, r1[0].fY);
1520 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1521 path1.lineTo(r1[index].fX, r1[index].fY);
1522 }
1523 path1.close();
1524 path1.lineTo(1, 0);
1525 if (!rectFirst) {
1526 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1527 }
1528 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1529
1530 // fail, move in the middle
1531 path1.reset();
1532 if (rectFirst) {
1533 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1534 }
1535 path1.moveTo(r1[0].fX, r1[0].fY);
1536 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1537 if (index == 2) {
1538 path1.moveTo(1, .5f);
1539 }
1540 path1.lineTo(r1[index].fX, r1[index].fY);
1541 }
1542 path1.close();
1543 if (!rectFirst) {
1544 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1545 }
1546 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1547
1548 // fail, move on the edge
1549 path1.reset();
1550 if (rectFirst) {
1551 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1552 }
1553 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1554 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1555 path1.lineTo(r1[index].fX, r1[index].fY);
1556 }
1557 path1.close();
1558 if (!rectFirst) {
1559 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1560 }
1561 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1562
1563 // fail, quad
1564 path1.reset();
1565 if (rectFirst) {
1566 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1567 }
1568 path1.moveTo(r1[0].fX, r1[0].fY);
1569 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1570 if (index == 2) {
1571 path1.quadTo(1, .5f, 1, .5f);
1572 }
1573 path1.lineTo(r1[index].fX, r1[index].fY);
1574 }
1575 path1.close();
1576 if (!rectFirst) {
1577 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1578 }
1579 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1580
1581 // fail, cubic
1582 path1.reset();
1583 if (rectFirst) {
1584 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1585 }
1586 path1.moveTo(r1[0].fX, r1[0].fY);
1587 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1588 if (index == 2) {
1589 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1590 }
1591 path1.lineTo(r1[index].fX, r1[index].fY);
1592 }
1593 path1.close();
1594 if (!rectFirst) {
1595 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1596 }
1597 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001598
caryclark@google.com56f233a2012-11-19 13:06:06 +00001599 // fail, not nested
1600 path1.reset();
1601 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1602 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1603 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1604 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001605
1606 // pass, stroke rect
1607 SkPath src, dst;
1608 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1609 SkPaint strokePaint;
1610 strokePaint.setStyle(SkPaint::kStroke_Style);
1611 strokePaint.setStrokeWidth(2);
1612 strokePaint.getFillPath(src, &dst);
1613 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001614}
1615
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001616static void write_and_read_back(skiatest::Reporter* reporter,
1617 const SkPath& p) {
1618 SkWriter32 writer(100);
1619 writer.writePath(p);
1620 size_t size = writer.size();
1621 SkAutoMalloc storage(size);
1622 writer.flatten(storage.get());
1623 SkReader32 reader(storage.get(), size);
1624
1625 SkPath readBack;
1626 REPORTER_ASSERT(reporter, readBack != p);
1627 reader.readPath(&readBack);
1628 REPORTER_ASSERT(reporter, readBack == p);
1629
rmistry@google.comd6176b02012-08-23 18:14:13 +00001630 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001631 p.getConvexityOrUnknown());
1632
1633 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1634
1635 const SkRect& origBounds = p.getBounds();
1636 const SkRect& readBackBounds = readBack.getBounds();
1637
1638 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1639}
1640
reed@google.com53effc52011-09-21 19:05:12 +00001641static void test_flattening(skiatest::Reporter* reporter) {
1642 SkPath p;
1643
1644 static const SkPoint pts[] = {
1645 { 0, 0 },
1646 { SkIntToScalar(10), SkIntToScalar(10) },
1647 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1648 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1649 };
1650 p.moveTo(pts[0]);
1651 p.lineTo(pts[1]);
1652 p.quadTo(pts[2], pts[3]);
1653 p.cubicTo(pts[4], pts[5], pts[6]);
1654
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001655 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001656
1657 // create a buffer that should be much larger than the path so we don't
1658 // kill our stack if writer goes too far.
1659 char buffer[1024];
1660 uint32_t size1 = p.writeToMemory(NULL);
1661 uint32_t size2 = p.writeToMemory(buffer);
1662 REPORTER_ASSERT(reporter, size1 == size2);
1663
1664 SkPath p2;
1665 uint32_t size3 = p2.readFromMemory(buffer);
1666 REPORTER_ASSERT(reporter, size1 == size3);
1667 REPORTER_ASSERT(reporter, p == p2);
1668
1669 char buffer2[1024];
1670 size3 = p2.writeToMemory(buffer2);
1671 REPORTER_ASSERT(reporter, size1 == size3);
1672 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001673
1674 // test persistence of the oval flag & convexity
1675 {
1676 SkPath oval;
1677 SkRect rect = SkRect::MakeWH(10, 10);
1678 oval.addOval(rect);
1679
1680 write_and_read_back(reporter, oval);
1681 }
reed@google.com53effc52011-09-21 19:05:12 +00001682}
1683
1684static void test_transform(skiatest::Reporter* reporter) {
1685 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001686
reed@google.com53effc52011-09-21 19:05:12 +00001687 static const SkPoint pts[] = {
1688 { 0, 0 },
1689 { SkIntToScalar(10), SkIntToScalar(10) },
1690 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1691 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1692 };
1693 p.moveTo(pts[0]);
1694 p.lineTo(pts[1]);
1695 p.quadTo(pts[2], pts[3]);
1696 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001697
reed@google.com53effc52011-09-21 19:05:12 +00001698 SkMatrix matrix;
1699 matrix.reset();
1700 p.transform(matrix, &p1);
1701 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001702
reed@google.com53effc52011-09-21 19:05:12 +00001703 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1704 p.transform(matrix, &p1);
1705 SkPoint pts1[7];
1706 int count = p1.getPoints(pts1, 7);
1707 REPORTER_ASSERT(reporter, 7 == count);
1708 for (int i = 0; i < count; ++i) {
1709 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1710 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1711 }
1712}
1713
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001714static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001715 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001716 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001717
caryclark@google.com56f233a2012-11-19 13:06:06 +00001718 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001719 const char* testPath;
1720 const size_t numResultPts;
1721 const SkRect resultBound;
1722 const SkPath::Verb* resultVerbs;
1723 const size_t numResultVerbs;
1724 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001725
schenney@chromium.org7e963602012-06-13 17:05:43 +00001726 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1727 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1728 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1729 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1730 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1731 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1732 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1733 static const SkPath::Verb resultVerbs8[] = {
1734 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1735 };
1736 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1737 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1738 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1739 static const SkPath::Verb resultVerbs12[] = {
1740 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1741 };
1742 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1743 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1744 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1745 static const SkPath::Verb resultVerbs16[] = {
1746 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1747 };
1748 static const struct zeroPathTestData gZeroLengthTests[] = {
1749 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001750 { "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 +00001751 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001752 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1753 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1754 { "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) },
1755 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1756 { "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) },
1757 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1758 { "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) },
1759 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1760 { "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) },
1761 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1762 { "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 +00001763 SK_ARRAY_COUNT(resultVerbs14)
1764 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001765 { "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) },
1766 { "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 +00001767 SK_ARRAY_COUNT(resultVerbs16)
1768 }
1769 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001770
schenney@chromium.org7e963602012-06-13 17:05:43 +00001771 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1772 p.reset();
1773 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1774 REPORTER_ASSERT(reporter, valid);
1775 REPORTER_ASSERT(reporter, !p.isEmpty());
1776 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1777 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1778 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1779 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1780 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1781 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001782 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001783}
1784
1785struct SegmentInfo {
1786 SkPath fPath;
1787 int fPointCount;
1788};
1789
reed@google.com10296cc2011-09-21 12:29:05 +00001790#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1791
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001792static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001793 SkPath p, p2;
1794
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001795 p.moveTo(0, 0);
1796 p.quadTo(100, 100, 200, 200);
1797 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1798 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001799 p2 = p;
1800 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001801 p.cubicTo(100, 100, 200, 200, 300, 300);
1802 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1803 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001804 p2 = p;
1805 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1806
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001807 p.reset();
1808 p.moveTo(0, 0);
1809 p.cubicTo(100, 100, 200, 200, 300, 300);
1810 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001811 p2 = p;
1812 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001813
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001814 REPORTER_ASSERT(reporter, !p.isEmpty());
1815}
1816
1817static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001818 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001819 SkPoint pts[4];
1820
1821 // Test an iterator with no path
1822 SkPath::Iter noPathIter;
1823 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001824
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001825 // Test that setting an empty path works
1826 noPathIter.setPath(p, false);
1827 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001828
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001829 // Test that close path makes no difference for an empty path
1830 noPathIter.setPath(p, true);
1831 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001832
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001833 // Test an iterator with an initial empty path
1834 SkPath::Iter iter(p, false);
1835 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1836
1837 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001838 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001839 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1840
rmistry@google.comd6176b02012-08-23 18:14:13 +00001841
schenney@chromium.org7e963602012-06-13 17:05:43 +00001842 struct iterTestData {
1843 const char* testPath;
1844 const bool forceClose;
1845 const bool consumeDegenerates;
1846 const size_t* numResultPtsPerVerb;
1847 const SkPoint* resultPts;
1848 const SkPath::Verb* resultVerbs;
1849 const size_t numResultVerbs;
1850 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001851
schenney@chromium.org7e963602012-06-13 17:05:43 +00001852 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1853 static const SkPath::Verb resultVerbs2[] = {
1854 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1855 };
1856 static const SkPath::Verb resultVerbs3[] = {
1857 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1858 };
1859 static const SkPath::Verb resultVerbs4[] = {
1860 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1861 };
1862 static const SkPath::Verb resultVerbs5[] = {
1863 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1864 };
1865 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001866 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1867 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1868 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1869 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001870 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001871 static const SkPoint resultPts2[] = {
1872 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1873 };
1874 static const SkPoint resultPts3[] = {
1875 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1876 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1877 };
1878 static const SkPoint resultPts4[] = {
1879 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1880 };
1881 static const SkPoint resultPts5[] = {
1882 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1883 };
1884 static const struct iterTestData gIterTests[] = {
1885 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001886 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1887 { "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 +00001888 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1889 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1890 { "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) },
1891 { "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 +00001892 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1893 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1894 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1895 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1896 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1897 { "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 +00001898 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001899
schenney@chromium.org7e963602012-06-13 17:05:43 +00001900 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1901 p.reset();
1902 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1903 REPORTER_ASSERT(reporter, valid);
1904 iter.setPath(p, gIterTests[i].forceClose);
1905 int j = 0, l = 0;
1906 do {
1907 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1908 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1909 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1910 }
1911 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1912 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1913 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001914
1915 // The GM degeneratesegments.cpp test is more extensive
1916}
1917
1918static void test_raw_iter(skiatest::Reporter* reporter) {
1919 SkPath p;
1920 SkPoint pts[4];
1921
1922 // Test an iterator with no path
1923 SkPath::RawIter noPathIter;
1924 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1925 // Test that setting an empty path works
1926 noPathIter.setPath(p);
1927 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001928
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001929 // Test an iterator with an initial empty path
1930 SkPath::RawIter iter(p);
1931 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1932
1933 // Test that a move-only path returns the move.
1934 p.moveTo(SK_Scalar1, 0);
1935 iter.setPath(p);
1936 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1937 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1938 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1939 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1940
1941 // No matter how many moves we add, we should get them all back
1942 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1943 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1944 iter.setPath(p);
1945 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1946 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1947 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1948 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1949 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1950 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1951 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1952 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1953 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1954 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1955
1956 // Initial close is never ever stored
1957 p.reset();
1958 p.close();
1959 iter.setPath(p);
1960 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1961
1962 // Move/close sequences
1963 p.reset();
1964 p.close(); // Not stored, no purpose
1965 p.moveTo(SK_Scalar1, 0);
1966 p.close();
1967 p.close(); // Not stored, no purpose
1968 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1969 p.close();
1970 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1971 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1972 p.close();
1973 iter.setPath(p);
1974 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1975 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1976 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1977 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1978 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1979 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1980 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1981 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1982 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1983 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1984 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1985 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1986 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1987 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1988 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1989 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1990 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1991 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1992 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1993 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1994 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1995 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1996
1997 // Generate random paths and verify
1998 SkPoint randomPts[25];
1999 for (int i = 0; i < 5; ++i) {
2000 for (int j = 0; j < 5; ++j) {
2001 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
2002 }
2003 }
2004
2005 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00002006 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002007 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00002008 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002009 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00002010
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002011 for (int i = 0; i < 500; ++i) {
2012 p.reset();
2013 bool lastWasClose = true;
2014 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00002015 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002016 int numPoints = 0;
2017 int numVerbs = (rand.nextU() >> 16) % 10;
2018 int numIterVerbs = 0;
2019 for (int j = 0; j < numVerbs; ++j) {
2020 do {
2021 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
2022 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002023 switch (nextVerb) {
2024 case SkPath::kMove_Verb:
2025 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2026 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00002027 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002028 numPoints += 1;
2029 lastWasClose = false;
2030 haveMoveTo = true;
2031 break;
2032 case SkPath::kLine_Verb:
2033 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002034 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002035 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2036 haveMoveTo = true;
2037 }
2038 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2039 p.lineTo(expectedPts[numPoints]);
2040 numPoints += 1;
2041 lastWasClose = false;
2042 break;
2043 case SkPath::kQuad_Verb:
2044 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002045 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002046 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2047 haveMoveTo = true;
2048 }
2049 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2050 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2051 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
2052 numPoints += 2;
2053 lastWasClose = false;
2054 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002055 case SkPath::kConic_Verb:
2056 if (!haveMoveTo) {
2057 expectedPts[numPoints++] = lastMoveToPt;
2058 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2059 haveMoveTo = true;
2060 }
2061 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2062 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2063 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2064 rand.nextUScalar1() * 4);
2065 numPoints += 2;
2066 lastWasClose = false;
2067 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002068 case SkPath::kCubic_Verb:
2069 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002070 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002071 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2072 haveMoveTo = true;
2073 }
2074 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2075 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2076 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2077 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2078 expectedPts[numPoints + 2]);
2079 numPoints += 3;
2080 lastWasClose = false;
2081 break;
2082 case SkPath::kClose_Verb:
2083 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002084 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002085 lastWasClose = true;
2086 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002087 default:
2088 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002089 }
2090 expectedVerbs[numIterVerbs++] = nextVerb;
2091 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002092
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002093 iter.setPath(p);
2094 numVerbs = numIterVerbs;
2095 numIterVerbs = 0;
2096 int numIterPts = 0;
2097 SkPoint lastMoveTo;
2098 SkPoint lastPt;
2099 lastMoveTo.set(0, 0);
2100 lastPt.set(0, 0);
2101 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2102 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2103 numIterVerbs++;
2104 switch (nextVerb) {
2105 case SkPath::kMove_Verb:
2106 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2107 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2108 lastPt = lastMoveTo = pts[0];
2109 numIterPts += 1;
2110 break;
2111 case SkPath::kLine_Verb:
2112 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2113 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2114 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2115 lastPt = pts[1];
2116 numIterPts += 1;
2117 break;
2118 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002119 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002120 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2121 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2122 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2123 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2124 lastPt = pts[2];
2125 numIterPts += 2;
2126 break;
2127 case SkPath::kCubic_Verb:
2128 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2129 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2130 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2131 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2132 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2133 lastPt = pts[3];
2134 numIterPts += 3;
2135 break;
2136 case SkPath::kClose_Verb:
2137 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2138 lastPt = lastMoveTo;
2139 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002140 default:
2141 SkASSERT(!"unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002142 }
2143 }
2144 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2145 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2146 }
2147}
2148
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002149static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002150 const SkPath& path,
2151 bool expectedCircle,
2152 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002153 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002154 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2155 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002156
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002157 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002158 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2159 }
2160}
2161
2162static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002163 const SkPath& path,
2164 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002165 SkPath tmp;
2166
2167 SkMatrix m;
2168 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2169 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002170 // this matrix reverses the direction.
2171 if (SkPath::kCCW_Direction == dir) {
2172 dir = SkPath::kCW_Direction;
2173 } else {
2174 SkASSERT(SkPath::kCW_Direction == dir);
2175 dir = SkPath::kCCW_Direction;
2176 }
2177 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002178}
2179
2180static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002181 const SkPath& path,
2182 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002183 SkPath tmp;
2184
2185 // translate at small offset
2186 SkMatrix m;
2187 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2188 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002189 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002190
2191 tmp.reset();
2192 m.reset();
2193
2194 // translate at a relatively big offset
2195 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2196 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002197 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002198}
2199
2200static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002201 const SkPath& path,
2202 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002203 for (int angle = 0; angle < 360; ++angle) {
2204 SkPath tmp;
2205 SkMatrix m;
2206 m.setRotate(SkIntToScalar(angle));
2207 path.transform(m, &tmp);
2208
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002209 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002210 // degrees is not an oval anymore, this can be improved. we made this
2211 // for the simplicity of our implementation.
2212 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002213 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002214 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002215 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002216 }
2217 }
2218}
2219
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002220static void test_circle_mirror_x(skiatest::Reporter* reporter,
2221 const SkPath& path,
2222 SkPath::Direction dir) {
2223 SkPath tmp;
2224 SkMatrix m;
2225 m.reset();
2226 m.setScaleX(-SK_Scalar1);
2227 path.transform(m, &tmp);
2228
2229 if (SkPath::kCW_Direction == dir) {
2230 dir = SkPath::kCCW_Direction;
2231 } else {
2232 SkASSERT(SkPath::kCCW_Direction == dir);
2233 dir = SkPath::kCW_Direction;
2234 }
2235
2236 check_for_circle(reporter, tmp, true, dir);
2237}
2238
2239static void test_circle_mirror_y(skiatest::Reporter* reporter,
2240 const SkPath& path,
2241 SkPath::Direction dir) {
2242 SkPath tmp;
2243 SkMatrix m;
2244 m.reset();
2245 m.setScaleY(-SK_Scalar1);
2246 path.transform(m, &tmp);
2247
2248 if (SkPath::kCW_Direction == dir) {
2249 dir = SkPath::kCCW_Direction;
2250 } else {
2251 SkASSERT(SkPath::kCCW_Direction == dir);
2252 dir = SkPath::kCW_Direction;
2253 }
2254
2255 check_for_circle(reporter, tmp, true, dir);
2256}
2257
2258static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2259 const SkPath& path,
2260 SkPath::Direction dir) {
2261 SkPath tmp;
2262 SkMatrix m;
2263 m.reset();
2264 m.setScaleX(-SK_Scalar1);
2265 m.setScaleY(-SK_Scalar1);
2266 path.transform(m, &tmp);
2267
2268 check_for_circle(reporter, tmp, true, dir);
2269}
2270
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002271static void test_circle_with_direction(skiatest::Reporter* reporter,
2272 SkPath::Direction dir) {
2273 SkPath path;
2274
2275 // circle at origin
2276 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002277 check_for_circle(reporter, path, true, dir);
2278 test_circle_rotate(reporter, path, dir);
2279 test_circle_translate(reporter, path, dir);
2280 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002281
2282 // circle at an offset at (10, 10)
2283 path.reset();
2284 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2285 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002286 check_for_circle(reporter, path, true, dir);
2287 test_circle_rotate(reporter, path, dir);
2288 test_circle_translate(reporter, path, dir);
2289 test_circle_skew(reporter, path, dir);
2290 test_circle_mirror_x(reporter, path, dir);
2291 test_circle_mirror_y(reporter, path, dir);
2292 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002293}
2294
2295static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2296 SkPath path;
2297 SkPath circle;
2298 SkPath rect;
2299 SkPath empty;
2300
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002301 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2302 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2303
2304 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002305 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2306 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2307
2308 SkMatrix translate;
2309 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2310
2311 // For simplicity, all the path concatenation related operations
2312 // would mark it non-circle, though in theory it's still a circle.
2313
2314 // empty + circle (translate)
2315 path = empty;
2316 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002317 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002318
2319 // circle + empty (translate)
2320 path = circle;
2321 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002322 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002323
2324 // test reverseAddPath
2325 path = circle;
2326 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002327 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002328}
2329
2330static void test_circle(skiatest::Reporter* reporter) {
2331 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2332 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2333
2334 // multiple addCircle()
2335 SkPath path;
2336 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2337 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002338 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002339
2340 // some extra lineTo() would make isOval() fail
2341 path.reset();
2342 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2343 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002344 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002345
2346 // not back to the original point
2347 path.reset();
2348 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2349 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002350 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002351
2352 test_circle_with_add_paths(reporter);
2353}
2354
2355static void test_oval(skiatest::Reporter* reporter) {
2356 SkRect rect;
2357 SkMatrix m;
2358 SkPath path;
2359
2360 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2361 path.addOval(rect);
2362
2363 REPORTER_ASSERT(reporter, path.isOval(NULL));
2364
2365 m.setRotate(SkIntToScalar(90));
2366 SkPath tmp;
2367 path.transform(m, &tmp);
2368 // an oval rotated 90 degrees is still an oval.
2369 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2370
2371 m.reset();
2372 m.setRotate(SkIntToScalar(30));
2373 tmp.reset();
2374 path.transform(m, &tmp);
2375 // an oval rotated 30 degrees is not an oval anymore.
2376 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2377
2378 // since empty path being transformed.
2379 path.reset();
2380 tmp.reset();
2381 m.reset();
2382 path.transform(m, &tmp);
2383 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2384
2385 // empty path is not an oval
2386 tmp.reset();
2387 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2388
2389 // only has moveTo()s
2390 tmp.reset();
2391 tmp.moveTo(0, 0);
2392 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2393 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2394
2395 // mimic WebKit's calling convention,
2396 // call moveTo() first and then call addOval()
2397 path.reset();
2398 path.moveTo(0, 0);
2399 path.addOval(rect);
2400 REPORTER_ASSERT(reporter, path.isOval(NULL));
2401
2402 // copy path
2403 path.reset();
2404 tmp.reset();
2405 tmp.addOval(rect);
2406 path = tmp;
2407 REPORTER_ASSERT(reporter, path.isOval(NULL));
2408}
2409
bungeman@google.coma5809a32013-06-21 15:13:34 +00002410static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2411 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002412
reed@android.com3abec1d2009-03-02 05:36:20 +00002413 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002414 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002415 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002416 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002417 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002418 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2419 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002420 REPORTER_ASSERT(reporter, p == empty);
2421 REPORTER_ASSERT(reporter, !(p != empty));
2422}
2423
2424static void TestPath(skiatest::Reporter* reporter) {
2425 SkTSize<SkScalar>::Make(3,4);
2426
2427 SkPath p, empty;
2428 SkRect bounds, bounds2;
2429 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002430
reed@android.comd252db02009-04-01 18:31:44 +00002431 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002432
reed@android.com3abec1d2009-03-02 05:36:20 +00002433 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002434
reed@android.com6b82d1a2009-06-03 02:35:01 +00002435 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2436 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002437 // we have quads or cubics
2438 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002439 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002440
reed@android.com6b82d1a2009-06-03 02:35:01 +00002441 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002442 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002443
reed@android.com6b82d1a2009-06-03 02:35:01 +00002444 p.addOval(bounds);
2445 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002446 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002447
bungeman@google.coma5809a32013-06-21 15:13:34 +00002448 p.rewind();
2449 test_empty(reporter, p);
2450
reed@android.com3abec1d2009-03-02 05:36:20 +00002451 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002452 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002453 // we have only lines
2454 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002455 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002456
bungeman@google.coma5809a32013-06-21 15:13:34 +00002457 REPORTER_ASSERT(reporter, p != empty);
2458 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002459
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002460 // do getPoints and getVerbs return the right result
2461 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2462 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002463 SkPoint pts[4];
2464 int count = p.getPoints(pts, 4);
2465 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002466 uint8_t verbs[6];
2467 verbs[5] = 0xff;
2468 p.getVerbs(verbs, 5);
2469 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2470 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2471 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2472 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2473 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2474 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002475 bounds2.set(pts, 4);
2476 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002477
reed@android.com3abec1d2009-03-02 05:36:20 +00002478 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2479 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002480 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002481
reed@android.com3abec1d2009-03-02 05:36:20 +00002482 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002483 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002484 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2485 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002486
reed@android.com3abec1d2009-03-02 05:36:20 +00002487 // now force p to not be a rect
2488 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2489 p.addRect(bounds);
2490 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002491
reed@google.com7e6c4d12012-05-10 14:05:43 +00002492 test_isLine(reporter);
2493 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002494 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002495 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002496 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002497 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002498 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002499 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002500 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002501 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002502 test_flattening(reporter);
2503 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002504 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002505 test_iter(reporter);
2506 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002507 test_circle(reporter);
2508 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002509 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002510 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002511 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002512 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002513 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002514 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002515 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002516 test_tricky_cubic();
2517 test_clipped_cubic();
2518 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002519 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002520 test_bad_cubic_crbug234190();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +00002521 test_android_specific_behavior(reporter);
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +00002522 test_path_close_issue1474(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002523}
2524
2525#include "TestClassDef.h"
2526DEFINE_TESTCLASS("Path", PathTestClass, TestPath)