xref: /aosp_15_r20/external/skia/gm/nonclosedpaths.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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:
getName() const40     SkString getName() const override { return SkString("nonclosedpaths"); }
41 
42     // 12 * 18 + 3 cases, every case is 100 * 100 pixels.
getISize()43     SkISize getISize() override { return SkISize::Make(1220, 1920); }
44 
45     // Use rect-like geometry for non-closed path, for right angles make it
46     // easier to show the visual difference of lineCap and lineJoin.
MakePath(ClosureType type)47     static SkPath MakePath(ClosureType type) {
48         SkPathBuilder path;
49         if (FakeCloseMiddle == type) {
50             path.moveTo(30, 50);
51             path.lineTo(30, 30);
52         } else {
53             path.moveTo(30, 30);
54         }
55         path.lineTo(70, 30);
56         path.lineTo(70, 70);
57         path.lineTo(30, 70);
58         path.lineTo(30, 50);
59         if (FakeCloseCorner == type) {
60             path.lineTo(30, 30);
61         }
62         return path.detach();
63     }
64 
65     // Set the location for the current test on the canvas
SetLocation(SkCanvas * canvas,int counter,int lineNum)66     static void SetLocation(SkCanvas* canvas, int counter, int lineNum) {
67         SkScalar x = SK_Scalar1 * 100 * (counter % lineNum) + 10 + SK_Scalar1 / 4;
68         SkScalar y = SK_Scalar1 * 100 * (counter / lineNum) + 10 + 3 * SK_Scalar1 / 4;
69         canvas->translate(x, y);
70     }
71 
onDraw(SkCanvas * canvas)72     void onDraw(SkCanvas* canvas) override {
73         // Stroke widths are:
74         // 0(may use hairline rendering), 10(common case for stroke-style)
75         // 40 and 50(>= geometry width/height, make the contour filled in fact)
76         constexpr int kStrokeWidth[] = {0, 10, 40, 50};
77         int numWidths = std::size(kStrokeWidth);
78 
79         constexpr SkPaint::Style kStyle[] = {
80             SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style
81         };
82 
83         constexpr SkPaint::Cap kCap[] = {
84             SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap
85         };
86 
87         constexpr SkPaint::Join kJoin[] = {
88             SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
89         };
90 
91         constexpr ClosureType kType[] = {
92             TotallyNonClosed, FakeCloseCorner, FakeCloseMiddle
93         };
94 
95         int counter = 0;
96         SkPaint paint;
97         paint.setAntiAlias(true);
98 
99         // For stroke style painter and fill-and-stroke style painter
100         for (size_t type = 0; type < kClosureTypeCount; ++type) {
101             for (size_t style = 0; style < std::size(kStyle); ++style) {
102                 for (size_t cap = 0; cap < std::size(kCap); ++cap) {
103                     for (size_t join = 0; join < std::size(kJoin); ++join) {
104                         for (int width = 0; width < numWidths; ++width) {
105                             canvas->save();
106                             SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);
107 
108                             SkPath path = MakePath(kType[type]);
109 
110                             paint.setStyle(kStyle[style]);
111                             paint.setStrokeCap(kCap[cap]);
112                             paint.setStrokeJoin(kJoin[join]);
113                             paint.setStrokeWidth(SkIntToScalar(kStrokeWidth[width]));
114 
115                             canvas->drawPath(path, paint);
116                             canvas->restore();
117                             ++counter;
118                         }
119                     }
120                 }
121             }
122         }
123 
124         // For fill style painter
125         paint.setStyle(SkPaint::kFill_Style);
126         for (size_t type = 0; type < kClosureTypeCount; ++type) {
127             canvas->save();
128             SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths);
129 
130             SkPath path = MakePath(kType[type]);
131 
132             canvas->drawPath(path, paint);
133             canvas->restore();
134             ++counter;
135         }
136     }
137 
138 private:
139     using INHERITED = GM;
140 };
141 
142 //////////////////////////////////////////////////////////////////////////////
143 
144 DEF_GM(return new NonClosedPathsGM;)
145 
146 }  // namespace skiagm
147