EmptyPathTest.cpp revision deee496cd30070e52556dcb538c2e5eb39b66b81
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkCanvas.h"
9#include "SkPath.h"
10#include "Test.h"
11
12#define DIMENSION   32
13
14static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path,
15                        const SkPaint& paint, bool shouldDraw) {
16    SkBitmap bm;
17    bm.allocN32Pixels(DIMENSION, DIMENSION);
18    SkASSERT(DIMENSION*4 == bm.rowBytes()); // ensure no padding on each row
19    bm.eraseColor(SK_ColorTRANSPARENT);
20
21    SkCanvas canvas(bm);
22    SkPaint p(paint);
23    p.setColor(SK_ColorWHITE);
24
25    canvas.drawPath(path, p);
26
27    size_t count = DIMENSION * DIMENSION;
28    const SkPMColor* ptr = bm.getAddr32(0, 0);
29
30    SkPMColor andValue = ~0U;
31    SkPMColor orValue = 0;
32    for (size_t i = 0; i < count; ++i) {
33        SkPMColor c = ptr[i];
34        andValue &= c;
35        orValue |= c;
36    }
37
38    // success means we drew everywhere or nowhere (depending on shouldDraw)
39    bool success = shouldDraw ? (~0U == andValue) : (0 == orValue);
40
41    if (!success) {
42        const char* str;
43        if (shouldDraw) {
44            str = "Path expected to draw everywhere, but didn't. ";
45        } else {
46            str = "Path expected to draw nowhere, but did. ";
47        }
48        ERRORF(reporter, "%s style[%d] cap[%d] join[%d] antialias[%d]"
49               " filltype[%d] ptcount[%d]", str, paint.getStyle(),
50               paint.getStrokeCap(), paint.getStrokeJoin(),
51               paint.isAntiAlias(), path.getFillType(), path.countPoints());
52// uncomment this if you want to step in to see the failure
53//        canvas.drawPath(path, p);
54    }
55}
56
57static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) {
58    static const SkPaint::Cap gCaps[] = {
59        SkPaint::kButt_Cap,
60        SkPaint::kRound_Cap,
61        SkPaint::kSquare_Cap
62    };
63    static const SkPaint::Join gJoins[] = {
64        SkPaint::kMiter_Join,
65        SkPaint::kRound_Join,
66        SkPaint::kBevel_Join
67    };
68    static const SkPaint::Style gStyles[] = {
69        SkPaint::kFill_Style,
70        SkPaint::kStroke_Style,
71        SkPaint::kStrokeAndFill_Style
72    };
73    for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
74        for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) {
75            for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
76                SkPaint paint;
77                paint.setStrokeWidth(SkIntToScalar(10));
78
79                paint.setStrokeCap(gCaps[cap]);
80                paint.setStrokeJoin(gJoins[join]);
81                paint.setStyle(gStyles[style]);
82
83                paint.setAntiAlias(false);
84                drawAndTest(reporter, path, paint, shouldDraw);
85                paint.setAntiAlias(true);
86                drawAndTest(reporter, path, paint, shouldDraw);
87            }
88        }
89    }
90}
91
92#define CX  (SkIntToScalar(DIMENSION) / 2)
93#define CY  (SkIntToScalar(DIMENSION) / 2)
94
95static void make_empty(SkPath*) {}
96static void make_M(SkPath* path) { path->moveTo(CX, CY); }
97static void make_MM(SkPath* path) { path->moveTo(CX, CY); path->moveTo(CX, CY); }
98static void make_MZM(SkPath* path) { path->moveTo(CX, CY); path->close(); path->moveTo(CX, CY); }
99static void make_L(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); }
100static void make_Q(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); }
101static void make_C(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); }
102
103/*  Two invariants are tested: How does an empty/degenerate path draw?
104 *  - if the path is drawn inverse, it should draw everywhere
105 *  - if the path is drawn non-inverse, it should draw nowhere
106 *
107 *  Things to iterate on:
108 *  - path (empty, degenerate line/quad/cubic w/ and w/o close
109 *  - paint style
110 *  - path filltype
111 *  - path stroke variants (e.g. caps, joins, width)
112 */
113static void test_emptydrawing(skiatest::Reporter* reporter) {
114    static void (*gMakeProc[])(SkPath*) = {
115        make_empty, make_M, make_MM, make_MZM, make_L, make_Q, make_C
116    };
117    static SkPath::FillType gFills[] = {
118        SkPath::kWinding_FillType,
119        SkPath::kEvenOdd_FillType,
120        SkPath::kInverseWinding_FillType,
121        SkPath::kInverseEvenOdd_FillType
122    };
123    for (int doClose = 0; doClose < 2; ++doClose) {
124        for  (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) {
125            SkPath path;
126            gMakeProc[i](&path);
127            if (doClose) {
128                path.close();
129            }
130            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
131                path.setFillType(gFills[fill]);
132                bool shouldDraw = path.isInverseFillType();
133                iter_paint(reporter, path, shouldDraw);
134            }
135        }
136    }
137}
138
139DEF_TEST(EmptyPath, reporter) {
140    test_emptydrawing(reporter);
141}
142