PathTest.cpp revision 2972bb5fd2441709026b350c6b9b66eecd80f868
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 "SkPaint.h"
10#include "SkPath.h"
11#include "SkParse.h"
12#include "SkParsePath.h"
13#include "SkPathEffect.h"
14#include "SkRandom.h"
15#include "SkReader32.h"
16#include "SkSize.h"
17#include "SkWriter32.h"
18
19static void test_rect_isfinite(skiatest::Reporter* reporter) {
20    const SkScalar inf = SK_ScalarInfinity;
21    const SkScalar nan = SK_ScalarNaN;
22
23    SkRect r;
24    r.setEmpty();
25    REPORTER_ASSERT(reporter, r.isFinite());
26    r.set(0, 0, inf, -inf);
27    REPORTER_ASSERT(reporter, !r.isFinite());
28    r.set(0, 0, nan, 0);
29    REPORTER_ASSERT(reporter, !r.isFinite());
30
31    SkPoint pts[] = {
32        { 0, 0 },
33        { SK_Scalar1, 0 },
34        { 0, SK_Scalar1 },
35    };
36
37    bool isFine = r.setBoundsCheck(pts, 3);
38    REPORTER_ASSERT(reporter, isFine);
39    REPORTER_ASSERT(reporter, !r.isEmpty());
40
41    pts[1].set(inf, 0);
42    isFine = r.setBoundsCheck(pts, 3);
43    REPORTER_ASSERT(reporter, !isFine);
44    REPORTER_ASSERT(reporter, r.isEmpty());
45
46    pts[1].set(nan, 0);
47    isFine = r.setBoundsCheck(pts, 3);
48    REPORTER_ASSERT(reporter, !isFine);
49    REPORTER_ASSERT(reporter, r.isEmpty());
50}
51
52static void test_path_isfinite(skiatest::Reporter* reporter) {
53    const SkScalar inf = SK_ScalarInfinity;
54    const SkScalar nan = SK_ScalarNaN;
55
56    SkPath path;
57    REPORTER_ASSERT(reporter, path.isFinite());
58
59    path.reset();
60    REPORTER_ASSERT(reporter, path.isFinite());
61
62    path.reset();
63    path.moveTo(SK_Scalar1, 0);
64    REPORTER_ASSERT(reporter, path.isFinite());
65
66    path.reset();
67    path.moveTo(inf, -inf);
68    REPORTER_ASSERT(reporter, !path.isFinite());
69
70    path.reset();
71    path.moveTo(nan, 0);
72    REPORTER_ASSERT(reporter, !path.isFinite());
73}
74
75static void test_isfinite(skiatest::Reporter* reporter) {
76    test_rect_isfinite(reporter);
77    test_path_isfinite(reporter);
78}
79
80// assert that we always
81//  start with a moveTo
82//  only have 1 moveTo
83//  only have Lines after that
84//  end with a single close
85//  only have (at most) 1 close
86//
87static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
88                      const SkPoint srcPts[], int count, bool expectClose) {
89    SkPath::RawIter iter(path);
90    SkPoint         pts[4];
91
92    bool firstTime = true;
93    bool foundClose = false;
94    for (;;) {
95        switch (iter.next(pts)) {
96            case SkPath::kMove_Verb:
97                REPORTER_ASSERT(reporter, firstTime);
98                REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
99                srcPts++;
100                firstTime = false;
101                break;
102            case SkPath::kLine_Verb:
103                REPORTER_ASSERT(reporter, !firstTime);
104                REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
105                srcPts++;
106                break;
107            case SkPath::kQuad_Verb:
108                REPORTER_ASSERT(reporter, !"unexpected quad verb");
109                break;
110            case SkPath::kCubic_Verb:
111                REPORTER_ASSERT(reporter, !"unexpected cubic verb");
112                break;
113            case SkPath::kClose_Verb:
114                REPORTER_ASSERT(reporter, !firstTime);
115                REPORTER_ASSERT(reporter, !foundClose);
116                REPORTER_ASSERT(reporter, expectClose);
117                foundClose = true;
118                break;
119            case SkPath::kDone_Verb:
120                goto DONE;
121        }
122    }
123DONE:
124    REPORTER_ASSERT(reporter, foundClose == expectClose);
125}
126
127static void test_addPoly(skiatest::Reporter* reporter) {
128    SkPoint pts[32];
129    SkRandom rand;
130
131    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
132        pts[i].fX = rand.nextSScalar1();
133        pts[i].fY = rand.nextSScalar1();
134    }
135
136    for (int doClose = 0; doClose <= 1; ++doClose) {
137        for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
138            SkPath path;
139            path.addPoly(pts, count, SkToBool(doClose));
140            test_poly(reporter, path, pts, count, SkToBool(doClose));
141        }
142    }
143}
144
145static void test_strokerec(skiatest::Reporter* reporter) {
146    SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
147    REPORTER_ASSERT(reporter, rec.isFillStyle());
148
149    rec.setHairlineStyle();
150    REPORTER_ASSERT(reporter, rec.isHairlineStyle());
151
152    rec.setStrokeStyle(SK_Scalar1, false);
153    REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
154
155    rec.setStrokeStyle(SK_Scalar1, true);
156    REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
157
158    rec.setStrokeStyle(0, false);
159    REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
160
161    rec.setStrokeStyle(0, true);
162    REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
163}
164
165/**
166 * cheapIsDirection can take a shortcut when a path is marked convex.
167 * This function ensures that we always test cheapIsDirection when the path
168 * is flagged with unknown convexity status.
169 */
170static void check_direction(SkPath* path,
171                            SkPath::Direction expectedDir,
172                            skiatest::Reporter* reporter) {
173    if (SkPath::kConvex_Convexity == path->getConvexity()) {
174        REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
175        path->setConvexity(SkPath::kUnknown_Convexity);
176    }
177    REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
178}
179
180static void test_direction(skiatest::Reporter* reporter) {
181    size_t i;
182    SkPath path;
183    REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
184    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
185    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
186
187    static const char* gDegen[] = {
188        "M 10 10",
189        "M 10 10 M 20 20",
190        "M 10 10 L 20 20",
191        "M 10 10 L 10 10 L 10 10",
192        "M 10 10 Q 10 10 10 10",
193        "M 10 10 C 10 10 10 10 10 10",
194    };
195    for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
196        path.reset();
197        bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
198        REPORTER_ASSERT(reporter, valid);
199        REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
200    }
201
202    static const char* gCW[] = {
203        "M 10 10 L 10 10 Q 20 10 20 20",
204        "M 10 10 C 20 10 20 20 20 20",
205        "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
206        // rect with top two corners replaced by cubics with identical middle
207        // control points
208        "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10"
209    };
210    for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
211        path.reset();
212        bool valid = SkParsePath::FromSVGString(gCW[i], &path);
213        REPORTER_ASSERT(reporter, valid);
214        check_direction(&path, SkPath::kCW_Direction, reporter);
215    }
216
217    static const char* gCCW[] = {
218        "M 10 10 L 10 10 Q 20 10 20 -20",
219        "M 10 10 C 20 10 20 -20 20 -20",
220        "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
221        // rect with top two corners replaced by cubics with identical middle
222        // control points
223        "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10"
224    };
225    for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
226        path.reset();
227        bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
228        REPORTER_ASSERT(reporter, valid);
229        check_direction(&path, SkPath::kCCW_Direction, reporter);
230    }
231
232    // Test two donuts, each wound a different direction. Only the outer contour
233    // determines the cheap direction
234    path.reset();
235    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
236    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
237    check_direction(&path, SkPath::kCW_Direction, reporter);
238
239    path.reset();
240    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
241    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
242    check_direction(&path, SkPath::kCCW_Direction, reporter);
243
244#ifdef SK_SCALAR_IS_FLOAT
245    // triangle with one point really far from the origin.
246    path.reset();
247    // the first point is roughly 1.05e10, 1.05e10
248    path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
249    path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
250    path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
251    check_direction(&path, SkPath::kCCW_Direction, reporter);
252#endif
253}
254
255static void add_rect(SkPath* path, const SkRect& r) {
256    path->moveTo(r.fLeft, r.fTop);
257    path->lineTo(r.fRight, r.fTop);
258    path->lineTo(r.fRight, r.fBottom);
259    path->lineTo(r.fLeft, r.fBottom);
260    path->close();
261}
262
263static void test_bounds(skiatest::Reporter* reporter) {
264    static const SkRect rects[] = {
265        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
266        { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
267        { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
268        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
269    };
270
271    SkPath path0, path1;
272    for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
273        path0.addRect(rects[i]);
274        add_rect(&path1, rects[i]);
275    }
276
277    REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
278}
279
280static void stroke_cubic(const SkPoint pts[4]) {
281    SkPath path;
282    path.moveTo(pts[0]);
283    path.cubicTo(pts[1], pts[2], pts[3]);
284
285    SkPaint paint;
286    paint.setStyle(SkPaint::kStroke_Style);
287    paint.setStrokeWidth(SK_Scalar1 * 2);
288
289    SkPath fill;
290    paint.getFillPath(path, &fill);
291}
292
293// just ensure this can run w/o any SkASSERTS firing in the debug build
294// we used to assert due to differences in how we determine a degenerate vector
295// but that was fixed with the introduction of SkPoint::CanNormalize
296static void stroke_tiny_cubic() {
297    SkPoint p0[] = {
298        { 372.0f,   92.0f },
299        { 372.0f,   92.0f },
300        { 372.0f,   92.0f },
301        { 372.0f,   92.0f },
302    };
303
304    stroke_cubic(p0);
305
306    SkPoint p1[] = {
307        { 372.0f,       92.0f },
308        { 372.0007f,    92.000755f },
309        { 371.99927f,   92.003922f },
310        { 371.99826f,   92.003899f },
311    };
312
313    stroke_cubic(p1);
314}
315
316static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
317    for (int i = 0; i < 2; ++i) {
318        SkPath::Iter iter(path, SkToBool(i));
319        SkPoint mv;
320        SkPoint pts[4];
321        SkPath::Verb v;
322        int nMT = 0;
323        int nCL = 0;
324        mv.set(0, 0);
325        while (SkPath::kDone_Verb != (v = iter.next(pts))) {
326            switch (v) {
327                case SkPath::kMove_Verb:
328                    mv = pts[0];
329                    ++nMT;
330                    break;
331                case SkPath::kClose_Verb:
332                    REPORTER_ASSERT(reporter, mv == pts[0]);
333                    ++nCL;
334                    break;
335                default:
336                    break;
337            }
338        }
339        // if we force a close on the interator we should have a close
340        // for every moveTo
341        REPORTER_ASSERT(reporter, !i || nMT == nCL);
342    }
343}
344
345static void test_close(skiatest::Reporter* reporter) {
346    SkPath closePt;
347    closePt.moveTo(0, 0);
348    closePt.close();
349    check_close(reporter, closePt);
350
351    SkPath openPt;
352    openPt.moveTo(0, 0);
353    check_close(reporter, openPt);
354
355    SkPath empty;
356    check_close(reporter, empty);
357    empty.close();
358    check_close(reporter, empty);
359
360    SkPath rect;
361    rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
362    check_close(reporter, rect);
363    rect.close();
364    check_close(reporter, rect);
365
366    SkPath quad;
367    quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
368    check_close(reporter, quad);
369    quad.close();
370    check_close(reporter, quad);
371
372    SkPath cubic;
373    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
374                 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
375    check_close(reporter, cubic);
376    cubic.close();
377    check_close(reporter, cubic);
378
379    SkPath line;
380    line.moveTo(SK_Scalar1, SK_Scalar1);
381    line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
382    check_close(reporter, line);
383    line.close();
384    check_close(reporter, line);
385
386    SkPath rect2;
387    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
388    rect2.close();
389    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
390    check_close(reporter, rect2);
391    rect2.close();
392    check_close(reporter, rect2);
393
394    SkPath oval3;
395    oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
396    oval3.close();
397    oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
398    check_close(reporter, oval3);
399    oval3.close();
400    check_close(reporter, oval3);
401
402    SkPath moves;
403    moves.moveTo(SK_Scalar1, SK_Scalar1);
404    moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
405    moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
406    moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
407    check_close(reporter, moves);
408
409    stroke_tiny_cubic();
410}
411
412static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
413                            SkPath::Convexity expected) {
414    SkPath::Convexity c = SkPath::ComputeConvexity(path);
415    REPORTER_ASSERT(reporter, c == expected);
416}
417
418static void test_convexity2(skiatest::Reporter* reporter) {
419    SkPath pt;
420    pt.moveTo(0, 0);
421    pt.close();
422    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
423
424    SkPath line;
425    line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
426    line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
427    line.close();
428    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
429
430    SkPath triLeft;
431    triLeft.moveTo(0, 0);
432    triLeft.lineTo(SK_Scalar1, 0);
433    triLeft.lineTo(SK_Scalar1, SK_Scalar1);
434    triLeft.close();
435    check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
436
437    SkPath triRight;
438    triRight.moveTo(0, 0);
439    triRight.lineTo(-SK_Scalar1, 0);
440    triRight.lineTo(SK_Scalar1, SK_Scalar1);
441    triRight.close();
442    check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
443
444    SkPath square;
445    square.moveTo(0, 0);
446    square.lineTo(SK_Scalar1, 0);
447    square.lineTo(SK_Scalar1, SK_Scalar1);
448    square.lineTo(0, SK_Scalar1);
449    square.close();
450    check_convexity(reporter, square, SkPath::kConvex_Convexity);
451
452    SkPath redundantSquare;
453    redundantSquare.moveTo(0, 0);
454    redundantSquare.lineTo(0, 0);
455    redundantSquare.lineTo(0, 0);
456    redundantSquare.lineTo(SK_Scalar1, 0);
457    redundantSquare.lineTo(SK_Scalar1, 0);
458    redundantSquare.lineTo(SK_Scalar1, 0);
459    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
460    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
461    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
462    redundantSquare.lineTo(0, SK_Scalar1);
463    redundantSquare.lineTo(0, SK_Scalar1);
464    redundantSquare.lineTo(0, SK_Scalar1);
465    redundantSquare.close();
466    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
467
468    SkPath bowTie;
469    bowTie.moveTo(0, 0);
470    bowTie.lineTo(0, 0);
471    bowTie.lineTo(0, 0);
472    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
473    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
474    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
475    bowTie.lineTo(SK_Scalar1, 0);
476    bowTie.lineTo(SK_Scalar1, 0);
477    bowTie.lineTo(SK_Scalar1, 0);
478    bowTie.lineTo(0, SK_Scalar1);
479    bowTie.lineTo(0, SK_Scalar1);
480    bowTie.lineTo(0, SK_Scalar1);
481    bowTie.close();
482    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
483
484    SkPath spiral;
485    spiral.moveTo(0, 0);
486    spiral.lineTo(100*SK_Scalar1, 0);
487    spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
488    spiral.lineTo(0, 100*SK_Scalar1);
489    spiral.lineTo(0, 50*SK_Scalar1);
490    spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
491    spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
492    spiral.close();
493    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
494
495    SkPath dent;
496    dent.moveTo(0, 0);
497    dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
498    dent.lineTo(0, 100*SK_Scalar1);
499    dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
500    dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
501    dent.close();
502    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
503}
504
505static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
506                                const SkRect& bounds) {
507    REPORTER_ASSERT(reporter, p.isConvex());
508    REPORTER_ASSERT(reporter, p.getBounds() == bounds);
509
510    SkPath p2(p);
511    REPORTER_ASSERT(reporter, p2.isConvex());
512    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
513
514    SkPath other;
515    other.swap(p2);
516    REPORTER_ASSERT(reporter, other.isConvex());
517    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
518}
519
520static void setFromString(SkPath* path, const char str[]) {
521    bool first = true;
522    while (str) {
523        SkScalar x, y;
524        str = SkParse::FindScalar(str, &x);
525        if (NULL == str) {
526            break;
527        }
528        str = SkParse::FindScalar(str, &y);
529        SkASSERT(str);
530        if (first) {
531            path->moveTo(x, y);
532            first = false;
533        } else {
534            path->lineTo(x, y);
535        }
536    }
537}
538
539static void test_convexity(skiatest::Reporter* reporter) {
540    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
541    static const SkPath::Convexity V = SkPath::kConvex_Convexity;
542
543    SkPath path;
544
545    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
546    path.addCircle(0, 0, SkIntToScalar(10));
547    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
548    path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
549    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
550    path.reset();
551    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
552    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
553    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
554    path.reset();
555    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
556    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
557    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
558
559    static const struct {
560        const char*         fPathStr;
561        SkPath::Convexity   fExpectedConvexity;
562    } gRec[] = {
563        { "", SkPath::kConvex_Convexity },
564        { "0 0", SkPath::kConvex_Convexity },
565        { "0 0 10 10", SkPath::kConvex_Convexity },
566        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
567        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
568        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
569        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
570        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
571    };
572
573    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
574        SkPath path;
575        setFromString(&path, gRec[i].fPathStr);
576        SkPath::Convexity c = SkPath::ComputeConvexity(path);
577        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
578    }
579}
580
581static void test_isLine(skiatest::Reporter* reporter) {
582    SkPath path;
583    SkPoint pts[2];
584    const SkScalar value = SkIntToScalar(5);
585
586    REPORTER_ASSERT(reporter, !path.isLine(NULL));
587
588    // set some non-zero values
589    pts[0].set(value, value);
590    pts[1].set(value, value);
591    REPORTER_ASSERT(reporter, !path.isLine(pts));
592    // check that pts was untouched
593    REPORTER_ASSERT(reporter, pts[0].equals(value, value));
594    REPORTER_ASSERT(reporter, pts[1].equals(value, value));
595
596    const SkScalar moveX = SkIntToScalar(1);
597    const SkScalar moveY = SkIntToScalar(2);
598    SkASSERT(value != moveX && value != moveY);
599
600    path.moveTo(moveX, moveY);
601    REPORTER_ASSERT(reporter, !path.isLine(NULL));
602    REPORTER_ASSERT(reporter, !path.isLine(pts));
603    // check that pts was untouched
604    REPORTER_ASSERT(reporter, pts[0].equals(value, value));
605    REPORTER_ASSERT(reporter, pts[1].equals(value, value));
606
607    const SkScalar lineX = SkIntToScalar(2);
608    const SkScalar lineY = SkIntToScalar(2);
609    SkASSERT(value != lineX && value != lineY);
610
611    path.lineTo(lineX, lineY);
612    REPORTER_ASSERT(reporter, path.isLine(NULL));
613
614    REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
615    REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
616    REPORTER_ASSERT(reporter, path.isLine(pts));
617    REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
618    REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
619
620    path.lineTo(0, 0);  // too many points/verbs
621    REPORTER_ASSERT(reporter, !path.isLine(NULL));
622    REPORTER_ASSERT(reporter, !path.isLine(pts));
623    REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
624    REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
625}
626
627// Simple isRect test is inline TestPath, below.
628// test_isRect provides more extensive testing.
629static void test_isRect(skiatest::Reporter* reporter) {
630    // passing tests (all moveTo / lineTo...
631    SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
632    SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
633    SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
634    SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
635    SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
636    SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
637    SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
638    SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
639    SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
640    SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
641        {1, 0}, {.5f, 0}};
642    SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
643        {0, 1}, {0, .5f}};
644    SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
645    SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
646    SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
647
648    // failing tests
649    SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
650    SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
651    SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
652    SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
653    SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
654    SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
655    SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
656    SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
657
658    // failing, no close
659    SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
660    SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
661
662    size_t testLen[] = {
663        sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
664        sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
665        sizeof(rd), sizeof(re),
666        sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
667        sizeof(f7), sizeof(f8),
668        sizeof(c1), sizeof(c2)
669    };
670    SkPoint* tests[] = {
671        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
672        f1, f2, f3, f4, f5, f6, f7, f8,
673        c1, c2
674    };
675    SkPoint* lastPass = re;
676    SkPoint* lastClose = f8;
677    bool fail = false;
678    bool close = true;
679    const size_t testCount = sizeof(tests) / sizeof(tests[0]);
680    size_t index;
681    for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
682        SkPath path;
683        path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
684        for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
685            path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
686        }
687        if (close) {
688            path.close();
689        }
690        REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
691        if (tests[testIndex] == lastPass) {
692            fail = true;
693        }
694        if (tests[testIndex] == lastClose) {
695            close = false;
696        }
697    }
698
699    // fail, close then line
700    SkPath path1;
701    path1.moveTo(r1[0].fX, r1[0].fY);
702    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
703        path1.lineTo(r1[index].fX, r1[index].fY);
704    }
705    path1.close();
706    path1.lineTo(1, 0);
707    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
708
709    // fail, move in the middle
710    path1.reset();
711    path1.moveTo(r1[0].fX, r1[0].fY);
712    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
713        if (index == 2) {
714            path1.moveTo(1, .5f);
715        }
716        path1.lineTo(r1[index].fX, r1[index].fY);
717    }
718    path1.close();
719    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
720
721    // fail, move on the edge
722    path1.reset();
723    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
724        path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
725        path1.lineTo(r1[index].fX, r1[index].fY);
726    }
727    path1.close();
728    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
729
730    // fail, quad
731    path1.reset();
732    path1.moveTo(r1[0].fX, r1[0].fY);
733    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
734        if (index == 2) {
735            path1.quadTo(1, .5f, 1, .5f);
736        }
737        path1.lineTo(r1[index].fX, r1[index].fY);
738    }
739    path1.close();
740    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
741
742    // fail, cubic
743    path1.reset();
744    path1.moveTo(r1[0].fX, r1[0].fY);
745    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
746        if (index == 2) {
747            path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
748        }
749        path1.lineTo(r1[index].fX, r1[index].fY);
750    }
751    path1.close();
752    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
753}
754
755static void write_and_read_back(skiatest::Reporter* reporter,
756                                const SkPath& p) {
757    SkWriter32 writer(100);
758    writer.writePath(p);
759    size_t size = writer.size();
760    SkAutoMalloc storage(size);
761    writer.flatten(storage.get());
762    SkReader32 reader(storage.get(), size);
763
764    SkPath readBack;
765    REPORTER_ASSERT(reporter, readBack != p);
766    reader.readPath(&readBack);
767    REPORTER_ASSERT(reporter, readBack == p);
768
769    REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
770                              p.getConvexityOrUnknown());
771
772    REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
773
774    const SkRect& origBounds = p.getBounds();
775    const SkRect& readBackBounds = readBack.getBounds();
776
777    REPORTER_ASSERT(reporter, origBounds == readBackBounds);
778}
779
780static void test_flattening(skiatest::Reporter* reporter) {
781    SkPath p;
782
783    static const SkPoint pts[] = {
784        { 0, 0 },
785        { SkIntToScalar(10), SkIntToScalar(10) },
786        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
787        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
788    };
789    p.moveTo(pts[0]);
790    p.lineTo(pts[1]);
791    p.quadTo(pts[2], pts[3]);
792    p.cubicTo(pts[4], pts[5], pts[6]);
793
794    write_and_read_back(reporter, p);
795
796    // create a buffer that should be much larger than the path so we don't
797    // kill our stack if writer goes too far.
798    char buffer[1024];
799    uint32_t size1 = p.writeToMemory(NULL);
800    uint32_t size2 = p.writeToMemory(buffer);
801    REPORTER_ASSERT(reporter, size1 == size2);
802
803    SkPath p2;
804    uint32_t size3 = p2.readFromMemory(buffer);
805    REPORTER_ASSERT(reporter, size1 == size3);
806    REPORTER_ASSERT(reporter, p == p2);
807
808    char buffer2[1024];
809    size3 = p2.writeToMemory(buffer2);
810    REPORTER_ASSERT(reporter, size1 == size3);
811    REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
812
813    // test persistence of the oval flag & convexity
814    {
815        SkPath oval;
816        SkRect rect = SkRect::MakeWH(10, 10);
817        oval.addOval(rect);
818
819        write_and_read_back(reporter, oval);
820    }
821}
822
823static void test_transform(skiatest::Reporter* reporter) {
824    SkPath p, p1;
825
826    static const SkPoint pts[] = {
827        { 0, 0 },
828        { SkIntToScalar(10), SkIntToScalar(10) },
829        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
830        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
831    };
832    p.moveTo(pts[0]);
833    p.lineTo(pts[1]);
834    p.quadTo(pts[2], pts[3]);
835    p.cubicTo(pts[4], pts[5], pts[6]);
836
837    SkMatrix matrix;
838    matrix.reset();
839    p.transform(matrix, &p1);
840    REPORTER_ASSERT(reporter, p == p1);
841
842    matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
843    p.transform(matrix, &p1);
844    SkPoint pts1[7];
845    int count = p1.getPoints(pts1, 7);
846    REPORTER_ASSERT(reporter, 7 == count);
847    for (int i = 0; i < count; ++i) {
848        SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
849        REPORTER_ASSERT(reporter, newPt == pts1[i]);
850    }
851}
852
853static void test_zero_length_paths(skiatest::Reporter* reporter) {
854    SkPath  p;
855    uint8_t verbs[32];
856
857    struct zeroPathTestData {
858        const char* testPath;
859        const size_t numResultPts;
860        const SkRect resultBound;
861        const SkPath::Verb* resultVerbs;
862        const size_t numResultVerbs;
863    };
864
865    static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
866    static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
867    static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
868    static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
869    static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
870    static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
871    static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
872    static const SkPath::Verb resultVerbs8[] = {
873        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
874    };
875    static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
876    static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
877    static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
878    static const SkPath::Verb resultVerbs12[] = {
879        SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
880    };
881    static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
882    static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
883    static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
884    static const SkPath::Verb resultVerbs16[] = {
885        SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
886    };
887    static const struct zeroPathTestData gZeroLengthTests[] = {
888        { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
889        { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
890        { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
891        { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
892        { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
893        { "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) },
894        { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
895        { "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) },
896        { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
897        { "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) },
898        { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
899        { "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) },
900        { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
901        { "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,
902            SK_ARRAY_COUNT(resultVerbs14)
903        },
904        { "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) },
905        { "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,
906            SK_ARRAY_COUNT(resultVerbs16)
907        }
908    };
909
910    for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
911        p.reset();
912        bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
913        REPORTER_ASSERT(reporter, valid);
914        REPORTER_ASSERT(reporter, !p.isEmpty());
915        REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
916        REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
917        REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
918        for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
919            REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
920        }
921    }
922}
923
924struct SegmentInfo {
925    SkPath fPath;
926    int    fPointCount;
927};
928
929#define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
930
931static void test_segment_masks(skiatest::Reporter* reporter) {
932    SkPath p, p2;
933
934    p.moveTo(0, 0);
935    p.quadTo(100, 100, 200, 200);
936    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
937    REPORTER_ASSERT(reporter, !p.isEmpty());
938    p2 = p;
939    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
940    p.cubicTo(100, 100, 200, 200, 300, 300);
941    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
942    REPORTER_ASSERT(reporter, !p.isEmpty());
943    p2 = p;
944    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
945
946    p.reset();
947    p.moveTo(0, 0);
948    p.cubicTo(100, 100, 200, 200, 300, 300);
949    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
950    p2 = p;
951    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
952
953    REPORTER_ASSERT(reporter, !p.isEmpty());
954}
955
956static void test_iter(skiatest::Reporter* reporter) {
957    SkPath  p;
958    SkPoint pts[4];
959
960    // Test an iterator with no path
961    SkPath::Iter noPathIter;
962    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
963
964    // Test that setting an empty path works
965    noPathIter.setPath(p, false);
966    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
967
968    // Test that close path makes no difference for an empty path
969    noPathIter.setPath(p, true);
970    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
971
972    // Test an iterator with an initial empty path
973    SkPath::Iter iter(p, false);
974    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
975
976    // Test that close path makes no difference
977    iter.setPath(p, true);
978    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
979
980
981    struct iterTestData {
982        const char* testPath;
983        const bool forceClose;
984        const bool consumeDegenerates;
985        const size_t* numResultPtsPerVerb;
986        const SkPoint* resultPts;
987        const SkPath::Verb* resultVerbs;
988        const size_t numResultVerbs;
989    };
990
991    static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
992    static const SkPath::Verb resultVerbs2[] = {
993        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
994    };
995    static const SkPath::Verb resultVerbs3[] = {
996        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
997    };
998    static const SkPath::Verb resultVerbs4[] = {
999        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1000    };
1001    static const SkPath::Verb resultVerbs5[] = {
1002        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1003    };
1004    static const size_t resultPtsSizes1[] = { 0 };
1005    static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1006    static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1007    static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1008    static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
1009    static const SkPoint* resultPts1 = 0;
1010    static const SkPoint resultPts2[] = {
1011        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1012    };
1013    static const SkPoint resultPts3[] = {
1014        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1015        { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1016    };
1017    static const SkPoint resultPts4[] = {
1018        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1019    };
1020    static const SkPoint resultPts5[] = {
1021        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1022    };
1023    static const struct iterTestData gIterTests[] = {
1024        { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1025        { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1026        { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1027        { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1028        { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1029        { "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) },
1030        { "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) },
1031        { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1032        { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1033        { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1034        { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1035        { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1036        { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
1037    };
1038
1039    for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1040        p.reset();
1041        bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1042        REPORTER_ASSERT(reporter, valid);
1043        iter.setPath(p, gIterTests[i].forceClose);
1044        int j = 0, l = 0;
1045        do {
1046            REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1047            for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1048                REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1049            }
1050        } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1051        REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1052    }
1053
1054    // The GM degeneratesegments.cpp test is more extensive
1055}
1056
1057static void test_raw_iter(skiatest::Reporter* reporter) {
1058    SkPath p;
1059    SkPoint pts[4];
1060
1061    // Test an iterator with no path
1062    SkPath::RawIter noPathIter;
1063    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1064    // Test that setting an empty path works
1065    noPathIter.setPath(p);
1066    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1067
1068    // Test an iterator with an initial empty path
1069    SkPath::RawIter iter(p);
1070    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1071
1072    // Test that a move-only path returns the move.
1073    p.moveTo(SK_Scalar1, 0);
1074    iter.setPath(p);
1075    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1076    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1077    REPORTER_ASSERT(reporter, pts[0].fY == 0);
1078    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1079
1080    // No matter how many moves we add, we should get them all back
1081    p.moveTo(SK_Scalar1*2, SK_Scalar1);
1082    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1083    iter.setPath(p);
1084    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1085    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1086    REPORTER_ASSERT(reporter, pts[0].fY == 0);
1087    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1088    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1089    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1090    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1091    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1092    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1093    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1094
1095    // Initial close is never ever stored
1096    p.reset();
1097    p.close();
1098    iter.setPath(p);
1099    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1100
1101    // Move/close sequences
1102    p.reset();
1103    p.close(); // Not stored, no purpose
1104    p.moveTo(SK_Scalar1, 0);
1105    p.close();
1106    p.close(); // Not stored, no purpose
1107    p.moveTo(SK_Scalar1*2, SK_Scalar1);
1108    p.close();
1109    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1110    p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1111    p.close();
1112    iter.setPath(p);
1113    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1114    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1115    REPORTER_ASSERT(reporter, pts[0].fY == 0);
1116    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1117    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1118    REPORTER_ASSERT(reporter, pts[0].fY == 0);
1119    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1120    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1121    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1122    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1123    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1124    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1125    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1126    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1127    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1128    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1129    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1130    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1131    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
1132    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
1133    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
1134    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1135
1136    // Generate random paths and verify
1137    SkPoint randomPts[25];
1138    for (int i = 0; i < 5; ++i) {
1139        for (int j = 0; j < 5; ++j) {
1140            randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
1141        }
1142    }
1143
1144    // Max of 10 segments, max 3 points per segment
1145    SkRandom rand(9876543);
1146    SkPoint          expectedPts[31]; // May have leading moveTo
1147    SkPath::Verb     expectedVerbs[22]; // May have leading moveTo
1148    SkPath::Verb     nextVerb;
1149
1150    for (int i = 0; i < 500; ++i) {
1151        p.reset();
1152        bool lastWasClose = true;
1153        bool haveMoveTo = false;
1154        SkPoint lastMoveToPt = { 0, 0 };
1155        int numPoints = 0;
1156        int numVerbs = (rand.nextU() >> 16) % 10;
1157        int numIterVerbs = 0;
1158        for (int j = 0; j < numVerbs; ++j) {
1159            do {
1160                nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
1161            } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
1162            switch (nextVerb) {
1163                case SkPath::kMove_Verb:
1164                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1165                    p.moveTo(expectedPts[numPoints]);
1166                    lastMoveToPt = expectedPts[numPoints];
1167                    numPoints += 1;
1168                    lastWasClose = false;
1169                    haveMoveTo = true;
1170                    break;
1171                case SkPath::kLine_Verb:
1172                    if (!haveMoveTo) {
1173                        expectedPts[numPoints++] = lastMoveToPt;
1174                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1175                        haveMoveTo = true;
1176                    }
1177                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1178                    p.lineTo(expectedPts[numPoints]);
1179                    numPoints += 1;
1180                    lastWasClose = false;
1181                    break;
1182                case SkPath::kQuad_Verb:
1183                    if (!haveMoveTo) {
1184                        expectedPts[numPoints++] = lastMoveToPt;
1185                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1186                        haveMoveTo = true;
1187                    }
1188                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1189                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1190                    p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
1191                    numPoints += 2;
1192                    lastWasClose = false;
1193                    break;
1194                case SkPath::kCubic_Verb:
1195                    if (!haveMoveTo) {
1196                        expectedPts[numPoints++] = lastMoveToPt;
1197                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
1198                        haveMoveTo = true;
1199                    }
1200                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
1201                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
1202                    expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
1203                    p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
1204                              expectedPts[numPoints + 2]);
1205                    numPoints += 3;
1206                    lastWasClose = false;
1207                    break;
1208                case SkPath::kClose_Verb:
1209                    p.close();
1210                    haveMoveTo = false;
1211                    lastWasClose = true;
1212                    break;
1213                default:;
1214            }
1215            expectedVerbs[numIterVerbs++] = nextVerb;
1216        }
1217
1218        iter.setPath(p);
1219        numVerbs = numIterVerbs;
1220        numIterVerbs = 0;
1221        int numIterPts = 0;
1222        SkPoint lastMoveTo;
1223        SkPoint lastPt;
1224        lastMoveTo.set(0, 0);
1225        lastPt.set(0, 0);
1226        while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1227            REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1228            numIterVerbs++;
1229            switch (nextVerb) {
1230                case SkPath::kMove_Verb:
1231                    REPORTER_ASSERT(reporter, numIterPts < numPoints);
1232                    REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1233                    lastPt = lastMoveTo = pts[0];
1234                    numIterPts += 1;
1235                    break;
1236                case SkPath::kLine_Verb:
1237                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1238                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1239                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1240                    lastPt = pts[1];
1241                    numIterPts += 1;
1242                    break;
1243                case SkPath::kQuad_Verb:
1244                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1245                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1246                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1247                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1248                    lastPt = pts[2];
1249                    numIterPts += 2;
1250                    break;
1251                case SkPath::kCubic_Verb:
1252                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1253                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1254                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1255                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1256                    REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1257                    lastPt = pts[3];
1258                    numIterPts += 3;
1259                    break;
1260                case SkPath::kClose_Verb:
1261                    REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1262                    lastPt = lastMoveTo;
1263                    break;
1264                default:;
1265            }
1266        }
1267        REPORTER_ASSERT(reporter, numIterPts == numPoints);
1268        REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1269    }
1270}
1271
1272static void check_for_circle(skiatest::Reporter* reporter,
1273                             const SkPath& path, bool expected) {
1274    SkRect rect;
1275    REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
1276    if (expected) {
1277        REPORTER_ASSERT(reporter, rect.height() == rect.width());
1278    }
1279}
1280
1281static void test_circle_skew(skiatest::Reporter* reporter,
1282                             const SkPath& path) {
1283    SkPath tmp;
1284
1285    SkMatrix m;
1286    m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
1287    path.transform(m, &tmp);
1288    check_for_circle(reporter, tmp, false);
1289}
1290
1291static void test_circle_translate(skiatest::Reporter* reporter,
1292                                  const SkPath& path) {
1293    SkPath tmp;
1294
1295    // translate at small offset
1296    SkMatrix m;
1297    m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
1298    path.transform(m, &tmp);
1299    check_for_circle(reporter, tmp, true);
1300
1301    tmp.reset();
1302    m.reset();
1303
1304    // translate at a relatively big offset
1305    m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
1306    path.transform(m, &tmp);
1307    check_for_circle(reporter, tmp, true);
1308}
1309
1310static void test_circle_rotate(skiatest::Reporter* reporter,
1311                               const SkPath& path) {
1312    for (int angle = 0; angle < 360; ++angle) {
1313        SkPath tmp;
1314        SkMatrix m;
1315        m.setRotate(SkIntToScalar(angle));
1316        path.transform(m, &tmp);
1317
1318        // TODO: a rotated circle whose rotated angle is not a mutiple of 90
1319        // degrees is not an oval anymore, this can be improved.  we made this
1320        // for the simplicity of our implementation.
1321        if (angle % 90 == 0) {
1322            check_for_circle(reporter, tmp, true);
1323        } else {
1324            check_for_circle(reporter, tmp, false);
1325        }
1326    }
1327}
1328
1329static void test_circle_with_direction(skiatest::Reporter* reporter,
1330                                       SkPath::Direction dir) {
1331    SkPath path;
1332
1333    // circle at origin
1334    path.addCircle(0, 0, SkIntToScalar(20), dir);
1335    check_for_circle(reporter, path, true);
1336    test_circle_rotate(reporter, path);
1337    test_circle_translate(reporter, path);
1338    test_circle_skew(reporter, path);
1339
1340    // circle at an offset at (10, 10)
1341    path.reset();
1342    path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
1343                   SkIntToScalar(20), dir);
1344    check_for_circle(reporter, path, true);
1345    test_circle_rotate(reporter, path);
1346    test_circle_translate(reporter, path);
1347    test_circle_skew(reporter, path);
1348}
1349
1350static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
1351    SkPath path;
1352    SkPath circle;
1353    SkPath rect;
1354    SkPath empty;
1355
1356    circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1357    rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
1358                 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
1359
1360    SkMatrix translate;
1361    translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
1362
1363    // For simplicity, all the path concatenation related operations
1364    // would mark it non-circle, though in theory it's still a circle.
1365
1366    // empty + circle (translate)
1367    path = empty;
1368    path.addPath(circle, translate);
1369    check_for_circle(reporter, path, false);
1370
1371    // circle + empty (translate)
1372    path = circle;
1373    path.addPath(empty, translate);
1374    check_for_circle(reporter, path, false);
1375
1376    // test reverseAddPath
1377    path = circle;
1378    path.reverseAddPath(rect);
1379    check_for_circle(reporter, path, false);
1380}
1381
1382static void test_circle(skiatest::Reporter* reporter) {
1383    test_circle_with_direction(reporter, SkPath::kCW_Direction);
1384    test_circle_with_direction(reporter, SkPath::kCCW_Direction);
1385
1386    // multiple addCircle()
1387    SkPath path;
1388    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1389    path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
1390    check_for_circle(reporter, path, false);
1391
1392    // some extra lineTo() would make isOval() fail
1393    path.reset();
1394    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1395    path.lineTo(0, 0);
1396    check_for_circle(reporter, path, false);
1397
1398    // not back to the original point
1399    path.reset();
1400    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
1401    path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
1402    check_for_circle(reporter, path, false);
1403
1404    test_circle_with_add_paths(reporter);
1405}
1406
1407static void test_oval(skiatest::Reporter* reporter) {
1408    SkRect rect;
1409    SkMatrix m;
1410    SkPath path;
1411
1412    rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
1413    path.addOval(rect);
1414
1415    REPORTER_ASSERT(reporter, path.isOval(NULL));
1416
1417    m.setRotate(SkIntToScalar(90));
1418    SkPath tmp;
1419    path.transform(m, &tmp);
1420    // an oval rotated 90 degrees is still an oval.
1421    REPORTER_ASSERT(reporter, tmp.isOval(NULL));
1422
1423    m.reset();
1424    m.setRotate(SkIntToScalar(30));
1425    tmp.reset();
1426    path.transform(m, &tmp);
1427    // an oval rotated 30 degrees is not an oval anymore.
1428    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1429
1430    // since empty path being transformed.
1431    path.reset();
1432    tmp.reset();
1433    m.reset();
1434    path.transform(m, &tmp);
1435    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1436
1437    // empty path is not an oval
1438    tmp.reset();
1439    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1440
1441    // only has moveTo()s
1442    tmp.reset();
1443    tmp.moveTo(0, 0);
1444    tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
1445    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
1446
1447    // mimic WebKit's calling convention,
1448    // call moveTo() first and then call addOval()
1449    path.reset();
1450    path.moveTo(0, 0);
1451    path.addOval(rect);
1452    REPORTER_ASSERT(reporter, path.isOval(NULL));
1453
1454    // copy path
1455    path.reset();
1456    tmp.reset();
1457    tmp.addOval(rect);
1458    path = tmp;
1459    REPORTER_ASSERT(reporter, path.isOval(NULL));
1460}
1461
1462static void TestPath(skiatest::Reporter* reporter) {
1463    {
1464        SkSize size;
1465        size.fWidth = 3.4f;
1466        size.width();
1467        size = SkSize::Make(3,4);
1468        SkISize isize = SkISize::Make(3,4);
1469    }
1470
1471    SkTSize<SkScalar>::Make(3,4);
1472
1473    SkPath  p, p2;
1474    SkRect  bounds, bounds2;
1475
1476    REPORTER_ASSERT(reporter, p.isEmpty());
1477    REPORTER_ASSERT(reporter, 0 == p.countPoints());
1478    REPORTER_ASSERT(reporter, 0 == p.countVerbs());
1479    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1480    REPORTER_ASSERT(reporter, p.isConvex());
1481    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1482    REPORTER_ASSERT(reporter, !p.isInverseFillType());
1483    REPORTER_ASSERT(reporter, p == p2);
1484    REPORTER_ASSERT(reporter, !(p != p2));
1485
1486    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
1487
1488    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
1489
1490    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1491    check_convex_bounds(reporter, p, bounds);
1492    // we have quads or cubics
1493    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
1494    REPORTER_ASSERT(reporter, !p.isEmpty());
1495
1496    p.reset();
1497    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1498    REPORTER_ASSERT(reporter, p.isEmpty());
1499
1500    p.addOval(bounds);
1501    check_convex_bounds(reporter, p, bounds);
1502    REPORTER_ASSERT(reporter, !p.isEmpty());
1503
1504    p.reset();
1505    p.addRect(bounds);
1506    check_convex_bounds(reporter, p, bounds);
1507    // we have only lines
1508    REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
1509    REPORTER_ASSERT(reporter, !p.isEmpty());
1510
1511    REPORTER_ASSERT(reporter, p != p2);
1512    REPORTER_ASSERT(reporter, !(p == p2));
1513
1514    // do getPoints and getVerbs return the right result
1515    REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
1516    REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
1517    SkPoint pts[4];
1518    int count = p.getPoints(pts, 4);
1519    REPORTER_ASSERT(reporter, count == 4);
1520    uint8_t verbs[6];
1521    verbs[5] = 0xff;
1522    p.getVerbs(verbs, 5);
1523    REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
1524    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
1525    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
1526    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
1527    REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
1528    REPORTER_ASSERT(reporter, 0xff == verbs[5]);
1529    bounds2.set(pts, 4);
1530    REPORTER_ASSERT(reporter, bounds == bounds2);
1531
1532    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1533    p.offset(SK_Scalar1*3, SK_Scalar1*4);
1534    REPORTER_ASSERT(reporter, bounds == p.getBounds());
1535
1536    REPORTER_ASSERT(reporter, p.isRect(NULL));
1537    bounds2.setEmpty();
1538    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1539    REPORTER_ASSERT(reporter, bounds == bounds2);
1540
1541    // now force p to not be a rect
1542    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1543    p.addRect(bounds);
1544    REPORTER_ASSERT(reporter, !p.isRect(NULL));
1545
1546    test_isLine(reporter);
1547    test_isRect(reporter);
1548    test_zero_length_paths(reporter);
1549    test_direction(reporter);
1550    test_convexity(reporter);
1551    test_convexity2(reporter);
1552    test_close(reporter);
1553    test_segment_masks(reporter);
1554    test_flattening(reporter);
1555    test_transform(reporter);
1556    test_bounds(reporter);
1557    test_iter(reporter);
1558    test_raw_iter(reporter);
1559    test_circle(reporter);
1560    test_oval(reporter);
1561    test_strokerec(reporter);
1562    test_addPoly(reporter);
1563    test_isfinite(reporter);
1564}
1565
1566#include "TestClassDef.h"
1567DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
1568