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