• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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