1
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 */
8 #include "Test.h"
9 #include "SkCanvas.h"
10 #include "SkPaint.h"
11 #include "SkPath.h"
12 #include "SkParse.h"
13 #include "SkParsePath.h"
14 #include "SkPathEffect.h"
15 #include "SkRandom.h"
16 #include "SkReader32.h"
17 #include "SkSize.h"
18 #include "SkWriter32.h"
19 #include "SkSurface.h"
20
21 #if defined(WIN32)
22 #define SUPPRESS_VISIBILITY_WARNING
23 #else
24 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
25 #endif
26
new_surface(int w,int h)27 static SkSurface* new_surface(int w, int h) {
28 SkImage::Info info = {
29 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
30 };
31 return SkSurface::NewRaster(info);
32 }
33
build_path_170666(SkPath & path)34 static void build_path_170666(SkPath& path) {
35 path.moveTo(17.9459f, 21.6344f);
36 path.lineTo(139.545f, -47.8105f);
37 path.lineTo(139.545f, -47.8105f);
38 path.lineTo(131.07f, -47.3888f);
39 path.lineTo(131.07f, -47.3888f);
40 path.lineTo(122.586f, -46.9532f);
41 path.lineTo(122.586f, -46.9532f);
42 path.lineTo(18076.6f, 31390.9f);
43 path.lineTo(18076.6f, 31390.9f);
44 path.lineTo(18085.1f, 31390.5f);
45 path.lineTo(18085.1f, 31390.5f);
46 path.lineTo(18076.6f, 31390.9f);
47 path.lineTo(18076.6f, 31390.9f);
48 path.lineTo(17955, 31460.3f);
49 path.lineTo(17955, 31460.3f);
50 path.lineTo(17963.5f, 31459.9f);
51 path.lineTo(17963.5f, 31459.9f);
52 path.lineTo(17971.9f, 31459.5f);
53 path.lineTo(17971.9f, 31459.5f);
54 path.lineTo(17.9551f, 21.6205f);
55 path.lineTo(17.9551f, 21.6205f);
56 path.lineTo(9.47091f, 22.0561f);
57 path.lineTo(9.47091f, 22.0561f);
58 path.lineTo(17.9459f, 21.6344f);
59 path.lineTo(17.9459f, 21.6344f);
60 path.close();path.moveTo(0.995934f, 22.4779f);
61 path.lineTo(0.986725f, 22.4918f);
62 path.lineTo(0.986725f, 22.4918f);
63 path.lineTo(17955, 31460.4f);
64 path.lineTo(17955, 31460.4f);
65 path.lineTo(17971.9f, 31459.5f);
66 path.lineTo(17971.9f, 31459.5f);
67 path.lineTo(18093.6f, 31390.1f);
68 path.lineTo(18093.6f, 31390.1f);
69 path.lineTo(18093.6f, 31390);
70 path.lineTo(18093.6f, 31390);
71 path.lineTo(139.555f, -47.8244f);
72 path.lineTo(139.555f, -47.8244f);
73 path.lineTo(122.595f, -46.9671f);
74 path.lineTo(122.595f, -46.9671f);
75 path.lineTo(0.995934f, 22.4779f);
76 path.lineTo(0.995934f, 22.4779f);
77 path.close();
78 path.moveTo(5.43941f, 25.5223f);
79 path.lineTo(798267, -28871.1f);
80 path.lineTo(798267, -28871.1f);
81 path.lineTo(3.12512e+06f, -113102);
82 path.lineTo(3.12512e+06f, -113102);
83 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
84 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
85 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
86 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
87 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
88 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
89 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
90 path.lineTo(2.78271e+08f, -1.00733e+07f);
91 path.lineTo(2.78271e+08f, -1.00733e+07f);
92 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
93 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
94 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
95 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
96 path.lineTo(2.77473e+08f, -1.00444e+07f);
97 path.lineTo(2.77473e+08f, -1.00444e+07f);
98 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
99 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
100 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
101 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
102 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
103 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
104 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
105 path.lineTo(798284, -28872);
106 path.lineTo(798284, -28872);
107 path.lineTo(22.4044f, 24.6677f);
108 path.lineTo(22.4044f, 24.6677f);
109 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
110 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
111 path.close();
112 }
113
build_path_simple_170666(SkPath & path)114 static void build_path_simple_170666(SkPath& path) {
115 path.moveTo(126.677f, 24.1591f);
116 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
117 }
118
119 // This used to assert in the SK_DEBUG build, as the clip step would fail with
120 // too-few interations in our cubic-line intersection code. That code now runs
121 // 24 interations (instead of 16).
test_crbug_170666(skiatest::Reporter * reporter)122 static void test_crbug_170666(skiatest::Reporter* reporter) {
123 SkPath path;
124 SkPaint paint;
125 paint.setAntiAlias(true);
126
127 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
128
129 build_path_simple_170666(path);
130 surface->getCanvas()->drawPath(path, paint);
131
132 build_path_170666(path);
133 surface->getCanvas()->drawPath(path, paint);
134 }
135
136 // Make sure we stay non-finite once we get there (unless we reset or rewind).
test_addrect_isfinite(skiatest::Reporter * reporter)137 static void test_addrect_isfinite(skiatest::Reporter* reporter) {
138 SkPath path;
139
140 path.addRect(SkRect::MakeWH(50, 100));
141 REPORTER_ASSERT(reporter, path.isFinite());
142
143 path.moveTo(0, 0);
144 path.lineTo(SK_ScalarInfinity, 42);
145 REPORTER_ASSERT(reporter, !path.isFinite());
146
147 path.addRect(SkRect::MakeWH(50, 100));
148 REPORTER_ASSERT(reporter, !path.isFinite());
149
150 path.reset();
151 REPORTER_ASSERT(reporter, path.isFinite());
152
153 path.addRect(SkRect::MakeWH(50, 100));
154 REPORTER_ASSERT(reporter, path.isFinite());
155 }
156
build_big_path(SkPath * path,bool reducedCase)157 static void build_big_path(SkPath* path, bool reducedCase) {
158 if (reducedCase) {
159 path->moveTo(577330, 1971.72f);
160 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
161 } else {
162 path->moveTo(60.1631f, 7.70567f);
163 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
164 path->lineTo(577379, 1977.77f);
165 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
166 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
167 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
168 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
169 path->quadTo(577198, 1972, 577238, 1971.31f);
170 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
171 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
172 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
173 path->moveTo(306.718f, -32.912f);
174 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
175 }
176 }
177
test_clipped_cubic(skiatest::Reporter * reporter)178 static void test_clipped_cubic(skiatest::Reporter* reporter) {
179 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
180
181 // This path used to assert, because our cubic-chopping code incorrectly
182 // moved control points after the chop. This test should be run in SK_DEBUG
183 // mode to ensure that we no long assert.
184 SkPath path;
185 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
186 build_big_path(&path, SkToBool(doReducedCase));
187
188 SkPaint paint;
189 for (int doAA = 0; doAA <= 1; ++doAA) {
190 paint.setAntiAlias(SkToBool(doAA));
191 surface->getCanvas()->drawPath(path, paint);
192 }
193 }
194 }
195
196 // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
197 // which triggered an assert, from a tricky cubic. This test replicates that
198 // example, so we can ensure that we handle it (in SkEdge.cpp), and don't
199 // assert in the SK_DEBUG build.
test_tricky_cubic(skiatest::Reporter * reporter)200 static void test_tricky_cubic(skiatest::Reporter* reporter) {
201 const SkPoint pts[] = {
202 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
203 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
204 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
205 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
206 };
207
208 SkPath path;
209 path.moveTo(pts[0]);
210 path.cubicTo(pts[1], pts[2], pts[3]);
211
212 SkPaint paint;
213 paint.setAntiAlias(true);
214
215 SkSurface* surface = new_surface(19, 130);
216 surface->getCanvas()->drawPath(path, paint);
217 surface->unref();
218 }
219
220 // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
221 //
test_isfinite_after_transform(skiatest::Reporter * reporter)222 static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
223 SkPath path;
224 path.quadTo(157, 366, 286, 208);
225 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
226
227 SkMatrix matrix;
228 matrix.setScale(1000*1000, 1000*1000);
229
230 // Be sure that path::transform correctly updates isFinite and the bounds
231 // if the transformation overflows. The previous bug was that isFinite was
232 // set to true in this case, but the bounds were not set to empty (which
233 // they should be).
234 while (path.isFinite()) {
235 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
236 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
237 path.transform(matrix);
238 }
239 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
240
241 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
242 path.transform(matrix);
243 // we need to still be non-finite
244 REPORTER_ASSERT(reporter, !path.isFinite());
245 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
246 }
247
add_corner_arc(SkPath * path,const SkRect & rect,SkScalar xIn,SkScalar yIn,int startAngle)248 static void add_corner_arc(SkPath* path, const SkRect& rect,
249 SkScalar xIn, SkScalar yIn,
250 int startAngle)
251 {
252
253 SkScalar rx = SkMinScalar(rect.width(), xIn);
254 SkScalar ry = SkMinScalar(rect.height(), yIn);
255
256 SkRect arcRect;
257 arcRect.set(-rx, -ry, rx, ry);
258 switch (startAngle) {
259 case 0:
260 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
261 break;
262 case 90:
263 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
264 break;
265 case 180:
266 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
267 break;
268 case 270:
269 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
270 break;
271 default:
272 break;
273 }
274
275 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
276 }
277
make_arb_round_rect(SkPath * path,const SkRect & r,SkScalar xCorner,SkScalar yCorner)278 static void make_arb_round_rect(SkPath* path, const SkRect& r,
279 SkScalar xCorner, SkScalar yCorner) {
280 // we are lazy here and use the same x & y for each corner
281 add_corner_arc(path, r, xCorner, yCorner, 270);
282 add_corner_arc(path, r, xCorner, yCorner, 0);
283 add_corner_arc(path, r, xCorner, yCorner, 90);
284 add_corner_arc(path, r, xCorner, yCorner, 180);
285 path->close();
286 }
287
288 // Chrome creates its own round rects with each corner possibly being different.
289 // Performance will suffer if they are not convex.
290 // Note: PathBench::ArbRoundRectBench performs almost exactly
291 // the same test (but with drawing)
test_arb_round_rect_is_convex(skiatest::Reporter * reporter)292 static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
293 SkRandom rand;
294 SkRect r;
295
296 for (int i = 0; i < 5000; ++i) {
297
298 SkScalar size = rand.nextUScalar1() * 30;
299 if (size < SK_Scalar1) {
300 continue;
301 }
302 r.fLeft = rand.nextUScalar1() * 300;
303 r.fTop = rand.nextUScalar1() * 300;
304 r.fRight = r.fLeft + 2 * size;
305 r.fBottom = r.fTop + 2 * size;
306
307 SkPath temp;
308
309 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
310
311 #ifndef SK_IGNORE_CONVEX_QUAD_OPT
312 REPORTER_ASSERT(reporter, temp.isConvex());
313 #endif
314 }
315 }
316
317 // Chrome will sometimes create a 0 radius round rect. The degenerate
318 // quads prevent the path from being converted to a rect
319 // Note: PathBench::ArbRoundRectBench performs almost exactly
320 // the same test (but with drawing)
test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter * reporter)321 static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
322 SkRandom rand;
323 SkRect r;
324
325 for (int i = 0; i < 5000; ++i) {
326
327 SkScalar size = rand.nextUScalar1() * 30;
328 if (size < SK_Scalar1) {
329 continue;
330 }
331 r.fLeft = rand.nextUScalar1() * 300;
332 r.fTop = rand.nextUScalar1() * 300;
333 r.fRight = r.fLeft + 2 * size;
334 r.fBottom = r.fTop + 2 * size;
335
336 SkPath temp;
337
338 make_arb_round_rect(&temp, r, 0, 0);
339
340 #ifndef SK_IGNORE_CONVEX_QUAD_OPT
341 SkRect result;
342 REPORTER_ASSERT(reporter, temp.isRect(&result));
343 REPORTER_ASSERT(reporter, r == result);
344 #endif
345 }
346 }
347
test_rect_isfinite(skiatest::Reporter * reporter)348 static void test_rect_isfinite(skiatest::Reporter* reporter) {
349 const SkScalar inf = SK_ScalarInfinity;
350 const SkScalar nan = SK_ScalarNaN;
351
352 SkRect r;
353 r.setEmpty();
354 REPORTER_ASSERT(reporter, r.isFinite());
355 r.set(0, 0, inf, -inf);
356 REPORTER_ASSERT(reporter, !r.isFinite());
357 r.set(0, 0, nan, 0);
358 REPORTER_ASSERT(reporter, !r.isFinite());
359
360 SkPoint pts[] = {
361 { 0, 0 },
362 { SK_Scalar1, 0 },
363 { 0, SK_Scalar1 },
364 };
365
366 bool isFine = r.setBoundsCheck(pts, 3);
367 REPORTER_ASSERT(reporter, isFine);
368 REPORTER_ASSERT(reporter, !r.isEmpty());
369
370 pts[1].set(inf, 0);
371 isFine = r.setBoundsCheck(pts, 3);
372 REPORTER_ASSERT(reporter, !isFine);
373 REPORTER_ASSERT(reporter, r.isEmpty());
374
375 pts[1].set(nan, 0);
376 isFine = r.setBoundsCheck(pts, 3);
377 REPORTER_ASSERT(reporter, !isFine);
378 REPORTER_ASSERT(reporter, r.isEmpty());
379 }
380
test_path_isfinite(skiatest::Reporter * reporter)381 static void test_path_isfinite(skiatest::Reporter* reporter) {
382 const SkScalar inf = SK_ScalarInfinity;
383 const SkScalar negInf = SK_ScalarNegativeInfinity;
384 const SkScalar nan = SK_ScalarNaN;
385
386 SkPath path;
387 REPORTER_ASSERT(reporter, path.isFinite());
388
389 path.reset();
390 REPORTER_ASSERT(reporter, path.isFinite());
391
392 path.reset();
393 path.moveTo(SK_Scalar1, 0);
394 REPORTER_ASSERT(reporter, path.isFinite());
395
396 path.reset();
397 path.moveTo(inf, negInf);
398 REPORTER_ASSERT(reporter, !path.isFinite());
399
400 path.reset();
401 path.moveTo(nan, 0);
402 REPORTER_ASSERT(reporter, !path.isFinite());
403 }
404
test_isfinite(skiatest::Reporter * reporter)405 static void test_isfinite(skiatest::Reporter* reporter) {
406 test_rect_isfinite(reporter);
407 test_path_isfinite(reporter);
408 }
409
410 // assert that we always
411 // start with a moveTo
412 // only have 1 moveTo
413 // only have Lines after that
414 // end with a single close
415 // only have (at most) 1 close
416 //
test_poly(skiatest::Reporter * reporter,const SkPath & path,const SkPoint srcPts[],int count,bool expectClose)417 static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
418 const SkPoint srcPts[], int count, bool expectClose) {
419 SkPath::RawIter iter(path);
420 SkPoint pts[4];
421
422 bool firstTime = true;
423 bool foundClose = false;
424 for (;;) {
425 switch (iter.next(pts)) {
426 case SkPath::kMove_Verb:
427 REPORTER_ASSERT(reporter, firstTime);
428 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
429 srcPts++;
430 firstTime = false;
431 break;
432 case SkPath::kLine_Verb:
433 REPORTER_ASSERT(reporter, !firstTime);
434 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
435 srcPts++;
436 break;
437 case SkPath::kQuad_Verb:
438 REPORTER_ASSERT(reporter, !"unexpected quad verb");
439 break;
440 case SkPath::kCubic_Verb:
441 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
442 break;
443 case SkPath::kClose_Verb:
444 REPORTER_ASSERT(reporter, !firstTime);
445 REPORTER_ASSERT(reporter, !foundClose);
446 REPORTER_ASSERT(reporter, expectClose);
447 foundClose = true;
448 break;
449 case SkPath::kDone_Verb:
450 goto DONE;
451 }
452 }
453 DONE:
454 REPORTER_ASSERT(reporter, foundClose == expectClose);
455 }
456
test_addPoly(skiatest::Reporter * reporter)457 static void test_addPoly(skiatest::Reporter* reporter) {
458 SkPoint pts[32];
459 SkRandom rand;
460
461 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
462 pts[i].fX = rand.nextSScalar1();
463 pts[i].fY = rand.nextSScalar1();
464 }
465
466 for (int doClose = 0; doClose <= 1; ++doClose) {
467 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
468 SkPath path;
469 path.addPoly(pts, count, SkToBool(doClose));
470 test_poly(reporter, path, pts, count, SkToBool(doClose));
471 }
472 }
473 }
474
test_strokerec(skiatest::Reporter * reporter)475 static void test_strokerec(skiatest::Reporter* reporter) {
476 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
477 REPORTER_ASSERT(reporter, rec.isFillStyle());
478
479 rec.setHairlineStyle();
480 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
481
482 rec.setStrokeStyle(SK_Scalar1, false);
483 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
484
485 rec.setStrokeStyle(SK_Scalar1, true);
486 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
487
488 rec.setStrokeStyle(0, false);
489 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
490
491 rec.setStrokeStyle(0, true);
492 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
493 }
494
495 // Set this for paths that don't have a consistent direction such as a bowtie.
496 // (cheapComputeDirection is not expected to catch these.)
497 static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
498
check_direction(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction expected)499 static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
500 SkPath::Direction expected) {
501 if (expected == kDontCheckDir) {
502 return;
503 }
504 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
505
506 SkPath::Direction dir;
507 if (copy.cheapComputeDirection(&dir)) {
508 REPORTER_ASSERT(reporter, dir == expected);
509 } else {
510 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
511 }
512 }
513
test_direction(skiatest::Reporter * reporter)514 static void test_direction(skiatest::Reporter* reporter) {
515 size_t i;
516 SkPath path;
517 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
518 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
519 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
520 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
521
522 static const char* gDegen[] = {
523 "M 10 10",
524 "M 10 10 M 20 20",
525 "M 10 10 L 20 20",
526 "M 10 10 L 10 10 L 10 10",
527 "M 10 10 Q 10 10 10 10",
528 "M 10 10 C 10 10 10 10 10 10",
529 };
530 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
531 path.reset();
532 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
533 REPORTER_ASSERT(reporter, valid);
534 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
535 }
536
537 static const char* gCW[] = {
538 "M 10 10 L 10 10 Q 20 10 20 20",
539 "M 10 10 C 20 10 20 20 20 20",
540 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
541 // rect with top two corners replaced by cubics with identical middle
542 // control points
543 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
544 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
545 };
546 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
547 path.reset();
548 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
549 REPORTER_ASSERT(reporter, valid);
550 check_direction(reporter, path, SkPath::kCW_Direction);
551 }
552
553 static const char* gCCW[] = {
554 "M 10 10 L 10 10 Q 20 10 20 -20",
555 "M 10 10 C 20 10 20 -20 20 -20",
556 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
557 // rect with top two corners replaced by cubics with identical middle
558 // control points
559 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
560 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
561 };
562 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
563 path.reset();
564 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
565 REPORTER_ASSERT(reporter, valid);
566 check_direction(reporter, path, SkPath::kCCW_Direction);
567 }
568
569 // Test two donuts, each wound a different direction. Only the outer contour
570 // determines the cheap direction
571 path.reset();
572 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
573 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
574 check_direction(reporter, path, SkPath::kCW_Direction);
575
576 path.reset();
577 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
578 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
579 check_direction(reporter, path, SkPath::kCCW_Direction);
580
581 #ifdef SK_SCALAR_IS_FLOAT
582 // triangle with one point really far from the origin.
583 path.reset();
584 // the first point is roughly 1.05e10, 1.05e10
585 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
586 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
587 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
588 check_direction(reporter, path, SkPath::kCCW_Direction);
589 #endif
590 }
591
add_rect(SkPath * path,const SkRect & r)592 static void add_rect(SkPath* path, const SkRect& r) {
593 path->moveTo(r.fLeft, r.fTop);
594 path->lineTo(r.fRight, r.fTop);
595 path->lineTo(r.fRight, r.fBottom);
596 path->lineTo(r.fLeft, r.fBottom);
597 path->close();
598 }
599
test_bounds(skiatest::Reporter * reporter)600 static void test_bounds(skiatest::Reporter* reporter) {
601 static const SkRect rects[] = {
602 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
603 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
604 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
605 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
606 };
607
608 SkPath path0, path1;
609 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
610 path0.addRect(rects[i]);
611 add_rect(&path1, rects[i]);
612 }
613
614 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
615 }
616
stroke_cubic(const SkPoint pts[4])617 static void stroke_cubic(const SkPoint pts[4]) {
618 SkPath path;
619 path.moveTo(pts[0]);
620 path.cubicTo(pts[1], pts[2], pts[3]);
621
622 SkPaint paint;
623 paint.setStyle(SkPaint::kStroke_Style);
624 paint.setStrokeWidth(SK_Scalar1 * 2);
625
626 SkPath fill;
627 paint.getFillPath(path, &fill);
628 }
629
630 // just ensure this can run w/o any SkASSERTS firing in the debug build
631 // we used to assert due to differences in how we determine a degenerate vector
632 // but that was fixed with the introduction of SkPoint::CanNormalize
stroke_tiny_cubic()633 static void stroke_tiny_cubic() {
634 SkPoint p0[] = {
635 { 372.0f, 92.0f },
636 { 372.0f, 92.0f },
637 { 372.0f, 92.0f },
638 { 372.0f, 92.0f },
639 };
640
641 stroke_cubic(p0);
642
643 SkPoint p1[] = {
644 { 372.0f, 92.0f },
645 { 372.0007f, 92.000755f },
646 { 371.99927f, 92.003922f },
647 { 371.99826f, 92.003899f },
648 };
649
650 stroke_cubic(p1);
651 }
652
check_close(skiatest::Reporter * reporter,const SkPath & path)653 static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
654 for (int i = 0; i < 2; ++i) {
655 SkPath::Iter iter(path, SkToBool(i));
656 SkPoint mv;
657 SkPoint pts[4];
658 SkPath::Verb v;
659 int nMT = 0;
660 int nCL = 0;
661 mv.set(0, 0);
662 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
663 switch (v) {
664 case SkPath::kMove_Verb:
665 mv = pts[0];
666 ++nMT;
667 break;
668 case SkPath::kClose_Verb:
669 REPORTER_ASSERT(reporter, mv == pts[0]);
670 ++nCL;
671 break;
672 default:
673 break;
674 }
675 }
676 // if we force a close on the interator we should have a close
677 // for every moveTo
678 REPORTER_ASSERT(reporter, !i || nMT == nCL);
679 }
680 }
681
test_close(skiatest::Reporter * reporter)682 static void test_close(skiatest::Reporter* reporter) {
683 SkPath closePt;
684 closePt.moveTo(0, 0);
685 closePt.close();
686 check_close(reporter, closePt);
687
688 SkPath openPt;
689 openPt.moveTo(0, 0);
690 check_close(reporter, openPt);
691
692 SkPath empty;
693 check_close(reporter, empty);
694 empty.close();
695 check_close(reporter, empty);
696
697 SkPath rect;
698 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
699 check_close(reporter, rect);
700 rect.close();
701 check_close(reporter, rect);
702
703 SkPath quad;
704 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
705 check_close(reporter, quad);
706 quad.close();
707 check_close(reporter, quad);
708
709 SkPath cubic;
710 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
711 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
712 check_close(reporter, cubic);
713 cubic.close();
714 check_close(reporter, cubic);
715
716 SkPath line;
717 line.moveTo(SK_Scalar1, SK_Scalar1);
718 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
719 check_close(reporter, line);
720 line.close();
721 check_close(reporter, line);
722
723 SkPath rect2;
724 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
725 rect2.close();
726 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
727 check_close(reporter, rect2);
728 rect2.close();
729 check_close(reporter, rect2);
730
731 SkPath oval3;
732 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
733 oval3.close();
734 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
735 check_close(reporter, oval3);
736 oval3.close();
737 check_close(reporter, oval3);
738
739 SkPath moves;
740 moves.moveTo(SK_Scalar1, SK_Scalar1);
741 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
742 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
743 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
744 check_close(reporter, moves);
745
746 stroke_tiny_cubic();
747 }
748
check_convexity(skiatest::Reporter * reporter,const SkPath & path,SkPath::Convexity expected)749 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
750 SkPath::Convexity expected) {
751 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
752 SkPath::Convexity c = copy.getConvexity();
753 REPORTER_ASSERT(reporter, c == expected);
754 }
755
test_convexity2(skiatest::Reporter * reporter)756 static void test_convexity2(skiatest::Reporter* reporter) {
757 SkPath pt;
758 pt.moveTo(0, 0);
759 pt.close();
760 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
761 check_direction(reporter, pt, SkPath::kUnknown_Direction);
762
763 SkPath line;
764 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
765 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
766 line.close();
767 check_convexity(reporter, line, SkPath::kConvex_Convexity);
768 check_direction(reporter, line, SkPath::kUnknown_Direction);
769
770 SkPath triLeft;
771 triLeft.moveTo(0, 0);
772 triLeft.lineTo(SK_Scalar1, 0);
773 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
774 triLeft.close();
775 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
776 check_direction(reporter, triLeft, SkPath::kCW_Direction);
777
778 SkPath triRight;
779 triRight.moveTo(0, 0);
780 triRight.lineTo(-SK_Scalar1, 0);
781 triRight.lineTo(SK_Scalar1, SK_Scalar1);
782 triRight.close();
783 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
784 check_direction(reporter, triRight, SkPath::kCCW_Direction);
785
786 SkPath square;
787 square.moveTo(0, 0);
788 square.lineTo(SK_Scalar1, 0);
789 square.lineTo(SK_Scalar1, SK_Scalar1);
790 square.lineTo(0, SK_Scalar1);
791 square.close();
792 check_convexity(reporter, square, SkPath::kConvex_Convexity);
793 check_direction(reporter, square, SkPath::kCW_Direction);
794
795 SkPath redundantSquare;
796 redundantSquare.moveTo(0, 0);
797 redundantSquare.lineTo(0, 0);
798 redundantSquare.lineTo(0, 0);
799 redundantSquare.lineTo(SK_Scalar1, 0);
800 redundantSquare.lineTo(SK_Scalar1, 0);
801 redundantSquare.lineTo(SK_Scalar1, 0);
802 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
803 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
804 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
805 redundantSquare.lineTo(0, SK_Scalar1);
806 redundantSquare.lineTo(0, SK_Scalar1);
807 redundantSquare.lineTo(0, SK_Scalar1);
808 redundantSquare.close();
809 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
810 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
811
812 SkPath bowTie;
813 bowTie.moveTo(0, 0);
814 bowTie.lineTo(0, 0);
815 bowTie.lineTo(0, 0);
816 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
817 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
818 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
819 bowTie.lineTo(SK_Scalar1, 0);
820 bowTie.lineTo(SK_Scalar1, 0);
821 bowTie.lineTo(SK_Scalar1, 0);
822 bowTie.lineTo(0, SK_Scalar1);
823 bowTie.lineTo(0, SK_Scalar1);
824 bowTie.lineTo(0, SK_Scalar1);
825 bowTie.close();
826 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
827 check_direction(reporter, bowTie, kDontCheckDir);
828
829 SkPath spiral;
830 spiral.moveTo(0, 0);
831 spiral.lineTo(100*SK_Scalar1, 0);
832 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
833 spiral.lineTo(0, 100*SK_Scalar1);
834 spiral.lineTo(0, 50*SK_Scalar1);
835 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
836 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
837 spiral.close();
838 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
839 check_direction(reporter, spiral, kDontCheckDir);
840
841 SkPath dent;
842 dent.moveTo(0, 0);
843 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
844 dent.lineTo(0, 100*SK_Scalar1);
845 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
846 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
847 dent.close();
848 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
849 check_direction(reporter, dent, SkPath::kCW_Direction);
850 }
851
check_convex_bounds(skiatest::Reporter * reporter,const SkPath & p,const SkRect & bounds)852 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
853 const SkRect& bounds) {
854 REPORTER_ASSERT(reporter, p.isConvex());
855 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
856
857 SkPath p2(p);
858 REPORTER_ASSERT(reporter, p2.isConvex());
859 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
860
861 SkPath other;
862 other.swap(p2);
863 REPORTER_ASSERT(reporter, other.isConvex());
864 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
865 }
866
setFromString(SkPath * path,const char str[])867 static void setFromString(SkPath* path, const char str[]) {
868 bool first = true;
869 while (str) {
870 SkScalar x, y;
871 str = SkParse::FindScalar(str, &x);
872 if (NULL == str) {
873 break;
874 }
875 str = SkParse::FindScalar(str, &y);
876 SkASSERT(str);
877 if (first) {
878 path->moveTo(x, y);
879 first = false;
880 } else {
881 path->lineTo(x, y);
882 }
883 }
884 }
885
test_convexity(skiatest::Reporter * reporter)886 static void test_convexity(skiatest::Reporter* reporter) {
887 SkPath path;
888
889 check_convexity(reporter, path, SkPath::kConvex_Convexity);
890 path.addCircle(0, 0, SkIntToScalar(10));
891 check_convexity(reporter, path, SkPath::kConvex_Convexity);
892 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
893 check_convexity(reporter, path, SkPath::kConcave_Convexity);
894
895 path.reset();
896 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
897 check_convexity(reporter, path, SkPath::kConvex_Convexity);
898 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
899
900 path.reset();
901 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
902 check_convexity(reporter, path, SkPath::kConvex_Convexity);
903 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
904
905 static const struct {
906 const char* fPathStr;
907 SkPath::Convexity fExpectedConvexity;
908 SkPath::Direction fExpectedDirection;
909 } gRec[] = {
910 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
911 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
912 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
913 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
914 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
915 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
916 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
917 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
918 };
919
920 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
921 SkPath path;
922 setFromString(&path, gRec[i].fPathStr);
923 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
924 check_direction(reporter, path, gRec[i].fExpectedDirection);
925 }
926 }
927
test_isLine(skiatest::Reporter * reporter)928 static void test_isLine(skiatest::Reporter* reporter) {
929 SkPath path;
930 SkPoint pts[2];
931 const SkScalar value = SkIntToScalar(5);
932
933 REPORTER_ASSERT(reporter, !path.isLine(NULL));
934
935 // set some non-zero values
936 pts[0].set(value, value);
937 pts[1].set(value, value);
938 REPORTER_ASSERT(reporter, !path.isLine(pts));
939 // check that pts was untouched
940 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
941 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
942
943 const SkScalar moveX = SkIntToScalar(1);
944 const SkScalar moveY = SkIntToScalar(2);
945 SkASSERT(value != moveX && value != moveY);
946
947 path.moveTo(moveX, moveY);
948 REPORTER_ASSERT(reporter, !path.isLine(NULL));
949 REPORTER_ASSERT(reporter, !path.isLine(pts));
950 // check that pts was untouched
951 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
952 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
953
954 const SkScalar lineX = SkIntToScalar(2);
955 const SkScalar lineY = SkIntToScalar(2);
956 SkASSERT(value != lineX && value != lineY);
957
958 path.lineTo(lineX, lineY);
959 REPORTER_ASSERT(reporter, path.isLine(NULL));
960
961 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
962 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
963 REPORTER_ASSERT(reporter, path.isLine(pts));
964 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
965 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
966
967 path.lineTo(0, 0); // too many points/verbs
968 REPORTER_ASSERT(reporter, !path.isLine(NULL));
969 REPORTER_ASSERT(reporter, !path.isLine(pts));
970 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
971 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
972 }
973
test_conservativelyContains(skiatest::Reporter * reporter)974 static void test_conservativelyContains(skiatest::Reporter* reporter) {
975 SkPath path;
976
977 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
978 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
979
980 // A circle that bounds kBaseRect (with a significant amount of slop)
981 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
982 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
983 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
984
985 // round-rect radii
986 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
987
988 static const struct SUPPRESS_VISIBILITY_WARNING {
989 SkRect fQueryRect;
990 bool fInRect;
991 bool fInCircle;
992 bool fInRR;
993 } kQueries[] = {
994 {kBaseRect, true, true, false},
995
996 // rect well inside of kBaseRect
997 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
998 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
999 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1000 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1001 true, true, true},
1002
1003 // rects with edges off by one from kBaseRect's edges
1004 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1005 kBaseRect.width(), kBaseRect.height() + 1),
1006 false, true, false},
1007 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1008 kBaseRect.width() + 1, kBaseRect.height()),
1009 false, true, false},
1010 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1011 kBaseRect.width() + 1, kBaseRect.height() + 1),
1012 false, true, false},
1013 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1014 kBaseRect.width(), kBaseRect.height()),
1015 false, true, false},
1016 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1017 kBaseRect.width(), kBaseRect.height()),
1018 false, true, false},
1019 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1020 kBaseRect.width() + 2, kBaseRect.height()),
1021 false, true, false},
1022 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1023 kBaseRect.width() + 2, kBaseRect.height()),
1024 false, true, false},
1025
1026 // zero-w/h rects at each corner of kBaseRect
1027 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1028 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1029 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1030 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1031
1032 // far away rect
1033 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1034 SkIntToScalar(10), SkIntToScalar(10)),
1035 false, false, false},
1036
1037 // very large rect containing kBaseRect
1038 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1039 kBaseRect.fTop - 5 * kBaseRect.height(),
1040 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1041 false, false, false},
1042
1043 // skinny rect that spans same y-range as kBaseRect
1044 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1045 SkIntToScalar(1), kBaseRect.height()),
1046 true, true, true},
1047
1048 // short rect that spans same x-range as kBaseRect
1049 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1050 true, true, true},
1051
1052 // skinny rect that spans slightly larger y-range than kBaseRect
1053 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1054 SkIntToScalar(1), kBaseRect.height() + 1),
1055 false, true, false},
1056
1057 // short rect that spans slightly larger x-range than kBaseRect
1058 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1059 kBaseRect.width() + 1, SkScalar(1)),
1060 false, true, false},
1061 };
1062
1063 for (int inv = 0; inv < 4; ++inv) {
1064 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
1065 SkRect qRect = kQueries[q].fQueryRect;
1066 if (inv & 0x1) {
1067 SkTSwap(qRect.fLeft, qRect.fRight);
1068 }
1069 if (inv & 0x2) {
1070 SkTSwap(qRect.fTop, qRect.fBottom);
1071 }
1072 for (int d = 0; d < 2; ++d) {
1073 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1074 path.reset();
1075 path.addRect(kBaseRect, dir);
1076 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1077 path.conservativelyContainsRect(qRect));
1078
1079 path.reset();
1080 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1081 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1082 path.conservativelyContainsRect(qRect));
1083
1084 path.reset();
1085 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1086 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1087 path.conservativelyContainsRect(qRect));
1088 }
1089 // Slightly non-convex shape, shouldn't contain any rects.
1090 path.reset();
1091 path.moveTo(0, 0);
1092 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1093 path.lineTo(SkIntToScalar(100), 0);
1094 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1095 path.lineTo(0, SkIntToScalar(100));
1096 path.close();
1097 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1098 }
1099 }
1100
1101 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1102 path.reset();
1103 path.moveTo(0, 0);
1104 path.lineTo(SkIntToScalar(100), 0);
1105 path.lineTo(0, SkIntToScalar(100));
1106
1107 // inside, on along top edge
1108 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1109 SkIntToScalar(10),
1110 SkIntToScalar(10))));
1111 // above
1112 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1113 SkRect::MakeXYWH(SkIntToScalar(50),
1114 SkIntToScalar(-10),
1115 SkIntToScalar(10),
1116 SkIntToScalar(10))));
1117 // to the left
1118 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1119 SkIntToScalar(5),
1120 SkIntToScalar(5),
1121 SkIntToScalar(5))));
1122
1123 // outside the diagonal edge
1124 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1125 SkIntToScalar(200),
1126 SkIntToScalar(20),
1127 SkIntToScalar(5))));
1128 }
1129
1130 // Simple isRect test is inline TestPath, below.
1131 // test_isRect provides more extensive testing.
test_isRect(skiatest::Reporter * reporter)1132 static void test_isRect(skiatest::Reporter* reporter) {
1133 // passing tests (all moveTo / lineTo...
1134 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1135 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1136 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1137 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1138 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1139 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1140 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1141 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1142 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1143 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1144 {1, 0}, {.5f, 0}};
1145 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1146 {0, 1}, {0, .5f}};
1147 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1148 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1149 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1150 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
1151
1152 // failing tests
1153 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1154 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1155 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1156 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1157 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1158 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1159 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1160 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1161 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1162 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1163 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
1164
1165 // failing, no close
1166 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1167 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1168
1169 size_t testLen[] = {
1170 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1171 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1172 sizeof(rd), sizeof(re), sizeof(rf),
1173 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1174 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
1175 sizeof(c1), sizeof(c2)
1176 };
1177 SkPoint* tests[] = {
1178 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1179 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
1180 c1, c2
1181 };
1182 SkPoint* lastPass = rf;
1183 SkPoint* lastClose = fb;
1184 bool fail = false;
1185 bool close = true;
1186 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1187 size_t index;
1188 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1189 SkPath path;
1190 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1191 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1192 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1193 }
1194 if (close) {
1195 path.close();
1196 }
1197 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1198 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
1199
1200 if (!fail) {
1201 SkRect computed, expected;
1202 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1203 REPORTER_ASSERT(reporter, path.isRect(&computed));
1204 REPORTER_ASSERT(reporter, expected == computed);
1205
1206 bool isClosed;
1207 SkPath::Direction direction, cheapDirection;
1208 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1209 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1210 REPORTER_ASSERT(reporter, isClosed == close);
1211 REPORTER_ASSERT(reporter, direction == cheapDirection);
1212 } else {
1213 SkRect computed;
1214 computed.set(123, 456, 789, 1011);
1215 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1216 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1217 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1218
1219 bool isClosed = (bool) -1;
1220 SkPath::Direction direction = (SkPath::Direction) -1;
1221 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1222 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1223 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
1224 }
1225
1226 if (tests[testIndex] == lastPass) {
1227 fail = true;
1228 }
1229 if (tests[testIndex] == lastClose) {
1230 close = false;
1231 }
1232 }
1233
1234 // fail, close then line
1235 SkPath path1;
1236 path1.moveTo(r1[0].fX, r1[0].fY);
1237 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1238 path1.lineTo(r1[index].fX, r1[index].fY);
1239 }
1240 path1.close();
1241 path1.lineTo(1, 0);
1242 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1243
1244 // fail, move in the middle
1245 path1.reset();
1246 path1.moveTo(r1[0].fX, r1[0].fY);
1247 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1248 if (index == 2) {
1249 path1.moveTo(1, .5f);
1250 }
1251 path1.lineTo(r1[index].fX, r1[index].fY);
1252 }
1253 path1.close();
1254 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1255
1256 // fail, move on the edge
1257 path1.reset();
1258 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1259 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1260 path1.lineTo(r1[index].fX, r1[index].fY);
1261 }
1262 path1.close();
1263 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1264
1265 // fail, quad
1266 path1.reset();
1267 path1.moveTo(r1[0].fX, r1[0].fY);
1268 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1269 if (index == 2) {
1270 path1.quadTo(1, .5f, 1, .5f);
1271 }
1272 path1.lineTo(r1[index].fX, r1[index].fY);
1273 }
1274 path1.close();
1275 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1276
1277 // fail, cubic
1278 path1.reset();
1279 path1.moveTo(r1[0].fX, r1[0].fY);
1280 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1281 if (index == 2) {
1282 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1283 }
1284 path1.lineTo(r1[index].fX, r1[index].fY);
1285 }
1286 path1.close();
1287 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
1288 }
1289
test_isNestedRects(skiatest::Reporter * reporter)1290 static void test_isNestedRects(skiatest::Reporter* reporter) {
1291 // passing tests (all moveTo / lineTo...
1292 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1293 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1294 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1295 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1296 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1297 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1298 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1299 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1300 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1301 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1302 {1, 0}, {.5f, 0}};
1303 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1304 {0, 1}, {0, .5f}};
1305 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1306 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1307 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1308
1309 // failing tests
1310 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1311 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1312 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1313 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1314 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1315 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1316 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1317 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1318
1319 // failing, no close
1320 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1321 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1322
1323 size_t testLen[] = {
1324 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1325 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1326 sizeof(rd), sizeof(re),
1327 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1328 sizeof(f7), sizeof(f8),
1329 sizeof(c1), sizeof(c2)
1330 };
1331 SkPoint* tests[] = {
1332 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1333 f1, f2, f3, f4, f5, f6, f7, f8,
1334 c1, c2
1335 };
1336 const SkPoint* lastPass = re;
1337 const SkPoint* lastClose = f8;
1338 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1339 size_t index;
1340 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1341 bool fail = false;
1342 bool close = true;
1343 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1344 SkPath path;
1345 if (rectFirst) {
1346 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1347 }
1348 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1349 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1350 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1351 }
1352 if (close) {
1353 path.close();
1354 }
1355 if (!rectFirst) {
1356 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1357 }
1358 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1359 if (!fail) {
1360 SkRect expected[2], computed[2];
1361 SkRect testBounds;
1362 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1363 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1364 expected[1] = testBounds;
1365 REPORTER_ASSERT(reporter, path.isNestedRects(computed));
1366 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1367 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1368 }
1369 if (tests[testIndex] == lastPass) {
1370 fail = true;
1371 }
1372 if (tests[testIndex] == lastClose) {
1373 close = false;
1374 }
1375 }
1376
1377 // fail, close then line
1378 SkPath path1;
1379 if (rectFirst) {
1380 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1381 }
1382 path1.moveTo(r1[0].fX, r1[0].fY);
1383 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1384 path1.lineTo(r1[index].fX, r1[index].fY);
1385 }
1386 path1.close();
1387 path1.lineTo(1, 0);
1388 if (!rectFirst) {
1389 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1390 }
1391 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1392
1393 // fail, move in the middle
1394 path1.reset();
1395 if (rectFirst) {
1396 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1397 }
1398 path1.moveTo(r1[0].fX, r1[0].fY);
1399 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1400 if (index == 2) {
1401 path1.moveTo(1, .5f);
1402 }
1403 path1.lineTo(r1[index].fX, r1[index].fY);
1404 }
1405 path1.close();
1406 if (!rectFirst) {
1407 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1408 }
1409 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1410
1411 // fail, move on the edge
1412 path1.reset();
1413 if (rectFirst) {
1414 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1415 }
1416 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1417 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1418 path1.lineTo(r1[index].fX, r1[index].fY);
1419 }
1420 path1.close();
1421 if (!rectFirst) {
1422 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1423 }
1424 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1425
1426 // fail, quad
1427 path1.reset();
1428 if (rectFirst) {
1429 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1430 }
1431 path1.moveTo(r1[0].fX, r1[0].fY);
1432 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1433 if (index == 2) {
1434 path1.quadTo(1, .5f, 1, .5f);
1435 }
1436 path1.lineTo(r1[index].fX, r1[index].fY);
1437 }
1438 path1.close();
1439 if (!rectFirst) {
1440 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1441 }
1442 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1443
1444 // fail, cubic
1445 path1.reset();
1446 if (rectFirst) {
1447 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1448 }
1449 path1.moveTo(r1[0].fX, r1[0].fY);
1450 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1451 if (index == 2) {
1452 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1453 }
1454 path1.lineTo(r1[index].fX, r1[index].fY);
1455 }
1456 path1.close();
1457 if (!rectFirst) {
1458 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1459 }
1460 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1461
1462 // fail, not nested
1463 path1.reset();
1464 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1465 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1466 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1467 }
1468
1469 // pass, stroke rect
1470 SkPath src, dst;
1471 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1472 SkPaint strokePaint;
1473 strokePaint.setStyle(SkPaint::kStroke_Style);
1474 strokePaint.setStrokeWidth(2);
1475 strokePaint.getFillPath(src, &dst);
1476 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
1477 }
1478
write_and_read_back(skiatest::Reporter * reporter,const SkPath & p)1479 static void write_and_read_back(skiatest::Reporter* reporter,
1480 const SkPath& p) {
1481 SkWriter32 writer(100);
1482 writer.writePath(p);
1483 size_t size = writer.size();
1484 SkAutoMalloc storage(size);
1485 writer.flatten(storage.get());
1486 SkReader32 reader(storage.get(), size);
1487
1488 SkPath readBack;
1489 REPORTER_ASSERT(reporter, readBack != p);
1490 reader.readPath(&readBack);
1491 REPORTER_ASSERT(reporter, readBack == p);
1492
1493 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
1494 p.getConvexityOrUnknown());
1495
1496 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1497
1498 const SkRect& origBounds = p.getBounds();
1499 const SkRect& readBackBounds = readBack.getBounds();
1500
1501 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1502 }
1503
test_flattening(skiatest::Reporter * reporter)1504 static void test_flattening(skiatest::Reporter* reporter) {
1505 SkPath p;
1506
1507 static const SkPoint pts[] = {
1508 { 0, 0 },
1509 { SkIntToScalar(10), SkIntToScalar(10) },
1510 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1511 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1512 };
1513 p.moveTo(pts[0]);
1514 p.lineTo(pts[1]);
1515 p.quadTo(pts[2], pts[3]);
1516 p.cubicTo(pts[4], pts[5], pts[6]);
1517
1518 write_and_read_back(reporter, p);
1519
1520 // create a buffer that should be much larger than the path so we don't
1521 // kill our stack if writer goes too far.
1522 char buffer[1024];
1523 uint32_t size1 = p.writeToMemory(NULL);
1524 uint32_t size2 = p.writeToMemory(buffer);
1525 REPORTER_ASSERT(reporter, size1 == size2);
1526
1527 SkPath p2;
1528 uint32_t size3 = p2.readFromMemory(buffer);
1529 REPORTER_ASSERT(reporter, size1 == size3);
1530 REPORTER_ASSERT(reporter, p == p2);
1531
1532 char buffer2[1024];
1533 size3 = p2.writeToMemory(buffer2);
1534 REPORTER_ASSERT(reporter, size1 == size3);
1535 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
1536
1537 // test persistence of the oval flag & convexity
1538 {
1539 SkPath oval;
1540 SkRect rect = SkRect::MakeWH(10, 10);
1541 oval.addOval(rect);
1542
1543 write_and_read_back(reporter, oval);
1544 }
1545 }
1546
test_transform(skiatest::Reporter * reporter)1547 static void test_transform(skiatest::Reporter* reporter) {
1548 SkPath p, p1;
1549
1550 static const SkPoint pts[] = {
1551 { 0, 0 },
1552 { SkIntToScalar(10), SkIntToScalar(10) },
1553 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1554 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1555 };
1556 p.moveTo(pts[0]);
1557 p.lineTo(pts[1]);
1558 p.quadTo(pts[2], pts[3]);
1559 p.cubicTo(pts[4], pts[5], pts[6]);
1560
1561 SkMatrix matrix;
1562 matrix.reset();
1563 p.transform(matrix, &p1);
1564 REPORTER_ASSERT(reporter, p == p1);
1565
1566 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1567 p.transform(matrix, &p1);
1568 SkPoint pts1[7];
1569 int count = p1.getPoints(pts1, 7);
1570 REPORTER_ASSERT(reporter, 7 == count);
1571 for (int i = 0; i < count; ++i) {
1572 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1573 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1574 }
1575 }
1576
test_zero_length_paths(skiatest::Reporter * reporter)1577 static void test_zero_length_paths(skiatest::Reporter* reporter) {
1578 SkPath p;
1579 uint8_t verbs[32];
1580
1581 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
1582 const char* testPath;
1583 const size_t numResultPts;
1584 const SkRect resultBound;
1585 const SkPath::Verb* resultVerbs;
1586 const size_t numResultVerbs;
1587 };
1588
1589 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1590 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1591 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1592 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1593 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1594 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1595 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1596 static const SkPath::Verb resultVerbs8[] = {
1597 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1598 };
1599 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1600 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1601 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1602 static const SkPath::Verb resultVerbs12[] = {
1603 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1604 };
1605 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1606 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1607 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1608 static const SkPath::Verb resultVerbs16[] = {
1609 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1610 };
1611 static const struct zeroPathTestData gZeroLengthTests[] = {
1612 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1613 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1614 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1615 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1616 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1617 { "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) },
1618 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1619 { "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) },
1620 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1621 { "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) },
1622 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1623 { "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) },
1624 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1625 { "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,
1626 SK_ARRAY_COUNT(resultVerbs14)
1627 },
1628 { "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) },
1629 { "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,
1630 SK_ARRAY_COUNT(resultVerbs16)
1631 }
1632 };
1633
1634 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1635 p.reset();
1636 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1637 REPORTER_ASSERT(reporter, valid);
1638 REPORTER_ASSERT(reporter, !p.isEmpty());
1639 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1640 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1641 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1642 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1643 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1644 }
1645 }
1646 }
1647
1648 struct SegmentInfo {
1649 SkPath fPath;
1650 int fPointCount;
1651 };
1652
1653 #define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1654
test_segment_masks(skiatest::Reporter * reporter)1655 static void test_segment_masks(skiatest::Reporter* reporter) {
1656 SkPath p, p2;
1657
1658 p.moveTo(0, 0);
1659 p.quadTo(100, 100, 200, 200);
1660 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1661 REPORTER_ASSERT(reporter, !p.isEmpty());
1662 p2 = p;
1663 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1664 p.cubicTo(100, 100, 200, 200, 300, 300);
1665 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1666 REPORTER_ASSERT(reporter, !p.isEmpty());
1667 p2 = p;
1668 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1669
1670 p.reset();
1671 p.moveTo(0, 0);
1672 p.cubicTo(100, 100, 200, 200, 300, 300);
1673 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
1674 p2 = p;
1675 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1676
1677 REPORTER_ASSERT(reporter, !p.isEmpty());
1678 }
1679
test_iter(skiatest::Reporter * reporter)1680 static void test_iter(skiatest::Reporter* reporter) {
1681 SkPath p;
1682 SkPoint pts[4];
1683
1684 // Test an iterator with no path
1685 SkPath::Iter noPathIter;
1686 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1687
1688 // Test that setting an empty path works
1689 noPathIter.setPath(p, false);
1690 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1691
1692 // Test that close path makes no difference for an empty path
1693 noPathIter.setPath(p, true);
1694 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1695
1696 // Test an iterator with an initial empty path
1697 SkPath::Iter iter(p, false);
1698 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1699
1700 // Test that close path makes no difference
1701 iter.setPath(p, true);
1702 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1703
1704
1705 struct iterTestData {
1706 const char* testPath;
1707 const bool forceClose;
1708 const bool consumeDegenerates;
1709 const size_t* numResultPtsPerVerb;
1710 const SkPoint* resultPts;
1711 const SkPath::Verb* resultVerbs;
1712 const size_t numResultVerbs;
1713 };
1714
1715 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1716 static const SkPath::Verb resultVerbs2[] = {
1717 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1718 };
1719 static const SkPath::Verb resultVerbs3[] = {
1720 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1721 };
1722 static const SkPath::Verb resultVerbs4[] = {
1723 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1724 };
1725 static const SkPath::Verb resultVerbs5[] = {
1726 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1727 };
1728 static const size_t resultPtsSizes1[] = { 0 };
1729 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1730 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1731 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1732 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
1733 static const SkPoint* resultPts1 = 0;
1734 static const SkPoint resultPts2[] = {
1735 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1736 };
1737 static const SkPoint resultPts3[] = {
1738 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1739 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1740 };
1741 static const SkPoint resultPts4[] = {
1742 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1743 };
1744 static const SkPoint resultPts5[] = {
1745 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1746 };
1747 static const struct iterTestData gIterTests[] = {
1748 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1749 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1750 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1751 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1752 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1753 { "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) },
1754 { "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) },
1755 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1756 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1757 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1758 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1759 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1760 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
1761 };
1762
1763 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1764 p.reset();
1765 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1766 REPORTER_ASSERT(reporter, valid);
1767 iter.setPath(p, gIterTests[i].forceClose);
1768 int j = 0, l = 0;
1769 do {
1770 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1771 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1772 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1773 }
1774 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1775 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1776 }
1777
1778 // The GM degeneratesegments.cpp test is more extensive
1779 }
1780
test_raw_iter(skiatest::Reporter * reporter)1781 static void test_raw_iter(skiatest::Reporter* reporter) {
1782 SkPath p;
1783 SkPoint pts[4];
1784
1785 // Test an iterator with no path
1786 SkPath::RawIter noPathIter;
1787 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1788 // Test that setting an empty path works
1789 noPathIter.setPath(p);
1790 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1791
1792 // Test an iterator with an initial empty path
1793 SkPath::RawIter iter(p);
1794 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1795
1796 // Test that a move-only path returns the move.
1797 p.moveTo(SK_Scalar1, 0);
1798 iter.setPath(p);
1799 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1800 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1801 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1802 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1803
1804 // No matter how many moves we add, we should get them all back
1805 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1806 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1807 iter.setPath(p);
1808 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1809 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1810 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1811 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1812 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1813 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1814 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1815 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1816 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1817 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1818
1819 // Initial close is never ever stored
1820 p.reset();
1821 p.close();
1822 iter.setPath(p);
1823 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1824
1825 // Move/close sequences
1826 p.reset();
1827 p.close(); // Not stored, no purpose
1828 p.moveTo(SK_Scalar1, 0);
1829 p.close();
1830 p.close(); // Not stored, no purpose
1831 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1832 p.close();
1833 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1834 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1835 p.close();
1836 iter.setPath(p);
1837 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1838 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1839 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1840 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1841 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1842 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1843 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1844 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1845 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1846 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1847 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1848 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1849 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1850 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1851 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1852 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1853 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1854 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1855 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1856 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1857 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1858 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1859
1860 // Generate random paths and verify
1861 SkPoint randomPts[25];
1862 for (int i = 0; i < 5; ++i) {
1863 for (int j = 0; j < 5; ++j) {
1864 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1865 }
1866 }
1867
1868 // Max of 10 segments, max 3 points per segment
1869 SkRandom rand(9876543);
1870 SkPoint expectedPts[31]; // May have leading moveTo
1871 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
1872 SkPath::Verb nextVerb;
1873
1874 for (int i = 0; i < 500; ++i) {
1875 p.reset();
1876 bool lastWasClose = true;
1877 bool haveMoveTo = false;
1878 SkPoint lastMoveToPt = { 0, 0 };
1879 int numPoints = 0;
1880 int numVerbs = (rand.nextU() >> 16) % 10;
1881 int numIterVerbs = 0;
1882 for (int j = 0; j < numVerbs; ++j) {
1883 do {
1884 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1885 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
1886 switch (nextVerb) {
1887 case SkPath::kMove_Verb:
1888 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1889 p.moveTo(expectedPts[numPoints]);
1890 lastMoveToPt = expectedPts[numPoints];
1891 numPoints += 1;
1892 lastWasClose = false;
1893 haveMoveTo = true;
1894 break;
1895 case SkPath::kLine_Verb:
1896 if (!haveMoveTo) {
1897 expectedPts[numPoints++] = lastMoveToPt;
1898 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1899 haveMoveTo = true;
1900 }
1901 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1902 p.lineTo(expectedPts[numPoints]);
1903 numPoints += 1;
1904 lastWasClose = false;
1905 break;
1906 case SkPath::kQuad_Verb:
1907 if (!haveMoveTo) {
1908 expectedPts[numPoints++] = lastMoveToPt;
1909 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1910 haveMoveTo = true;
1911 }
1912 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1913 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1914 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1915 numPoints += 2;
1916 lastWasClose = false;
1917 break;
1918 case SkPath::kCubic_Verb:
1919 if (!haveMoveTo) {
1920 expectedPts[numPoints++] = lastMoveToPt;
1921 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1922 haveMoveTo = true;
1923 }
1924 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1925 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1926 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1927 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1928 expectedPts[numPoints + 2]);
1929 numPoints += 3;
1930 lastWasClose = false;
1931 break;
1932 case SkPath::kClose_Verb:
1933 p.close();
1934 haveMoveTo = false;
1935 lastWasClose = true;
1936 break;
1937 default:;
1938 }
1939 expectedVerbs[numIterVerbs++] = nextVerb;
1940 }
1941
1942 iter.setPath(p);
1943 numVerbs = numIterVerbs;
1944 numIterVerbs = 0;
1945 int numIterPts = 0;
1946 SkPoint lastMoveTo;
1947 SkPoint lastPt;
1948 lastMoveTo.set(0, 0);
1949 lastPt.set(0, 0);
1950 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1951 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1952 numIterVerbs++;
1953 switch (nextVerb) {
1954 case SkPath::kMove_Verb:
1955 REPORTER_ASSERT(reporter, numIterPts < numPoints);
1956 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1957 lastPt = lastMoveTo = pts[0];
1958 numIterPts += 1;
1959 break;
1960 case SkPath::kLine_Verb:
1961 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1962 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1963 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1964 lastPt = pts[1];
1965 numIterPts += 1;
1966 break;
1967 case SkPath::kQuad_Verb:
1968 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1969 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1970 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1971 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1972 lastPt = pts[2];
1973 numIterPts += 2;
1974 break;
1975 case SkPath::kCubic_Verb:
1976 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1977 REPORTER_ASSERT(reporter, pts[0] == lastPt);
1978 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1979 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1980 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1981 lastPt = pts[3];
1982 numIterPts += 3;
1983 break;
1984 case SkPath::kClose_Verb:
1985 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1986 lastPt = lastMoveTo;
1987 break;
1988 default:;
1989 }
1990 }
1991 REPORTER_ASSERT(reporter, numIterPts == numPoints);
1992 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1993 }
1994 }
1995
check_for_circle(skiatest::Reporter * reporter,const SkPath & path,bool expectedCircle,SkPath::Direction expectedDir)1996 static void check_for_circle(skiatest::Reporter* reporter,
1997 const SkPath& path,
1998 bool expectedCircle,
1999 SkPath::Direction expectedDir) {
2000 SkRect rect;
2001 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2002 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
2003
2004 if (expectedCircle) {
2005 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2006 }
2007 }
2008
test_circle_skew(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2009 static void test_circle_skew(skiatest::Reporter* reporter,
2010 const SkPath& path,
2011 SkPath::Direction dir) {
2012 SkPath tmp;
2013
2014 SkMatrix m;
2015 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2016 path.transform(m, &tmp);
2017 // this matrix reverses the direction.
2018 if (SkPath::kCCW_Direction == dir) {
2019 dir = SkPath::kCW_Direction;
2020 } else {
2021 SkASSERT(SkPath::kCW_Direction == dir);
2022 dir = SkPath::kCCW_Direction;
2023 }
2024 check_for_circle(reporter, tmp, false, dir);
2025 }
2026
test_circle_translate(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2027 static void test_circle_translate(skiatest::Reporter* reporter,
2028 const SkPath& path,
2029 SkPath::Direction dir) {
2030 SkPath tmp;
2031
2032 // translate at small offset
2033 SkMatrix m;
2034 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2035 path.transform(m, &tmp);
2036 check_for_circle(reporter, tmp, true, dir);
2037
2038 tmp.reset();
2039 m.reset();
2040
2041 // translate at a relatively big offset
2042 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2043 path.transform(m, &tmp);
2044 check_for_circle(reporter, tmp, true, dir);
2045 }
2046
test_circle_rotate(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2047 static void test_circle_rotate(skiatest::Reporter* reporter,
2048 const SkPath& path,
2049 SkPath::Direction dir) {
2050 for (int angle = 0; angle < 360; ++angle) {
2051 SkPath tmp;
2052 SkMatrix m;
2053 m.setRotate(SkIntToScalar(angle));
2054 path.transform(m, &tmp);
2055
2056 // TODO: a rotated circle whose rotated angle is not a multiple of 90
2057 // degrees is not an oval anymore, this can be improved. we made this
2058 // for the simplicity of our implementation.
2059 if (angle % 90 == 0) {
2060 check_for_circle(reporter, tmp, true, dir);
2061 } else {
2062 check_for_circle(reporter, tmp, false, dir);
2063 }
2064 }
2065 }
2066
test_circle_mirror_x(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2067 static void test_circle_mirror_x(skiatest::Reporter* reporter,
2068 const SkPath& path,
2069 SkPath::Direction dir) {
2070 SkPath tmp;
2071 SkMatrix m;
2072 m.reset();
2073 m.setScaleX(-SK_Scalar1);
2074 path.transform(m, &tmp);
2075
2076 if (SkPath::kCW_Direction == dir) {
2077 dir = SkPath::kCCW_Direction;
2078 } else {
2079 SkASSERT(SkPath::kCCW_Direction == dir);
2080 dir = SkPath::kCW_Direction;
2081 }
2082
2083 check_for_circle(reporter, tmp, true, dir);
2084 }
2085
test_circle_mirror_y(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2086 static void test_circle_mirror_y(skiatest::Reporter* reporter,
2087 const SkPath& path,
2088 SkPath::Direction dir) {
2089 SkPath tmp;
2090 SkMatrix m;
2091 m.reset();
2092 m.setScaleY(-SK_Scalar1);
2093 path.transform(m, &tmp);
2094
2095 if (SkPath::kCW_Direction == dir) {
2096 dir = SkPath::kCCW_Direction;
2097 } else {
2098 SkASSERT(SkPath::kCCW_Direction == dir);
2099 dir = SkPath::kCW_Direction;
2100 }
2101
2102 check_for_circle(reporter, tmp, true, dir);
2103 }
2104
test_circle_mirror_xy(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2105 static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2106 const SkPath& path,
2107 SkPath::Direction dir) {
2108 SkPath tmp;
2109 SkMatrix m;
2110 m.reset();
2111 m.setScaleX(-SK_Scalar1);
2112 m.setScaleY(-SK_Scalar1);
2113 path.transform(m, &tmp);
2114
2115 check_for_circle(reporter, tmp, true, dir);
2116 }
2117
test_circle_with_direction(skiatest::Reporter * reporter,SkPath::Direction dir)2118 static void test_circle_with_direction(skiatest::Reporter* reporter,
2119 SkPath::Direction dir) {
2120 SkPath path;
2121
2122 // circle at origin
2123 path.addCircle(0, 0, SkIntToScalar(20), dir);
2124 check_for_circle(reporter, path, true, dir);
2125 test_circle_rotate(reporter, path, dir);
2126 test_circle_translate(reporter, path, dir);
2127 test_circle_skew(reporter, path, dir);
2128
2129 // circle at an offset at (10, 10)
2130 path.reset();
2131 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2132 SkIntToScalar(20), dir);
2133 check_for_circle(reporter, path, true, dir);
2134 test_circle_rotate(reporter, path, dir);
2135 test_circle_translate(reporter, path, dir);
2136 test_circle_skew(reporter, path, dir);
2137 test_circle_mirror_x(reporter, path, dir);
2138 test_circle_mirror_y(reporter, path, dir);
2139 test_circle_mirror_xy(reporter, path, dir);
2140 }
2141
test_circle_with_add_paths(skiatest::Reporter * reporter)2142 static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2143 SkPath path;
2144 SkPath circle;
2145 SkPath rect;
2146 SkPath empty;
2147
2148 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2149 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2150
2151 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
2152 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2153 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2154
2155 SkMatrix translate;
2156 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2157
2158 // For simplicity, all the path concatenation related operations
2159 // would mark it non-circle, though in theory it's still a circle.
2160
2161 // empty + circle (translate)
2162 path = empty;
2163 path.addPath(circle, translate);
2164 check_for_circle(reporter, path, false, kCircleDir);
2165
2166 // circle + empty (translate)
2167 path = circle;
2168 path.addPath(empty, translate);
2169 check_for_circle(reporter, path, false, kCircleDir);
2170
2171 // test reverseAddPath
2172 path = circle;
2173 path.reverseAddPath(rect);
2174 check_for_circle(reporter, path, false, kCircleDirOpposite);
2175 }
2176
test_circle(skiatest::Reporter * reporter)2177 static void test_circle(skiatest::Reporter* reporter) {
2178 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2179 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2180
2181 // multiple addCircle()
2182 SkPath path;
2183 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2184 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
2185 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2186
2187 // some extra lineTo() would make isOval() fail
2188 path.reset();
2189 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2190 path.lineTo(0, 0);
2191 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2192
2193 // not back to the original point
2194 path.reset();
2195 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2196 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
2197 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2198
2199 test_circle_with_add_paths(reporter);
2200 }
2201
test_oval(skiatest::Reporter * reporter)2202 static void test_oval(skiatest::Reporter* reporter) {
2203 SkRect rect;
2204 SkMatrix m;
2205 SkPath path;
2206
2207 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2208 path.addOval(rect);
2209
2210 REPORTER_ASSERT(reporter, path.isOval(NULL));
2211
2212 m.setRotate(SkIntToScalar(90));
2213 SkPath tmp;
2214 path.transform(m, &tmp);
2215 // an oval rotated 90 degrees is still an oval.
2216 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2217
2218 m.reset();
2219 m.setRotate(SkIntToScalar(30));
2220 tmp.reset();
2221 path.transform(m, &tmp);
2222 // an oval rotated 30 degrees is not an oval anymore.
2223 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2224
2225 // since empty path being transformed.
2226 path.reset();
2227 tmp.reset();
2228 m.reset();
2229 path.transform(m, &tmp);
2230 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2231
2232 // empty path is not an oval
2233 tmp.reset();
2234 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2235
2236 // only has moveTo()s
2237 tmp.reset();
2238 tmp.moveTo(0, 0);
2239 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2240 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2241
2242 // mimic WebKit's calling convention,
2243 // call moveTo() first and then call addOval()
2244 path.reset();
2245 path.moveTo(0, 0);
2246 path.addOval(rect);
2247 REPORTER_ASSERT(reporter, path.isOval(NULL));
2248
2249 // copy path
2250 path.reset();
2251 tmp.reset();
2252 tmp.addOval(rect);
2253 path = tmp;
2254 REPORTER_ASSERT(reporter, path.isOval(NULL));
2255 }
2256
TestPath(skiatest::Reporter * reporter)2257 static void TestPath(skiatest::Reporter* reporter) {
2258 SkTSize<SkScalar>::Make(3,4);
2259
2260 SkPath p, p2;
2261 SkRect bounds, bounds2;
2262
2263 REPORTER_ASSERT(reporter, p.isEmpty());
2264 REPORTER_ASSERT(reporter, 0 == p.countPoints());
2265 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
2266 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
2267 REPORTER_ASSERT(reporter, p.isConvex());
2268 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2269 REPORTER_ASSERT(reporter, !p.isInverseFillType());
2270 REPORTER_ASSERT(reporter, p == p2);
2271 REPORTER_ASSERT(reporter, !(p != p2));
2272
2273 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
2274
2275 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
2276
2277 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2278 check_convex_bounds(reporter, p, bounds);
2279 // we have quads or cubics
2280 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
2281 REPORTER_ASSERT(reporter, !p.isEmpty());
2282
2283 p.reset();
2284 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
2285 REPORTER_ASSERT(reporter, p.isEmpty());
2286
2287 p.addOval(bounds);
2288 check_convex_bounds(reporter, p, bounds);
2289 REPORTER_ASSERT(reporter, !p.isEmpty());
2290
2291 p.reset();
2292 p.addRect(bounds);
2293 check_convex_bounds(reporter, p, bounds);
2294 // we have only lines
2295 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
2296 REPORTER_ASSERT(reporter, !p.isEmpty());
2297
2298 REPORTER_ASSERT(reporter, p != p2);
2299 REPORTER_ASSERT(reporter, !(p == p2));
2300
2301 // do getPoints and getVerbs return the right result
2302 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2303 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
2304 SkPoint pts[4];
2305 int count = p.getPoints(pts, 4);
2306 REPORTER_ASSERT(reporter, count == 4);
2307 uint8_t verbs[6];
2308 verbs[5] = 0xff;
2309 p.getVerbs(verbs, 5);
2310 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2311 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2312 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2313 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2314 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2315 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
2316 bounds2.set(pts, 4);
2317 REPORTER_ASSERT(reporter, bounds == bounds2);
2318
2319 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2320 p.offset(SK_Scalar1*3, SK_Scalar1*4);
2321 REPORTER_ASSERT(reporter, bounds == p.getBounds());
2322
2323 REPORTER_ASSERT(reporter, p.isRect(NULL));
2324 bounds2.setEmpty();
2325 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2326 REPORTER_ASSERT(reporter, bounds == bounds2);
2327
2328 // now force p to not be a rect
2329 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2330 p.addRect(bounds);
2331 REPORTER_ASSERT(reporter, !p.isRect(NULL));
2332
2333 test_isLine(reporter);
2334 test_isRect(reporter);
2335 test_isNestedRects(reporter);
2336 test_zero_length_paths(reporter);
2337 test_direction(reporter);
2338 test_convexity(reporter);
2339 test_convexity2(reporter);
2340 test_conservativelyContains(reporter);
2341 test_close(reporter);
2342 test_segment_masks(reporter);
2343 test_flattening(reporter);
2344 test_transform(reporter);
2345 test_bounds(reporter);
2346 test_iter(reporter);
2347 test_raw_iter(reporter);
2348 test_circle(reporter);
2349 test_oval(reporter);
2350 test_strokerec(reporter);
2351 test_addPoly(reporter);
2352 test_isfinite(reporter);
2353 test_isfinite_after_transform(reporter);
2354 test_tricky_cubic(reporter);
2355 test_arb_round_rect_is_convex(reporter);
2356 test_arb_zero_rad_round_rect_is_rect(reporter);
2357 test_addrect_isfinite(reporter);
2358 test_clipped_cubic(reporter);
2359 test_crbug_170666(reporter);
2360 }
2361
2362 #include "TestClassDef.h"
2363 DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
2364