xref: /aosp_15_r20/external/skia/gm/strokedlines.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 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/SkBitmap.h"
10 #include "include/core/SkBlurTypes.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkMaskFilter.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPath.h"
17 #include "include/core/SkPathEffect.h"
18 #include "include/core/SkPathUtils.h"
19 #include "include/core/SkPoint.h"
20 #include "include/core/SkRect.h"
21 #include "include/core/SkScalar.h"
22 #include "include/core/SkShader.h"
23 #include "include/core/SkSize.h"
24 #include "include/core/SkString.h"
25 #include "include/core/SkTileMode.h"
26 #include "include/core/SkTypes.h"
27 #include "include/effects/SkDashPathEffect.h"
28 #include "include/effects/SkGradientShader.h"
29 #include "include/private/base/SkTArray.h"
30 #include "include/private/base/SkTemplates.h"
31 #include "tools/ToolUtils.h"
32 
33 #include <initializer_list>
34 
35 using namespace skia_private;
36 
37 constexpr int kNumColumns = 6;
38 constexpr int kNumRows = 8;
39 constexpr int kRadius = 40;  // radius of the snowflake
40 constexpr int kPad = 5;      // padding on both sides of the snowflake
41 constexpr int kNumSpokes = 6;
42 constexpr SkScalar kStrokeWidth = 5.0f;
43 
draw_line(SkCanvas * canvas,const SkPoint & p0,const SkPoint & p1,const SkPaint & paint,bool useDrawPath)44 static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
45                       const SkPaint& paint, bool useDrawPath) {
46     if (useDrawPath) {
47         canvas->drawPath(SkPath::Line(p0, p1), paint);
48     } else {
49         canvas->drawLine(p0, p1, paint);
50     }
51 }
52 
draw_fins(SkCanvas * canvas,const SkPoint & offset,float angle,const SkPaint & paint,bool useDrawPath)53 static void draw_fins(SkCanvas* canvas, const SkPoint& offset, float angle, const SkPaint& paint,
54                       bool useDrawPath) {
55     SkScalar cos, sin;
56 
57     // first fin
58     sin = SkScalarSin(angle + (SK_ScalarPI/4));
59     cos = SkScalarCos(angle + (SK_ScalarPI/4));
60     sin *= kRadius / 2.0f;
61     cos *= kRadius / 2.0f;
62 
63     draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath);
64 
65     // second fin
66     sin = SkScalarSin(angle - (SK_ScalarPI/4));
67     cos = SkScalarCos(angle - (SK_ScalarPI/4));
68     sin *= kRadius / 2.0f;
69     cos *= kRadius / 2.0f;
70 
71     draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath);
72 }
73 
74 // draw a snowflake centered at the origin
draw_snowflake(SkCanvas * canvas,const SkPaint & paint,bool useDrawPath)75 static void draw_snowflake(SkCanvas* canvas, const SkPaint& paint, bool useDrawPath) {
76 
77     canvas->clipRect(SkRect::MakeLTRB(-kRadius-kPad, -kRadius-kPad, kRadius+kPad, kRadius+kPad));
78 
79     SkScalar sin, cos, angle = 0.0f;
80     for (int i = 0; i < kNumSpokes/2; ++i, angle += SK_ScalarPI/(kNumSpokes/2)) {
81         sin = SkScalarSin(angle);
82         cos = SkScalarCos(angle);
83         sin *= kRadius;
84         cos *= kRadius;
85 
86         // main spoke
87         draw_line(canvas, {-cos, -sin}, {cos, sin}, paint, useDrawPath);
88 
89         // fins on positive side
90         const SkPoint posOffset = SkPoint::Make(0.5f * cos, 0.5f * sin);
91         draw_fins(canvas, posOffset, angle, paint, useDrawPath);
92 
93         // fins on negative side
94         const SkPoint negOffset = SkPoint::Make(-0.5f * cos, -0.5f * sin);
95         draw_fins(canvas, negOffset, angle+SK_ScalarPI, paint, useDrawPath);
96     }
97 }
98 
draw_row(SkCanvas * canvas,const SkPaint & paint,const SkMatrix & localMatrix,bool useDrawPath)99 static void draw_row(SkCanvas* canvas, const SkPaint& paint, const SkMatrix& localMatrix,
100                      bool useDrawPath) {
101     canvas->translate(kRadius+kPad, 0.0f);
102 
103     for (auto cap : { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap }) {
104         for (auto isAA : { true, false }) {
105             SkPaint tmp(paint);
106             tmp.setStrokeWidth(kStrokeWidth);
107             tmp.setStyle(SkPaint::kStroke_Style);
108             tmp.setStrokeCap(cap);
109             tmp.setAntiAlias(isAA);
110 
111             int saveCount = canvas->save();
112             canvas->concat(localMatrix);
113             draw_snowflake(canvas, tmp, useDrawPath);
114             canvas->restoreToCount(saveCount);
115 
116             canvas->translate(2*(kRadius+kPad), 0.0f);
117         }
118     }
119 }
120 
121 namespace skiagm {
122 
123 // This GM exercises the special case of a stroked lines.
124 // Various shaders are applied to ensure the coordinate spaces work out right.
125 class StrokedLinesGM : public GM {
126 public:
StrokedLinesGM(bool useDrawPath)127     StrokedLinesGM(bool useDrawPath) : fUseDrawPath(useDrawPath) {
128         this->setBGColor(ToolUtils::color_to_565(0xFF1A65D7));
129     }
130 
131 protected:
getName() const132     SkString getName() const override {
133         // To preserve history, useDrawPath==true has no suffix.
134         SkString name{"strokedlines"};
135         if (!fUseDrawPath) {
136             name.append("_drawPoints");
137         }
138         return name;
139     }
140 
getISize()141     SkISize getISize() override {
142         return SkISize::Make(kNumColumns * (2*kRadius+2*kPad), kNumRows * (2*kRadius+2*kPad));
143     }
144 
onOnceBeforeDraw()145     void onOnceBeforeDraw() override {
146         // paints
147         {
148             // basic white
149             SkPaint p;
150             p.setColor(SK_ColorWHITE);
151             fPaints.push_back(p);
152         }
153         {
154             // gradient
155             SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
156             SkPoint pts[] = { {-kRadius-kPad, -kRadius-kPad }, { kRadius+kPad, kRadius+kPad } };
157 
158             SkPaint p;
159             p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
160 
161             fPaints.push_back(p);
162         }
163         {
164             // dashing
165             SkScalar intervals[] = { kStrokeWidth, kStrokeWidth };
166             int intervalCount = (int) std::size(intervals);
167             SkPaint p;
168             p.setColor(SK_ColorWHITE);
169             p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, kStrokeWidth));
170 
171             fPaints.push_back(p);
172         }
173         {
174             // Bitmap shader
175             SkBitmap bm;
176             bm.allocN32Pixels(2, 2);
177             *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
178             *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = 0x0;
179 
180             SkMatrix m;
181             m.setRotate(12.0f);
182             m.preScale(3.0f, 3.0f);
183 
184             SkPaint p;
185             p.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
186                                       SkSamplingOptions(), m));
187             fPaints.push_back(p);
188         }
189         {
190             // blur
191             SkPaint p;
192             p.setColor(SK_ColorWHITE);
193             p.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3.0f));
194             fPaints.push_back(p);
195         }
196 
197         // matrices
198         {
199             // rotation
200             SkMatrix m;
201             m.setRotate(12.0f);
202 
203             fMatrices.push_back(m);
204         }
205         {
206             // skew
207             SkMatrix m;
208             m.setSkew(0.3f, 0.5f);
209 
210             fMatrices.push_back(m);
211         }
212         {
213             // perspective
214             SkMatrix m;
215             m.reset();
216             m.setPerspX(-SK_Scalar1 / 300);
217             m.setPerspY(SK_Scalar1 / 300);
218 
219             fMatrices.push_back(m);
220         }
221 
222         SkASSERT(kNumRows == fPaints.size() + fMatrices.size());
223     }
224 
onDraw(SkCanvas * canvas)225     void onDraw(SkCanvas* canvas) override {
226         canvas->translate(0, kRadius+kPad);
227 
228         for (int i = 0; i < fPaints.size(); ++i) {
229             int saveCount = canvas->save();
230             draw_row(canvas, fPaints[i], SkMatrix::I(), fUseDrawPath);
231             canvas->restoreToCount(saveCount);
232 
233             canvas->translate(0, 2*(kRadius+kPad));
234         }
235 
236         for (int i = 0; i < fMatrices.size(); ++i) {
237             int saveCount = canvas->save();
238             draw_row(canvas, fPaints[0], fMatrices[i], fUseDrawPath);
239             canvas->restoreToCount(saveCount);
240 
241             canvas->translate(0, 2*(kRadius+kPad));
242         }
243     }
244 
245 private:
246     TArray<SkPaint> fPaints;
247     TArray<SkMatrix> fMatrices;
248 
249     const bool fUseDrawPath;
250 
251     using INHERITED = GM;
252 };
253 
254 //////////////////////////////////////////////////////////////////////////////
255 
256 DEF_GM(return new StrokedLinesGM(true);)
257 DEF_GM(return new StrokedLinesGM(false);)
258 
259 //////////////////////////////////////////////////////////////////////////////
260 
261 static constexpr float kStrokeWidth = 20.f;
262 
draw_path(SkCanvas * canvas,const SkPoint & p0,const SkPoint & p1,SkPaint::Cap cap)263 static void draw_path(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, SkPaint::Cap cap) {
264     // Add a gradient *not* aligned with the line's points to show local coords are tracked properly
265     constexpr SkRect kRect {-kStrokeWidth, -kStrokeWidth, 2*kStrokeWidth, 4*kStrokeWidth};
266     constexpr SkPoint kPts[] {{kRect.fLeft, kRect.fTop}, {kRect.fRight, kRect.fBottom}};
267     constexpr SkColor kColors[] {SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE};
268     constexpr SkScalar kStops[] {0.f, 0.75f, 1.f};
269     sk_sp<SkShader> shader = SkGradientShader::MakeLinear(kPts, kColors, kStops, 3,
270                                                           SkTileMode::kClamp);
271 
272     SkPaint paint;
273     paint.setAntiAlias(true);
274     paint.setStyle(SkPaint::kStroke_Style);
275 
276     paint.setShader(std::move(shader));
277     paint.setStrokeWidth(kStrokeWidth);
278     paint.setStrokeCap(cap);
279     canvas->drawLine(p0, p1, paint);
280 
281     // Show outline and control points
282     SkPath fillPath;
283     SkPath path = SkPath::Line(p0, p1);
284     skpathutils::FillPathWithPaint(path, paint, &fillPath);
285 
286     paint.setStyle(SkPaint::kStroke_Style);
287     paint.setStrokeWidth(0);
288     paint.setShader(nullptr);
289     paint.setColor(SK_ColorRED);
290     canvas->drawPath(fillPath, paint);
291 
292     paint.setStrokeWidth(3);
293     paint.setStrokeCap(SkPaint::kSquare_Cap);
294     int n = fillPath.countPoints();
295     AutoTArray<SkPoint> points(n);
296     fillPath.getPoints(points.get(), n);
297     canvas->drawPoints(SkCanvas::kPoints_PointMode, n, points.get(), paint);
298 }
299 
300 DEF_SIMPLE_GM(strokedline_caps, canvas, 1400, 740) {
301     canvas->translate(kStrokeWidth*3/2, kStrokeWidth*3/2);
302 
303     constexpr SkPaint::Cap kCaps[] = {
304         SkPaint::kSquare_Cap, SkPaint::kButt_Cap, SkPaint::kRound_Cap
305     };
306 
307     constexpr float kLengths[] = {
308         4*kStrokeWidth, kStrokeWidth, kStrokeWidth/2, kStrokeWidth/4
309     };
310 
311     for (size_t i = 0; i < std::size(kCaps); ++i) {
312         SkAutoCanvasRestore acr(canvas, true);
313 
__anonddfeee690102(float x0, float y0, float x1, float y1) 314         auto drawLine = [&](float x0, float y0, float x1, float y1) {
315             draw_path(canvas, {x0, y0}, {x1, y1}, kCaps[i]);
316             canvas->translate(std::max(x0, x1) + 2 * kStrokeWidth, 0);
317         };
318 
319         for (size_t j = 0; j < std::size(kLengths); ++j) {
320             float l = kLengths[j];
321 
322             drawLine(0.f, 0.f, l, l);
323             drawLine(l, l, 0.f, 0.f);
324             drawLine(l/2, 0, l/2, l);
325             drawLine(0, l/2, l, l/2);
326         }
327 
328         drawLine(kStrokeWidth/2, kStrokeWidth/2, kStrokeWidth/2, kStrokeWidth/2);
329 
330         acr.restore();
331         canvas->translate(0, kLengths[0] + 2 * kStrokeWidth);
332     }
333 }
334 
335 }  // namespace skiagm
336