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