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 "SkRandom.h"
14#include "SkReader32.h"
15#include "SkSize.h"
16#include "SkWriter32.h"
17
18/**
19 * cheapIsDirection can take a shortcut when a path is marked convex.
20 * This function ensures that we always test cheapIsDirection when the path
21 * is flagged with unknown convexity status.
22 */
23static void check_direction(SkPath* path,
24                            SkPath::Direction expectedDir,
25                            skiatest::Reporter* reporter) {
26    if (SkPath::kConvex_Convexity == path->getConvexity()) {
27        REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
28        path->setConvexity(SkPath::kUnknown_Convexity);
29    }
30    REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
31}
32
33static void test_direction(skiatest::Reporter* reporter) {
34    size_t i;
35    SkPath path;
36    REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
37    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
38    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
39
40    static const char* gDegen[] = {
41        "M 10 10",
42        "M 10 10 M 20 20",
43        "M 10 10 L 20 20",
44        "M 10 10 L 10 10 L 10 10",
45        "M 10 10 Q 10 10 10 10",
46        "M 10 10 C 10 10 10 10 10 10",
47    };
48    for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
49        path.reset();
50        bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
51        REPORTER_ASSERT(reporter, valid);
52        REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
53    }
54
55    static const char* gCW[] = {
56        "M 10 10 L 10 10 Q 20 10 20 20",
57        "M 10 10 C 20 10 20 20 20 20",
58        "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
59    };
60    for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
61        path.reset();
62        bool valid = SkParsePath::FromSVGString(gCW[i], &path);
63        REPORTER_ASSERT(reporter, valid);
64        check_direction(&path, SkPath::kCW_Direction, reporter);
65    }
66
67    static const char* gCCW[] = {
68        "M 10 10 L 10 10 Q 20 10 20 -20",
69        "M 10 10 C 20 10 20 -20 20 -20",
70        "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
71    };
72    for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
73        path.reset();
74        bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
75        REPORTER_ASSERT(reporter, valid);
76        check_direction(&path, SkPath::kCCW_Direction, reporter);
77    }
78
79    // Test two donuts, each wound a different direction. Only the outer contour
80    // determines the cheap direction
81    path.reset();
82    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
83    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
84    check_direction(&path, SkPath::kCW_Direction, reporter);
85
86    path.reset();
87    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
88    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
89    check_direction(&path, SkPath::kCCW_Direction, reporter);
90
91#ifdef SK_SCALAR_IS_FLOAT
92    // triangle with one point really far from the origin.
93    path.reset();
94    // the first point is roughly 1.05e10, 1.05e10
95    path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
96    path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
97    path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
98    check_direction(&path, SkPath::kCCW_Direction, reporter);
99#endif
100}
101
102static void add_rect(SkPath* path, const SkRect& r) {
103    path->moveTo(r.fLeft, r.fTop);
104    path->lineTo(r.fRight, r.fTop);
105    path->lineTo(r.fRight, r.fBottom);
106    path->lineTo(r.fLeft, r.fBottom);
107    path->close();
108}
109
110static void test_bounds(skiatest::Reporter* reporter) {
111    static const SkRect rects[] = {
112        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
113        { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
114        { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
115        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
116    };
117
118    SkPath path0, path1;
119    for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
120        path0.addRect(rects[i]);
121        add_rect(&path1, rects[i]);
122    }
123
124    REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
125}
126
127static void stroke_cubic(const SkPoint pts[4]) {
128    SkPath path;
129    path.moveTo(pts[0]);
130    path.cubicTo(pts[1], pts[2], pts[3]);
131
132    SkPaint paint;
133    paint.setStyle(SkPaint::kStroke_Style);
134    paint.setStrokeWidth(SK_Scalar1 * 2);
135
136    SkPath fill;
137    paint.getFillPath(path, &fill);
138}
139
140// just ensure this can run w/o any SkASSERTS firing in the debug build
141// we used to assert due to differences in how we determine a degenerate vector
142// but that was fixed with the introduction of SkPoint::CanNormalize
143static void stroke_tiny_cubic() {
144    SkPoint p0[] = {
145        { 372.0f,   92.0f },
146        { 372.0f,   92.0f },
147        { 372.0f,   92.0f },
148        { 372.0f,   92.0f },
149    };
150
151    stroke_cubic(p0);
152
153    SkPoint p1[] = {
154        { 372.0f,       92.0f },
155        { 372.0007f,    92.000755f },
156        { 371.99927f,   92.003922f },
157        { 371.99826f,   92.003899f },
158    };
159
160    stroke_cubic(p1);
161}
162
163static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
164    for (int i = 0; i < 2; ++i) {
165        SkPath::Iter iter(path, (bool)i);
166        SkPoint mv;
167        SkPoint pts[4];
168        SkPath::Verb v;
169        int nMT = 0;
170        int nCL = 0;
171        mv.set(0, 0);
172        while (SkPath::kDone_Verb != (v = iter.next(pts))) {
173            switch (v) {
174                case SkPath::kMove_Verb:
175                    mv = pts[0];
176                    ++nMT;
177                    break;
178                case SkPath::kClose_Verb:
179                    REPORTER_ASSERT(reporter, mv == pts[0]);
180                    ++nCL;
181                    break;
182                default:
183                    break;
184            }
185        }
186        // if we force a close on the interator we should have a close
187        // for every moveTo
188        REPORTER_ASSERT(reporter, !i || nMT == nCL);
189    }
190}
191
192static void test_close(skiatest::Reporter* reporter) {
193    SkPath closePt;
194    closePt.moveTo(0, 0);
195    closePt.close();
196    check_close(reporter, closePt);
197
198    SkPath openPt;
199    openPt.moveTo(0, 0);
200    check_close(reporter, openPt);
201
202    SkPath empty;
203    check_close(reporter, empty);
204    empty.close();
205    check_close(reporter, empty);
206
207    SkPath rect;
208    rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
209    check_close(reporter, rect);
210    rect.close();
211    check_close(reporter, rect);
212
213    SkPath quad;
214    quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
215    check_close(reporter, quad);
216    quad.close();
217    check_close(reporter, quad);
218
219    SkPath cubic;
220    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
221                 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
222    check_close(reporter, cubic);
223    cubic.close();
224    check_close(reporter, cubic);
225
226    SkPath line;
227    line.moveTo(SK_Scalar1, SK_Scalar1);
228    line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
229    check_close(reporter, line);
230    line.close();
231    check_close(reporter, line);
232
233    SkPath rect2;
234    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
235    rect2.close();
236    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
237    check_close(reporter, rect2);
238    rect2.close();
239    check_close(reporter, rect2);
240
241    SkPath oval3;
242    oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
243    oval3.close();
244    oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
245    check_close(reporter, oval3);
246    oval3.close();
247    check_close(reporter, oval3);
248
249    SkPath moves;
250    moves.moveTo(SK_Scalar1, SK_Scalar1);
251    moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
252    moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
253    moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
254    check_close(reporter, moves);
255
256    stroke_tiny_cubic();
257}
258
259static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
260                            SkPath::Convexity expected) {
261    SkPath::Convexity c = SkPath::ComputeConvexity(path);
262    REPORTER_ASSERT(reporter, c == expected);
263}
264
265static void test_convexity2(skiatest::Reporter* reporter) {
266    SkPath pt;
267    pt.moveTo(0, 0);
268    pt.close();
269    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
270
271    SkPath line;
272    line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
273    line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
274    line.close();
275    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
276
277    SkPath triLeft;
278    triLeft.moveTo(0, 0);
279    triLeft.lineTo(SK_Scalar1, 0);
280    triLeft.lineTo(SK_Scalar1, SK_Scalar1);
281    triLeft.close();
282    check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
283
284    SkPath triRight;
285    triRight.moveTo(0, 0);
286    triRight.lineTo(-SK_Scalar1, 0);
287    triRight.lineTo(SK_Scalar1, SK_Scalar1);
288    triRight.close();
289    check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
290
291    SkPath square;
292    square.moveTo(0, 0);
293    square.lineTo(SK_Scalar1, 0);
294    square.lineTo(SK_Scalar1, SK_Scalar1);
295    square.lineTo(0, SK_Scalar1);
296    square.close();
297    check_convexity(reporter, square, SkPath::kConvex_Convexity);
298
299    SkPath redundantSquare;
300    redundantSquare.moveTo(0, 0);
301    redundantSquare.lineTo(0, 0);
302    redundantSquare.lineTo(0, 0);
303    redundantSquare.lineTo(SK_Scalar1, 0);
304    redundantSquare.lineTo(SK_Scalar1, 0);
305    redundantSquare.lineTo(SK_Scalar1, 0);
306    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
307    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
308    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
309    redundantSquare.lineTo(0, SK_Scalar1);
310    redundantSquare.lineTo(0, SK_Scalar1);
311    redundantSquare.lineTo(0, SK_Scalar1);
312    redundantSquare.close();
313    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
314
315    SkPath bowTie;
316    bowTie.moveTo(0, 0);
317    bowTie.lineTo(0, 0);
318    bowTie.lineTo(0, 0);
319    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
320    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
321    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
322    bowTie.lineTo(SK_Scalar1, 0);
323    bowTie.lineTo(SK_Scalar1, 0);
324    bowTie.lineTo(SK_Scalar1, 0);
325    bowTie.lineTo(0, SK_Scalar1);
326    bowTie.lineTo(0, SK_Scalar1);
327    bowTie.lineTo(0, SK_Scalar1);
328    bowTie.close();
329    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
330
331    SkPath spiral;
332    spiral.moveTo(0, 0);
333    spiral.lineTo(100*SK_Scalar1, 0);
334    spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
335    spiral.lineTo(0, 100*SK_Scalar1);
336    spiral.lineTo(0, 50*SK_Scalar1);
337    spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
338    spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
339    spiral.close();
340    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
341
342    SkPath dent;
343    dent.moveTo(0, 0);
344    dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
345    dent.lineTo(0, 100*SK_Scalar1);
346    dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
347    dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
348    dent.close();
349    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
350}
351
352static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
353                                const SkRect& bounds) {
354    REPORTER_ASSERT(reporter, p.isConvex());
355    REPORTER_ASSERT(reporter, p.getBounds() == bounds);
356
357    SkPath p2(p);
358    REPORTER_ASSERT(reporter, p2.isConvex());
359    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
360
361    SkPath other;
362    other.swap(p2);
363    REPORTER_ASSERT(reporter, other.isConvex());
364    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
365}
366
367static void setFromString(SkPath* path, const char str[]) {
368    bool first = true;
369    while (str) {
370        SkScalar x, y;
371        str = SkParse::FindScalar(str, &x);
372        if (NULL == str) {
373            break;
374        }
375        str = SkParse::FindScalar(str, &y);
376        SkASSERT(str);
377        if (first) {
378            path->moveTo(x, y);
379            first = false;
380        } else {
381            path->lineTo(x, y);
382        }
383    }
384}
385
386static void test_convexity(skiatest::Reporter* reporter) {
387    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
388    static const SkPath::Convexity V = SkPath::kConvex_Convexity;
389
390    SkPath path;
391
392    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
393    path.addCircle(0, 0, SkIntToScalar(10));
394    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
395    path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
396    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
397    path.reset();
398    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
399    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
400    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
401    path.reset();
402    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
403    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
404    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
405
406    static const struct {
407        const char*         fPathStr;
408        SkPath::Convexity   fExpectedConvexity;
409    } gRec[] = {
410        { "", SkPath::kConvex_Convexity },
411        { "0 0", SkPath::kConvex_Convexity },
412        { "0 0 10 10", SkPath::kConvex_Convexity },
413        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
414        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
415        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
416        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
417        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
418    };
419
420    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
421        SkPath path;
422        setFromString(&path, gRec[i].fPathStr);
423        SkPath::Convexity c = SkPath::ComputeConvexity(path);
424        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
425    }
426}
427
428// Simple isRect test is inline TestPath, below.
429// test_isRect provides more extensive testing.
430static void test_isRect(skiatest::Reporter* reporter) {
431    // passing tests (all moveTo / lineTo...
432    SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
433    SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
434    SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
435    SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
436    SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
437    SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
438    SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
439    SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
440    SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
441    SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
442        {1, 0}, {.5f, 0}};
443    SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
444        {0, 1}, {0, .5f}};
445    SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
446    SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
447    SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
448
449    // failing tests
450    SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
451    SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
452    SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
453    SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
454    SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
455    SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
456    SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
457    SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
458
459    // failing, no close
460    SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
461    SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
462
463    size_t testLen[] = {
464        sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
465        sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
466        sizeof(rd), sizeof(re),
467        sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
468        sizeof(f7), sizeof(f8),
469        sizeof(c1), sizeof(c2)
470    };
471    SkPoint* tests[] = {
472        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
473        f1, f2, f3, f4, f5, f6, f7, f8,
474        c1, c2
475    };
476    SkPoint* lastPass = re;
477    SkPoint* lastClose = f8;
478    bool fail = false;
479    bool close = true;
480    const size_t testCount = sizeof(tests) / sizeof(tests[0]);
481    size_t index;
482    for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
483        SkPath path;
484        path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
485        for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
486            path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
487        }
488        if (close) {
489            path.close();
490        }
491        REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
492        if (tests[testIndex] == lastPass) {
493            fail = true;
494        }
495        if (tests[testIndex] == lastClose) {
496            close = false;
497        }
498    }
499
500    // fail, close then line
501    SkPath path1;
502    path1.moveTo(r1[0].fX, r1[0].fY);
503    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
504        path1.lineTo(r1[index].fX, r1[index].fY);
505    }
506    path1.close();
507    path1.lineTo(1, 0);
508    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
509
510    // fail, move in the middle
511    path1.reset();
512    path1.moveTo(r1[0].fX, r1[0].fY);
513    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
514        if (index == 2) {
515            path1.moveTo(1, .5f);
516        }
517        path1.lineTo(r1[index].fX, r1[index].fY);
518    }
519    path1.close();
520    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
521
522    // fail, move on the edge
523    path1.reset();
524    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
525        path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
526        path1.lineTo(r1[index].fX, r1[index].fY);
527    }
528    path1.close();
529    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
530
531    // fail, quad
532    path1.reset();
533    path1.moveTo(r1[0].fX, r1[0].fY);
534    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
535        if (index == 2) {
536            path1.quadTo(1, .5f, 1, .5f);
537        }
538        path1.lineTo(r1[index].fX, r1[index].fY);
539    }
540    path1.close();
541    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
542
543    // fail, cubic
544    path1.reset();
545    path1.moveTo(r1[0].fX, r1[0].fY);
546    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
547        if (index == 2) {
548            path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
549        }
550        path1.lineTo(r1[index].fX, r1[index].fY);
551    }
552    path1.close();
553    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
554}
555
556static void test_flattening(skiatest::Reporter* reporter) {
557    SkPath p;
558
559    static const SkPoint pts[] = {
560        { 0, 0 },
561        { SkIntToScalar(10), SkIntToScalar(10) },
562        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
563        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
564    };
565    p.moveTo(pts[0]);
566    p.lineTo(pts[1]);
567    p.quadTo(pts[2], pts[3]);
568    p.cubicTo(pts[4], pts[5], pts[6]);
569
570    SkWriter32 writer(100);
571    p.flatten(writer);
572    size_t size = writer.size();
573    SkAutoMalloc storage(size);
574    writer.flatten(storage.get());
575    SkReader32 reader(storage.get(), size);
576
577    SkPath p1;
578    REPORTER_ASSERT(reporter, p1 != p);
579    p1.unflatten(reader);
580    REPORTER_ASSERT(reporter, p1 == p);
581}
582
583static void test_transform(skiatest::Reporter* reporter) {
584    SkPath p, p1;
585
586    static const SkPoint pts[] = {
587        { 0, 0 },
588        { SkIntToScalar(10), SkIntToScalar(10) },
589        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
590        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
591    };
592    p.moveTo(pts[0]);
593    p.lineTo(pts[1]);
594    p.quadTo(pts[2], pts[3]);
595    p.cubicTo(pts[4], pts[5], pts[6]);
596
597    SkMatrix matrix;
598    matrix.reset();
599    p.transform(matrix, &p1);
600    REPORTER_ASSERT(reporter, p == p1);
601
602    matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
603    p.transform(matrix, &p1);
604    SkPoint pts1[7];
605    int count = p1.getPoints(pts1, 7);
606    REPORTER_ASSERT(reporter, 7 == count);
607    for (int i = 0; i < count; ++i) {
608        SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
609        REPORTER_ASSERT(reporter, newPt == pts1[i]);
610    }
611}
612
613static void test_zero_length_paths(skiatest::Reporter* reporter) {
614    SkPath  p;
615    SkPoint pt;
616    SkRect  bounds;
617
618    // Lone moveTo case
619    p.moveTo(SK_Scalar1, SK_Scalar1);
620    REPORTER_ASSERT(reporter, !p.isEmpty());
621    REPORTER_ASSERT(reporter, 1 == p.countPoints());
622    p.getLastPt(&pt);
623    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
624    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
625    bounds.set(0, 0, 0, 0);
626    REPORTER_ASSERT(reporter, bounds == p.getBounds());
627
628    // MoveTo-MoveTo case
629    p.moveTo(SK_Scalar1*2, SK_Scalar1);
630    REPORTER_ASSERT(reporter, !p.isEmpty());
631    REPORTER_ASSERT(reporter, 2 == p.countPoints());
632    p.getLastPt(&pt);
633    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
634    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
635    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
636    REPORTER_ASSERT(reporter, bounds == p.getBounds());
637
638    // moveTo-close case
639    p.reset();
640    p.moveTo(SK_Scalar1, SK_Scalar1);
641    p.close();
642    bounds.set(0, 0, 0, 0);
643    REPORTER_ASSERT(reporter, !p.isEmpty());
644    REPORTER_ASSERT(reporter, 1 == p.countPoints());
645    REPORTER_ASSERT(reporter, bounds == p.getBounds());
646
647    // moveTo-close-moveTo-close case
648    p.moveTo(SK_Scalar1*2, SK_Scalar1);
649    p.close();
650    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
651    REPORTER_ASSERT(reporter, !p.isEmpty());
652    REPORTER_ASSERT(reporter, 2 == p.countPoints());
653    REPORTER_ASSERT(reporter, bounds == p.getBounds());
654
655    // moveTo-line case
656    p.reset();
657    p.moveTo(SK_Scalar1, SK_Scalar1);
658    p.lineTo(SK_Scalar1, SK_Scalar1);
659    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
660    REPORTER_ASSERT(reporter, !p.isEmpty());
661    REPORTER_ASSERT(reporter, 2 == p.countPoints());
662    REPORTER_ASSERT(reporter, bounds == p.getBounds());
663
664    // moveTo-lineTo-moveTo-lineTo case
665    p.moveTo(SK_Scalar1*2, SK_Scalar1);
666    p.lineTo(SK_Scalar1*2, SK_Scalar1);
667    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
668    REPORTER_ASSERT(reporter, !p.isEmpty());
669    REPORTER_ASSERT(reporter, 4 == p.countPoints());
670    REPORTER_ASSERT(reporter, bounds == p.getBounds());
671
672    // moveTo-line-close case
673    p.reset();
674    p.moveTo(SK_Scalar1, SK_Scalar1);
675    p.lineTo(SK_Scalar1, SK_Scalar1);
676    p.close();
677    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
678    REPORTER_ASSERT(reporter, !p.isEmpty());
679    REPORTER_ASSERT(reporter, 2 == p.countPoints());
680    REPORTER_ASSERT(reporter, bounds == p.getBounds());
681
682    // moveTo-line-close-moveTo-line-close case
683    p.moveTo(SK_Scalar1*2, SK_Scalar1);
684    p.lineTo(SK_Scalar1*2, SK_Scalar1);
685    p.close();
686    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
687    REPORTER_ASSERT(reporter, !p.isEmpty());
688    REPORTER_ASSERT(reporter, 4 == p.countPoints());
689    REPORTER_ASSERT(reporter, bounds == p.getBounds());
690
691    // moveTo-quadTo case
692    p.reset();
693    p.moveTo(SK_Scalar1, SK_Scalar1);
694    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
695    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
696    REPORTER_ASSERT(reporter, !p.isEmpty());
697    REPORTER_ASSERT(reporter, 3 == p.countPoints());
698    REPORTER_ASSERT(reporter, bounds == p.getBounds());
699
700    // moveTo-quadTo-close case
701    p.close();
702    REPORTER_ASSERT(reporter, !p.isEmpty());
703    REPORTER_ASSERT(reporter, 3 == p.countPoints());
704    REPORTER_ASSERT(reporter, bounds == p.getBounds());
705
706    // moveTo-quadTo-moveTo-quadTo case
707    p.reset();
708    p.moveTo(SK_Scalar1, SK_Scalar1);
709    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
710    p.moveTo(SK_Scalar1*2, SK_Scalar1);
711    p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
712    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
713    REPORTER_ASSERT(reporter, !p.isEmpty());
714    REPORTER_ASSERT(reporter, 6 == p.countPoints());
715    REPORTER_ASSERT(reporter, bounds == p.getBounds());
716
717    // moveTo-cubicTo case
718    p.reset();
719    p.moveTo(SK_Scalar1, SK_Scalar1);
720    p.cubicTo(SK_Scalar1, SK_Scalar1,
721              SK_Scalar1, SK_Scalar1,
722              SK_Scalar1, SK_Scalar1);
723    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
724    REPORTER_ASSERT(reporter, !p.isEmpty());
725    REPORTER_ASSERT(reporter, 4 == p.countPoints());
726    REPORTER_ASSERT(reporter, bounds == p.getBounds());
727
728    // moveTo-quadTo-close case
729    p.close();
730    REPORTER_ASSERT(reporter, !p.isEmpty());
731    REPORTER_ASSERT(reporter, 4 == p.countPoints());
732    REPORTER_ASSERT(reporter, bounds == p.getBounds());
733
734    // moveTo-quadTo-moveTo-quadTo case
735    p.reset();
736    p.moveTo(SK_Scalar1, SK_Scalar1);
737    p.cubicTo(SK_Scalar1, SK_Scalar1,
738              SK_Scalar1, SK_Scalar1,
739              SK_Scalar1, SK_Scalar1);
740    p.moveTo(SK_Scalar1*2, SK_Scalar1);
741    p.cubicTo(SK_Scalar1*2, SK_Scalar1,
742              SK_Scalar1*2, SK_Scalar1,
743              SK_Scalar1*2, SK_Scalar1);
744    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
745    REPORTER_ASSERT(reporter, !p.isEmpty());
746    REPORTER_ASSERT(reporter, 8 == p.countPoints());
747    REPORTER_ASSERT(reporter, bounds == p.getBounds());
748}
749
750struct SegmentInfo {
751    SkPath fPath;
752    int    fPointCount;
753};
754
755#define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
756
757static void test_segment_masks(skiatest::Reporter* reporter) {
758    SkPath p;
759    p.moveTo(0, 0);
760    p.quadTo(100, 100, 200, 200);
761    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
762    REPORTER_ASSERT(reporter, !p.isEmpty());
763    p.cubicTo(100, 100, 200, 200, 300, 300);
764    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
765    REPORTER_ASSERT(reporter, !p.isEmpty());
766    p.reset();
767    p.moveTo(0, 0);
768    p.cubicTo(100, 100, 200, 200, 300, 300);
769    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
770    REPORTER_ASSERT(reporter, !p.isEmpty());
771}
772
773static void test_iter(skiatest::Reporter* reporter) {
774    SkPath p;
775    SkPoint pts[4];
776
777    // Test an iterator with no path
778    SkPath::Iter noPathIter;
779    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
780    // Test that setting an empty path works
781    noPathIter.setPath(p, false);
782    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
783    // Test that close path makes no difference for an empty path
784    noPathIter.setPath(p, true);
785    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
786
787    // Test an iterator with an initial empty path
788    SkPath::Iter iter(p, false);
789    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
790
791    // Test that close path makes no difference
792    SkPath::Iter forceCloseIter(p, true);
793    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
794
795    // Test that a move-only path produces nothing when iterated.
796    p.moveTo(SK_Scalar1, 0);
797    iter.setPath(p, false);
798    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
799
800    // No matter how many moves we add, we should still get nothing back.
801    p.moveTo(SK_Scalar1*2, 0);
802    p.moveTo(SK_Scalar1*3, 0);
803    p.moveTo(SK_Scalar1*4, 0);
804    p.moveTo(SK_Scalar1*5, 0);
805    iter.setPath(p, false);
806    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
807
808    // Nor should force closing
809    forceCloseIter.setPath(p, true);
810    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
811
812    // Initial closes should be ignored
813    p.reset();
814    p.close();
815    iter.setPath(p, false);
816    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
817    // Even if force closed
818    forceCloseIter.setPath(p, true);
819    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
820
821    // Move/close sequences should also be ignored
822    p.reset();
823    p.close();
824    p.moveTo(SK_Scalar1, 0);
825    p.close();
826    p.close();
827    p.moveTo(SK_Scalar1*2, 0);
828    p.close();
829    p.moveTo(SK_Scalar1*3, 0);
830    p.moveTo(SK_Scalar1*4, 0);
831    p.close();
832    iter.setPath(p, false);
833    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
834    // Even if force closed
835    forceCloseIter.setPath(p, true);
836    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
837
838    // The GM degeneratesegments.cpp test is more extensive
839}
840
841static void test_raw_iter(skiatest::Reporter* reporter) {
842    SkPath p;
843    SkPoint pts[4];
844
845    // Test an iterator with no path
846    SkPath::RawIter noPathIter;
847    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
848    // Test that setting an empty path works
849    noPathIter.setPath(p);
850    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
851
852    // Test an iterator with an initial empty path
853    SkPath::RawIter iter(p);
854    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
855
856    // Test that a move-only path returns the move.
857    p.moveTo(SK_Scalar1, 0);
858    iter.setPath(p);
859    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
860    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
861    REPORTER_ASSERT(reporter, pts[0].fY == 0);
862    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
863
864    // No matter how many moves we add, we should get them all back
865    p.moveTo(SK_Scalar1*2, SK_Scalar1);
866    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
867    iter.setPath(p);
868    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
869    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
870    REPORTER_ASSERT(reporter, pts[0].fY == 0);
871    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
872    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
873    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
874    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
875    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
876    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
877    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
878
879    // Initial close is never ever stored
880    p.reset();
881    p.close();
882    iter.setPath(p);
883    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
884
885    // Move/close sequences
886    p.reset();
887    p.close(); // Not stored, no purpose
888    p.moveTo(SK_Scalar1, 0);
889    p.close();
890    p.close(); // Not stored, no purpose
891    p.moveTo(SK_Scalar1*2, SK_Scalar1);
892    p.close();
893    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
894    p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
895    p.close();
896    iter.setPath(p);
897    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
898    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
899    REPORTER_ASSERT(reporter, pts[0].fY == 0);
900    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
901    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
902    REPORTER_ASSERT(reporter, pts[0].fY == 0);
903    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
904    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
905    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
906    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
907    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
908    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
909    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
910    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
911    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
912    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
913    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
914    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
915    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
916    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
917    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
918    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
919
920    // Generate random paths and verify
921    SkPoint randomPts[25];
922    for (int i = 0; i < 5; ++i) {
923        for (int j = 0; j < 5; ++j) {
924            randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
925        }
926    }
927
928    // Max of 10 segments, max 3 points per segment
929    SkRandom rand(9876543);
930    SkPoint          expectedPts[31]; // May have leading moveTo
931    SkPath::Verb     expectedVerbs[22]; // May have leading moveTo
932    SkPath::Verb     nextVerb;
933
934    for (int i = 0; i < 500; ++i) {
935        p.reset();
936        bool lastWasClose = true;
937        bool haveMoveTo = false;
938        SkPoint lastMoveToPt = { 0, 0 };
939        int numPoints = 0;
940        int numVerbs = (rand.nextU() >> 16) % 10;
941        int numIterVerbs = 0;
942        for (int j = 0; j < numVerbs; ++j) {
943            do {
944                nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
945            } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
946            int numRequiredPts;
947            switch (nextVerb) {
948                case SkPath::kMove_Verb:
949                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
950                    p.moveTo(expectedPts[numPoints]);
951                    lastMoveToPt = expectedPts[numPoints];
952                    numPoints += 1;
953                    lastWasClose = false;
954                    haveMoveTo = true;
955                    break;
956                case SkPath::kLine_Verb:
957                    if (!haveMoveTo) {
958                        expectedPts[numPoints++] = lastMoveToPt;
959                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
960                        haveMoveTo = true;
961                    }
962                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
963                    p.lineTo(expectedPts[numPoints]);
964                    numPoints += 1;
965                    lastWasClose = false;
966                    break;
967                case SkPath::kQuad_Verb:
968                    if (!haveMoveTo) {
969                        expectedPts[numPoints++] = lastMoveToPt;
970                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
971                        haveMoveTo = true;
972                    }
973                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
974                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
975                    p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
976                    numPoints += 2;
977                    lastWasClose = false;
978                    break;
979                case SkPath::kCubic_Verb:
980                    if (!haveMoveTo) {
981                        expectedPts[numPoints++] = lastMoveToPt;
982                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
983                        haveMoveTo = true;
984                    }
985                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
986                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
987                    expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
988                    p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
989                              expectedPts[numPoints + 2]);
990                    numPoints += 3;
991                    lastWasClose = false;
992                    break;
993                case SkPath::kClose_Verb:
994                    p.close();
995                    haveMoveTo = false;
996                    lastWasClose = true;
997                    break;
998                default:;
999            }
1000            expectedVerbs[numIterVerbs++] = nextVerb;
1001        }
1002
1003        iter.setPath(p);
1004        numVerbs = numIterVerbs;
1005        numIterVerbs = 0;
1006        int numIterPts = 0;
1007        SkPoint lastMoveTo;
1008        SkPoint lastPt;
1009        lastMoveTo.set(0, 0);
1010        lastPt.set(0, 0);
1011        while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
1012            REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
1013            numIterVerbs++;
1014            switch (nextVerb) {
1015                case SkPath::kMove_Verb:
1016                    REPORTER_ASSERT(reporter, numIterPts < numPoints);
1017                    REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
1018                    lastPt = lastMoveTo = pts[0];
1019                    numIterPts += 1;
1020                    break;
1021                case SkPath::kLine_Verb:
1022                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
1023                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1024                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1025                    lastPt = pts[1];
1026                    numIterPts += 1;
1027                    break;
1028                case SkPath::kQuad_Verb:
1029                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
1030                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1031                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1032                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1033                    lastPt = pts[2];
1034                    numIterPts += 2;
1035                    break;
1036                case SkPath::kCubic_Verb:
1037                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
1038                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
1039                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
1040                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
1041                    REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
1042                    lastPt = pts[3];
1043                    numIterPts += 3;
1044                    break;
1045                case SkPath::kClose_Verb:
1046                    REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
1047                    lastPt = lastMoveTo;
1048                    break;
1049                default:;
1050            }
1051        }
1052        REPORTER_ASSERT(reporter, numIterPts == numPoints);
1053        REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
1054    }
1055}
1056
1057void TestPath(skiatest::Reporter* reporter);
1058void TestPath(skiatest::Reporter* reporter) {
1059    {
1060        SkSize size;
1061        size.fWidth = 3.4f;
1062        size.width();
1063        size = SkSize::Make(3,4);
1064        SkISize isize = SkISize::Make(3,4);
1065    }
1066
1067    SkTSize<SkScalar>::Make(3,4);
1068
1069    SkPath  p, p2;
1070    SkRect  bounds, bounds2;
1071
1072    REPORTER_ASSERT(reporter, p.isEmpty());
1073    REPORTER_ASSERT(reporter, 0 == p.countPoints());
1074    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1075    REPORTER_ASSERT(reporter, p.isConvex());
1076    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
1077    REPORTER_ASSERT(reporter, !p.isInverseFillType());
1078    REPORTER_ASSERT(reporter, p == p2);
1079    REPORTER_ASSERT(reporter, !(p != p2));
1080
1081    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
1082
1083    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
1084
1085    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
1086    check_convex_bounds(reporter, p, bounds);
1087    // we have quads or cubics
1088    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
1089    REPORTER_ASSERT(reporter, !p.isEmpty());
1090
1091    p.reset();
1092    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
1093    REPORTER_ASSERT(reporter, p.isEmpty());
1094
1095    p.addOval(bounds);
1096    check_convex_bounds(reporter, p, bounds);
1097    REPORTER_ASSERT(reporter, !p.isEmpty());
1098
1099    p.reset();
1100    p.addRect(bounds);
1101    check_convex_bounds(reporter, p, bounds);
1102    // we have only lines
1103    REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
1104    REPORTER_ASSERT(reporter, !p.isEmpty());
1105
1106    REPORTER_ASSERT(reporter, p != p2);
1107    REPORTER_ASSERT(reporter, !(p == p2));
1108
1109    // does getPoints return the right result
1110    REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
1111    SkPoint pts[4];
1112    int count = p.getPoints(pts, 4);
1113    REPORTER_ASSERT(reporter, count == 4);
1114    bounds2.set(pts, 4);
1115    REPORTER_ASSERT(reporter, bounds == bounds2);
1116
1117    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
1118    p.offset(SK_Scalar1*3, SK_Scalar1*4);
1119    REPORTER_ASSERT(reporter, bounds == p.getBounds());
1120
1121    REPORTER_ASSERT(reporter, p.isRect(NULL));
1122    bounds2.setEmpty();
1123    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
1124    REPORTER_ASSERT(reporter, bounds == bounds2);
1125
1126    // now force p to not be a rect
1127    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
1128    p.addRect(bounds);
1129    REPORTER_ASSERT(reporter, !p.isRect(NULL));
1130    test_isRect(reporter);
1131
1132    test_zero_length_paths(reporter);
1133    test_direction(reporter);
1134    test_convexity(reporter);
1135    test_convexity2(reporter);
1136    test_close(reporter);
1137    test_segment_masks(reporter);
1138    test_flattening(reporter);
1139    test_transform(reporter);
1140    test_bounds(reporter);
1141    test_iter(reporter);
1142    test_raw_iter(reporter);
1143}
1144
1145#include "TestClassDef.h"
1146DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
1147