xref: /third_party/skia/gm/nonclosedpaths.cpp (revision cb93a386)
1/*
2 * Copyright 2013 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 "gm/gm.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkPaint.h"
11#include "include/core/SkPathBuilder.h"
12#include "include/core/SkScalar.h"
13#include "include/core/SkSize.h"
14#include "include/core/SkString.h"
15#include "include/core/SkTypes.h"
16
17namespace skiagm {
18
19// This GM tests a grab-bag of non-closed paths. All these paths look like
20// closed rects, but they don't call path.close(). Depending on the stroke
21// settings these slightly different paths give widely different results.
22class NonClosedPathsGM: public GM {
23public:
24    NonClosedPathsGM() {}
25
26    enum ClosureType {
27        TotallyNonClosed,  // The last point doesn't coincide with the first one in the contour.
28                           // The path looks not closed at all.
29
30        FakeCloseCorner,   // The last point coincides with the first one at a corner.
31                           // The path looks closed, but final rendering has 2 ends with cap.
32
33        FakeCloseMiddle,   // The last point coincides with the first one in the middle of a line.
34                           // The path looks closed, and the final rendering looks closed too.
35
36        kClosureTypeCount
37    };
38
39protected:
40
41    SkString onShortName() override {
42        return SkString("nonclosedpaths");
43    }
44
45    // 12 * 18 + 3 cases, every case is 100 * 100 pixels.
46    SkISize onISize() override {
47        return SkISize::Make(1220, 1920);
48    }
49
50    // Use rect-like geometry for non-closed path, for right angles make it
51    // easier to show the visual difference of lineCap and lineJoin.
52    static SkPath MakePath(ClosureType type) {
53        SkPathBuilder path;
54        if (FakeCloseMiddle == type) {
55            path.moveTo(30, 50);
56            path.lineTo(30, 30);
57        } else {
58            path.moveTo(30, 30);
59        }
60        path.lineTo(70, 30);
61        path.lineTo(70, 70);
62        path.lineTo(30, 70);
63        path.lineTo(30, 50);
64        if (FakeCloseCorner == type) {
65            path.lineTo(30, 30);
66        }
67        return path.detach();
68    }
69
70    // Set the location for the current test on the canvas
71    static void SetLocation(SkCanvas* canvas, int counter, int lineNum) {
72        SkScalar x = SK_Scalar1 * 100 * (counter % lineNum) + 10 + SK_Scalar1 / 4;
73        SkScalar y = SK_Scalar1 * 100 * (counter / lineNum) + 10 + 3 * SK_Scalar1 / 4;
74        canvas->translate(x, y);
75    }
76
77    void onDraw(SkCanvas* canvas) override {
78        // Stroke widths are:
79        // 0(may use hairline rendering), 10(common case for stroke-style)
80        // 40 and 50(>= geometry width/height, make the contour filled in fact)
81        constexpr int kStrokeWidth[] = {0, 10, 40, 50};
82        int numWidths = SK_ARRAY_COUNT(kStrokeWidth);
83
84        constexpr SkPaint::Style kStyle[] = {
85            SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style
86        };
87
88        constexpr SkPaint::Cap kCap[] = {
89            SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap
90        };
91
92        constexpr SkPaint::Join kJoin[] = {
93            SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
94        };
95
96        constexpr ClosureType kType[] = {
97            TotallyNonClosed, FakeCloseCorner, FakeCloseMiddle
98        };
99
100        int counter = 0;
101        SkPaint paint;
102        paint.setAntiAlias(true);
103
104        // For stroke style painter and fill-and-stroke style painter
105        for (size_t type = 0; type < kClosureTypeCount; ++type) {
106            for (size_t style = 0; style < SK_ARRAY_COUNT(kStyle); ++style) {
107                for (size_t cap = 0; cap < SK_ARRAY_COUNT(kCap); ++cap) {
108                    for (size_t join = 0; join < SK_ARRAY_COUNT(kJoin); ++join) {
109                        for (int width = 0; width < numWidths; ++width) {
110                            canvas->save();
111                            SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);
112
113                            SkPath path = MakePath(kType[type]);
114
115                            paint.setStyle(kStyle[style]);
116                            paint.setStrokeCap(kCap[cap]);
117                            paint.setStrokeJoin(kJoin[join]);
118                            paint.setStrokeWidth(SkIntToScalar(kStrokeWidth[width]));
119
120                            canvas->drawPath(path, paint);
121                            canvas->restore();
122                            ++counter;
123                        }
124                    }
125                }
126            }
127        }
128
129        // For fill style painter
130        paint.setStyle(SkPaint::kFill_Style);
131        for (size_t type = 0; type < kClosureTypeCount; ++type) {
132            canvas->save();
133            SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);
134
135            SkPath path = MakePath(kType[type]);
136
137            canvas->drawPath(path, paint);
138            canvas->restore();
139            ++counter;
140        }
141    }
142
143private:
144    using INHERITED = GM;
145};
146
147//////////////////////////////////////////////////////////////////////////////
148
149DEF_GM(return new NonClosedPathsGM;)
150
151}  // namespace skiagm
152