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 
17 namespace 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.
22 class NonClosedPathsGM: public GM {
23 public:
NonClosedPathsGM()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 
39 protected:
40 
onShortName()41     SkString onShortName() override {
42         return SkString("nonclosedpaths");
43     }
44 
45     // 12 * 18 + 3 cases, every case is 100 * 100 pixels.
onISize()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.
MakePath(ClosureType type)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
SetLocation(SkCanvas * canvas,int counter,int lineNum)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 
onDraw(SkCanvas * canvas)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 
143 private:
144     using INHERITED = GM;
145 };
146 
147 //////////////////////////////////////////////////////////////////////////////
148 
149 DEF_GM(return new NonClosedPathsGM;)
150 
151 }  // namespace skiagm
152