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