PathTest.cpp revision 4da06ab3351f2a96f9216d96106db33a77b19644
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 "SkReader32.h"
13#include "SkSize.h"
14#include "SkWriter32.h"
15
16static void add_rect(SkPath* path, const SkRect& r) {
17    path->moveTo(r.fLeft, r.fTop);
18    path->lineTo(r.fRight, r.fTop);
19    path->lineTo(r.fRight, r.fBottom);
20    path->lineTo(r.fLeft, r.fBottom);
21    path->close();
22}
23
24static void test_bounds(skiatest::Reporter* reporter) {
25    static const SkRect rects[] = {
26        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
27        { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
28        { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
29        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
30    };
31
32    SkPath path0, path1;
33    for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
34        path0.addRect(rects[i]);
35        add_rect(&path1, rects[i]);
36    }
37
38    REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
39}
40
41static void stroke_cubic(const SkPoint pts[4]) {
42    SkPath path;
43    path.moveTo(pts[0]);
44    path.cubicTo(pts[1], pts[2], pts[3]);
45
46    SkPaint paint;
47    paint.setStyle(SkPaint::kStroke_Style);
48    paint.setStrokeWidth(SK_Scalar1 * 2);
49
50    SkPath fill;
51    paint.getFillPath(path, &fill);
52}
53
54// just ensure this can run w/o any SkASSERTS firing in the debug build
55// we used to assert due to differences in how we determine a degenerate vector
56// but that was fixed with the introduction of SkPoint::CanNormalize
57static void stroke_tiny_cubic() {
58    SkPoint p0[] = {
59        { 372.0f,   92.0f },
60        { 372.0f,   92.0f },
61        { 372.0f,   92.0f },
62        { 372.0f,   92.0f },
63    };
64
65    stroke_cubic(p0);
66
67    SkPoint p1[] = {
68        { 372.0f,       92.0f },
69        { 372.0007f,    92.000755f },
70        { 371.99927f,   92.003922f },
71        { 371.99826f,   92.003899f },
72    };
73
74    stroke_cubic(p1);
75}
76
77static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
78    for (int i = 0; i < 2; ++i) {
79        SkPath::Iter iter(path, (bool)i);
80        SkPoint mv;
81        SkPoint pts[4];
82        SkPath::Verb v;
83        int nMT = 0;
84        int nCL = 0;
85        mv.set(0, 0);
86        while (SkPath::kDone_Verb != (v = iter.next(pts))) {
87            switch (v) {
88                case SkPath::kMove_Verb:
89                    mv = pts[0];
90                    ++nMT;
91                    break;
92                case SkPath::kClose_Verb:
93                    REPORTER_ASSERT(reporter, mv == pts[0]);
94                    ++nCL;
95                    break;
96                default:
97                    break;
98            }
99        }
100        // if we force a close on the interator we should have a close
101        // for every moveTo
102        REPORTER_ASSERT(reporter, !i || nMT == nCL);
103    }
104}
105
106static void test_close(skiatest::Reporter* reporter) {
107    SkPath closePt;
108    closePt.moveTo(0, 0);
109    closePt.close();
110    check_close(reporter, closePt);
111
112    SkPath openPt;
113    openPt.moveTo(0, 0);
114    check_close(reporter, openPt);
115
116    SkPath empty;
117    check_close(reporter, empty);
118    empty.close();
119    check_close(reporter, empty);
120
121    SkPath rect;
122    rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
123    check_close(reporter, rect);
124    rect.close();
125    check_close(reporter, rect);
126
127    SkPath quad;
128    quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
129    check_close(reporter, quad);
130    quad.close();
131    check_close(reporter, quad);
132
133    SkPath cubic;
134    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
135                 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
136    check_close(reporter, cubic);
137    cubic.close();
138    check_close(reporter, cubic);
139
140    SkPath line;
141    line.moveTo(SK_Scalar1, SK_Scalar1);
142    line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
143    check_close(reporter, line);
144    line.close();
145    check_close(reporter, line);
146
147    SkPath rect2;
148    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
149    rect2.close();
150    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
151    check_close(reporter, rect2);
152    rect2.close();
153    check_close(reporter, rect2);
154
155    SkPath oval3;
156    oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
157    oval3.close();
158    oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
159    check_close(reporter, oval3);
160    oval3.close();
161    check_close(reporter, oval3);
162
163    SkPath moves;
164    moves.moveTo(SK_Scalar1, SK_Scalar1);
165    moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
166    moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
167    moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
168    check_close(reporter, moves);
169
170    stroke_tiny_cubic();
171}
172
173static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
174                            SkPath::Convexity expected) {
175    SkPath::Convexity c = SkPath::ComputeConvexity(path);
176    REPORTER_ASSERT(reporter, c == expected);
177}
178
179static void test_convexity2(skiatest::Reporter* reporter) {
180    SkPath pt;
181    pt.moveTo(0, 0);
182    pt.close();
183    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
184
185    SkPath line;
186    line.moveTo(12, 20);
187    line.lineTo(-12, -20);
188    line.close();
189    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
190
191    SkPath triLeft;
192    triLeft.moveTo(0, 0);
193    triLeft.lineTo(1, 0);
194    triLeft.lineTo(1, 1);
195    triLeft.close();
196    check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
197
198    SkPath triRight;
199    triRight.moveTo(0, 0);
200    triRight.lineTo(-1, 0);
201    triRight.lineTo(1, 1);
202    triRight.close();
203    check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
204
205    SkPath square;
206    square.moveTo(0, 0);
207    square.lineTo(1, 0);
208    square.lineTo(1, 1);
209    square.lineTo(0, 1);
210    square.close();
211    check_convexity(reporter, square, SkPath::kConvex_Convexity);
212
213    SkPath redundantSquare;
214    redundantSquare.moveTo(0, 0);
215    redundantSquare.lineTo(0, 0);
216    redundantSquare.lineTo(0, 0);
217    redundantSquare.lineTo(1, 0);
218    redundantSquare.lineTo(1, 0);
219    redundantSquare.lineTo(1, 0);
220    redundantSquare.lineTo(1, 1);
221    redundantSquare.lineTo(1, 1);
222    redundantSquare.lineTo(1, 1);
223    redundantSquare.lineTo(0, 1);
224    redundantSquare.lineTo(0, 1);
225    redundantSquare.lineTo(0, 1);
226    redundantSquare.close();
227    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
228
229    SkPath bowTie;
230    bowTie.moveTo(0, 0);
231    bowTie.lineTo(0, 0);
232    bowTie.lineTo(0, 0);
233    bowTie.lineTo(1, 1);
234    bowTie.lineTo(1, 1);
235    bowTie.lineTo(1, 1);
236    bowTie.lineTo(1, 0);
237    bowTie.lineTo(1, 0);
238    bowTie.lineTo(1, 0);
239    bowTie.lineTo(0, 1);
240    bowTie.lineTo(0, 1);
241    bowTie.lineTo(0, 1);
242    bowTie.close();
243    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
244
245    SkPath spiral;
246    spiral.moveTo(0, 0);
247    spiral.lineTo(100, 0);
248    spiral.lineTo(100, 100);
249    spiral.lineTo(0, 100);
250    spiral.lineTo(0, 50);
251    spiral.lineTo(50, 50);
252    spiral.lineTo(50, 75);
253    spiral.close();
254    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
255
256    SkPath dent;
257    dent.moveTo(SkIntToScalar(0), SkIntToScalar(0));
258    dent.lineTo(SkIntToScalar(100), SkIntToScalar(100));
259    dent.lineTo(SkIntToScalar(0), SkIntToScalar(100));
260    dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200));
261    dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100));
262    dent.close();
263    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
264}
265
266static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
267                                const SkRect& bounds) {
268    REPORTER_ASSERT(reporter, p.isConvex());
269    REPORTER_ASSERT(reporter, p.getBounds() == bounds);
270
271    SkPath p2(p);
272    REPORTER_ASSERT(reporter, p2.isConvex());
273    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
274
275    SkPath other;
276    other.swap(p2);
277    REPORTER_ASSERT(reporter, other.isConvex());
278    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
279}
280
281static void setFromString(SkPath* path, const char str[]) {
282    bool first = true;
283    while (str) {
284        SkScalar x, y;
285        str = SkParse::FindScalar(str, &x);
286        if (NULL == str) {
287            break;
288        }
289        str = SkParse::FindScalar(str, &y);
290        SkASSERT(str);
291        if (first) {
292            path->moveTo(x, y);
293            first = false;
294        } else {
295            path->lineTo(x, y);
296        }
297    }
298}
299
300static void test_convexity(skiatest::Reporter* reporter) {
301    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
302    static const SkPath::Convexity V = SkPath::kConvex_Convexity;
303
304    SkPath path;
305
306    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
307    path.addCircle(0, 0, 10);
308    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
309    path.addCircle(0, 0, 10);   // 2nd circle
310    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
311    path.reset();
312    path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
313    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
314    path.reset();
315    path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
316    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
317
318    static const struct {
319        const char*         fPathStr;
320        SkPath::Convexity   fExpectedConvexity;
321    } gRec[] = {
322        { "", SkPath::kConvex_Convexity },
323        { "0 0", SkPath::kConvex_Convexity },
324        { "0 0 10 10", SkPath::kConvex_Convexity },
325        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
326        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
327        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
328        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
329        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
330    };
331
332    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
333        SkPath path;
334        setFromString(&path, gRec[i].fPathStr);
335        SkPath::Convexity c = SkPath::ComputeConvexity(path);
336        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
337    }
338}
339
340// Simple isRect test is inline TestPath, below.
341// test_isRect provides more extensive testing.
342static void test_isRect(skiatest::Reporter* reporter) {
343    // passing tests (all moveTo / lineTo...
344    SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
345    SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
346    SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
347    SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
348    SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
349    SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
350    SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
351    SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
352    SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
353    SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
354        {1, 0}, {.5f, 0}};
355    SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
356        {0, 1}, {0, .5f}};
357    SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
358    SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
359    SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
360
361    // failing tests
362    SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
363    SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
364    SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
365    SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
366    SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
367    SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
368    SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
369    SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
370
371    // failing, no close
372    SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
373    SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
374
375    size_t testLen[] = {
376        sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
377        sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
378        sizeof(rd), sizeof(re),
379        sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
380        sizeof(f7), sizeof(f8),
381        sizeof(c1), sizeof(c2)
382    };
383    SkPoint* tests[] = {
384        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
385        f1, f2, f3, f4, f5, f6, f7, f8,
386        c1, c2
387    };
388    SkPoint* lastPass = re;
389    SkPoint* lastClose = f8;
390    bool fail = false;
391    bool close = true;
392    const size_t testCount = sizeof(tests) / sizeof(tests[0]);
393    size_t index;
394    for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
395        SkPath path;
396        path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
397        for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
398            path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
399        }
400        if (close) {
401            path.close();
402        }
403        REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
404        if (tests[testIndex] == lastPass) {
405            fail = true;
406        }
407        if (tests[testIndex] == lastClose) {
408            close = false;
409        }
410    }
411
412    // fail, close then line
413    SkPath path1;
414    path1.moveTo(r1[0].fX, r1[0].fY);
415    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
416        path1.lineTo(r1[index].fX, r1[index].fY);
417    }
418    path1.close();
419    path1.lineTo(1, 0);
420    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
421
422    // fail, move in the middle
423    path1.reset();
424    path1.moveTo(r1[0].fX, r1[0].fY);
425    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
426        if (index == 2) {
427            path1.moveTo(1, .5f);
428        }
429        path1.lineTo(r1[index].fX, r1[index].fY);
430    }
431    path1.close();
432    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
433
434    // fail, move on the edge
435    path1.reset();
436    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
437        path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
438        path1.lineTo(r1[index].fX, r1[index].fY);
439    }
440    path1.close();
441    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
442
443    // fail, quad
444    path1.reset();
445    path1.moveTo(r1[0].fX, r1[0].fY);
446    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
447        if (index == 2) {
448            path1.quadTo(1, .5f, 1, .5f);
449        }
450        path1.lineTo(r1[index].fX, r1[index].fY);
451    }
452    path1.close();
453    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
454
455    // fail, cubic
456    path1.reset();
457    path1.moveTo(r1[0].fX, r1[0].fY);
458    for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
459        if (index == 2) {
460            path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
461        }
462        path1.lineTo(r1[index].fX, r1[index].fY);
463    }
464    path1.close();
465    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
466}
467
468static void test_flattening(skiatest::Reporter* reporter) {
469    SkPath p;
470
471    static const SkPoint pts[] = {
472        { 0, 0 },
473        { SkIntToScalar(10), SkIntToScalar(10) },
474        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
475        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
476    };
477    p.moveTo(pts[0]);
478    p.lineTo(pts[1]);
479    p.quadTo(pts[2], pts[3]);
480    p.cubicTo(pts[4], pts[5], pts[6]);
481
482    SkWriter32 writer(100);
483    p.flatten(writer);
484    size_t size = writer.size();
485    SkAutoMalloc storage(size);
486    writer.flatten(storage.get());
487    SkReader32 reader(storage.get(), size);
488
489    SkPath p1;
490    REPORTER_ASSERT(reporter, p1 != p);
491    p1.unflatten(reader);
492    REPORTER_ASSERT(reporter, p1 == p);
493}
494
495static void test_transform(skiatest::Reporter* reporter) {
496    SkPath p, p1;
497
498    static const SkPoint pts[] = {
499        { 0, 0 },
500        { SkIntToScalar(10), SkIntToScalar(10) },
501        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
502        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
503    };
504    p.moveTo(pts[0]);
505    p.lineTo(pts[1]);
506    p.quadTo(pts[2], pts[3]);
507    p.cubicTo(pts[4], pts[5], pts[6]);
508
509    SkMatrix matrix;
510    matrix.reset();
511    p.transform(matrix, &p1);
512    REPORTER_ASSERT(reporter, p == p1);
513
514    matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
515    p.transform(matrix, &p1);
516    SkPoint pts1[7];
517    int count = p1.getPoints(pts1, 7);
518    REPORTER_ASSERT(reporter, 7 == count);
519    for (int i = 0; i < count; ++i) {
520        SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
521        REPORTER_ASSERT(reporter, newPt == pts1[i]);
522    }
523}
524
525static void test_zero_length_paths(skiatest::Reporter* reporter) {
526    SkPath p;
527    SkRect bounds;
528
529    // Lone moveTo case
530    p.moveTo(SK_Scalar1, SK_Scalar1);
531    bounds.set(0, 0, 0, 0);
532    REPORTER_ASSERT(reporter, !p.isEmpty());
533    REPORTER_ASSERT(reporter, 1 == p.countPoints());
534    REPORTER_ASSERT(reporter, bounds == p.getBounds());
535
536    // MoveTo-MoveTo case
537    p.moveTo(SK_Scalar1*2, SK_Scalar1);
538    bounds.set(1, 1, 2, 1);
539    REPORTER_ASSERT(reporter, !p.isEmpty());
540    REPORTER_ASSERT(reporter, 2 == p.countPoints());
541    REPORTER_ASSERT(reporter, bounds == p.getBounds());
542
543    // moveTo-close case
544    p.reset();
545    p.moveTo(SK_Scalar1, SK_Scalar1);
546    p.close();
547    bounds.set(0, 0, 0, 0);
548    REPORTER_ASSERT(reporter, !p.isEmpty());
549    REPORTER_ASSERT(reporter, 1 == p.countPoints());
550    REPORTER_ASSERT(reporter, bounds == p.getBounds());
551
552    // moveTo-close-moveTo-close case
553    p.moveTo(SK_Scalar1*2, SK_Scalar1);
554    p.close();
555    bounds.set(1, 1, 2, 1);
556    REPORTER_ASSERT(reporter, !p.isEmpty());
557    REPORTER_ASSERT(reporter, 2 == p.countPoints());
558    REPORTER_ASSERT(reporter, bounds == p.getBounds());
559
560    // moveTo-line case
561    p.reset();
562    p.moveTo(SK_Scalar1, SK_Scalar1);
563    p.lineTo(SK_Scalar1, SK_Scalar1);
564    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
565    REPORTER_ASSERT(reporter, !p.isEmpty());
566    REPORTER_ASSERT(reporter, 2 == p.countPoints());
567    REPORTER_ASSERT(reporter, bounds == p.getBounds());
568
569    // moveTo-lineTo-moveTo-lineTo case
570    p.moveTo(SK_Scalar1*2, SK_Scalar1);
571    p.lineTo(SK_Scalar1*2, SK_Scalar1);
572    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
573    REPORTER_ASSERT(reporter, !p.isEmpty());
574    REPORTER_ASSERT(reporter, 4 == p.countPoints());
575    REPORTER_ASSERT(reporter, bounds == p.getBounds());
576
577    // moveTo-line-close case
578    p.reset();
579    p.moveTo(SK_Scalar1, SK_Scalar1);
580    p.lineTo(SK_Scalar1, SK_Scalar1);
581    p.close();
582    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
583    REPORTER_ASSERT(reporter, !p.isEmpty());
584    REPORTER_ASSERT(reporter, 2 == p.countPoints());
585    REPORTER_ASSERT(reporter, bounds == p.getBounds());
586
587    // moveTo-line-close-moveTo-line-close case
588    p.moveTo(SK_Scalar1*2, SK_Scalar1);
589    p.lineTo(SK_Scalar1*2, SK_Scalar1);
590    p.close();
591    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
592    REPORTER_ASSERT(reporter, !p.isEmpty());
593    REPORTER_ASSERT(reporter, 4 == p.countPoints());
594    REPORTER_ASSERT(reporter, bounds == p.getBounds());
595
596    // moveTo-quadTo case
597    p.reset();
598    p.moveTo(SK_Scalar1, SK_Scalar1);
599    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
600    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
601    REPORTER_ASSERT(reporter, !p.isEmpty());
602    REPORTER_ASSERT(reporter, 3 == p.countPoints());
603    REPORTER_ASSERT(reporter, bounds == p.getBounds());
604
605    // moveTo-quadTo-close case
606    p.close();
607    REPORTER_ASSERT(reporter, !p.isEmpty());
608    REPORTER_ASSERT(reporter, 3 == p.countPoints());
609    REPORTER_ASSERT(reporter, bounds == p.getBounds());
610
611    // moveTo-quadTo-moveTo-quadTo case
612    p.reset();
613    p.moveTo(SK_Scalar1, SK_Scalar1);
614    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
615    p.moveTo(SK_Scalar1*2, SK_Scalar1);
616    p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
617    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
618    REPORTER_ASSERT(reporter, !p.isEmpty());
619    REPORTER_ASSERT(reporter, 6 == p.countPoints());
620    REPORTER_ASSERT(reporter, bounds == p.getBounds());
621
622    // moveTo-cubicTo case
623    p.reset();
624    p.moveTo(SK_Scalar1, SK_Scalar1);
625    p.cubicTo(SK_Scalar1, SK_Scalar1,
626              SK_Scalar1, SK_Scalar1,
627              SK_Scalar1, SK_Scalar1);
628    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
629    REPORTER_ASSERT(reporter, !p.isEmpty());
630    REPORTER_ASSERT(reporter, 4 == p.countPoints());
631    REPORTER_ASSERT(reporter, bounds == p.getBounds());
632
633    // moveTo-quadTo-close case
634    p.close();
635    REPORTER_ASSERT(reporter, !p.isEmpty());
636    REPORTER_ASSERT(reporter, 4 == p.countPoints());
637    REPORTER_ASSERT(reporter, bounds == p.getBounds());
638
639    // moveTo-quadTo-moveTo-quadTo case
640    p.reset();
641    p.moveTo(SK_Scalar1, SK_Scalar1);
642    p.cubicTo(SK_Scalar1, SK_Scalar1,
643              SK_Scalar1, SK_Scalar1,
644              SK_Scalar1, SK_Scalar1);
645    p.moveTo(SK_Scalar1*2, SK_Scalar1);
646    p.cubicTo(SK_Scalar1*2, SK_Scalar1,
647              SK_Scalar1*2, SK_Scalar1,
648              SK_Scalar1*2, SK_Scalar1);
649    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
650    REPORTER_ASSERT(reporter, !p.isEmpty());
651    REPORTER_ASSERT(reporter, 8 == p.countPoints());
652    REPORTER_ASSERT(reporter, bounds == p.getBounds());
653}
654
655struct SegmentInfo {
656    SkPath fPath;
657    int    fPointCount;
658};
659
660#define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
661
662void TestPath(skiatest::Reporter* reporter);
663void TestPath(skiatest::Reporter* reporter) {
664    {
665        SkSize size;
666        size.fWidth = 3.4f;
667        size.width();
668        size = SkSize::Make(3,4);
669        SkISize isize = SkISize::Make(3,4);
670    }
671
672    SkTSize<SkScalar>::Make(3,4);
673
674    SkPath  p, p2;
675    SkRect  bounds, bounds2;
676
677    REPORTER_ASSERT(reporter, p.isEmpty());
678    REPORTER_ASSERT(reporter, 0 == p.countPoints());
679    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
680    REPORTER_ASSERT(reporter, p.isConvex());
681    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
682    REPORTER_ASSERT(reporter, !p.isInverseFillType());
683    REPORTER_ASSERT(reporter, p == p2);
684    REPORTER_ASSERT(reporter, !(p != p2));
685
686    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
687
688    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
689
690    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
691    check_convex_bounds(reporter, p, bounds);
692    // we have quads or cubics
693    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
694    REPORTER_ASSERT(reporter, !p.isEmpty());
695
696    p.reset();
697    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
698    REPORTER_ASSERT(reporter, p.isEmpty());
699
700    p.addOval(bounds);
701    check_convex_bounds(reporter, p, bounds);
702    REPORTER_ASSERT(reporter, !p.isEmpty());
703
704    p.reset();
705    p.addRect(bounds);
706    check_convex_bounds(reporter, p, bounds);
707    // we have only lines
708    REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
709    REPORTER_ASSERT(reporter, !p.isEmpty());
710
711    REPORTER_ASSERT(reporter, p != p2);
712    REPORTER_ASSERT(reporter, !(p == p2));
713
714    // does getPoints return the right result
715    REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
716    SkPoint pts[4];
717    int count = p.getPoints(pts, 4);
718    REPORTER_ASSERT(reporter, count == 4);
719    bounds2.set(pts, 4);
720    REPORTER_ASSERT(reporter, bounds == bounds2);
721
722    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
723    p.offset(SK_Scalar1*3, SK_Scalar1*4);
724    REPORTER_ASSERT(reporter, bounds == p.getBounds());
725
726    REPORTER_ASSERT(reporter, p.isRect(NULL));
727    bounds2.setEmpty();
728    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
729    REPORTER_ASSERT(reporter, bounds == bounds2);
730
731    // now force p to not be a rect
732    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
733    p.addRect(bounds);
734    REPORTER_ASSERT(reporter, !p.isRect(NULL));
735    test_isRect(reporter);
736
737    SkPoint pt;
738
739    p.moveTo(SK_Scalar1, 0);
740    p.getLastPt(&pt);
741    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
742    REPORTER_ASSERT(reporter, !p.isEmpty());
743
744    test_zero_length_paths(reporter);
745    test_convexity(reporter);
746    test_convexity2(reporter);
747    test_close(reporter);
748
749    p.reset();
750    p.moveTo(0, 0);
751    p.quadTo(100, 100, 200, 200);
752    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
753    REPORTER_ASSERT(reporter, !p.isEmpty());
754    p.cubicTo(100, 100, 200, 200, 300, 300);
755    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
756    REPORTER_ASSERT(reporter, !p.isEmpty());
757    p.reset();
758    p.moveTo(0, 0);
759    p.cubicTo(100, 100, 200, 200, 300, 300);
760    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
761    REPORTER_ASSERT(reporter, !p.isEmpty());
762
763    test_flattening(reporter);
764    test_transform(reporter);
765    test_bounds(reporter);
766}
767
768#include "TestClassDef.h"
769DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
770