blob: e4105cc71e0e3796f4d2e08f595d692c1b929a27 [file] [log] [blame]
robertphillips0ee62202016-05-31 06:59:18 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBitmap.h"
10#include "include/core/SkBlurTypes.h"
11#include "include/core/SkCanvas.h"
12#include "include/core/SkColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkMaskFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040014#include "include/core/SkMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkPaint.h"
16#include "include/core/SkPath.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040017#include "include/core/SkPathEffect.h"
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040018#include "include/core/SkPathUtils.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040019#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "include/effects/SkDashPathEffect.h"
28#include "include/effects/SkGradientShader.h"
Kevin Lubickdc6cc022023-01-13 11:24:27 -050029#include "include/private/base/SkTArray.h"
Kevin Lubick78fced82023-09-20 15:56:21 -040030#include "include/private/base/SkTemplates.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "tools/ToolUtils.h"
robertphillips0ee62202016-05-31 06:59:18 -070032
Ben Wagner7fde8e12019-05-01 17:28:53 -040033#include <initializer_list>
34
Herb Derbyec96c212023-03-06 10:31:22 -050035using namespace skia_private;
36
mtkleindbfd7ab2016-09-01 11:24:54 -070037constexpr int kNumColumns = 6;
38constexpr int kNumRows = 8;
39constexpr int kRadius = 40; // radius of the snowflake
40constexpr int kPad = 5; // padding on both sides of the snowflake
41constexpr int kNumSpokes = 6;
42constexpr SkScalar kStrokeWidth = 5.0f;
robertphillips0ee62202016-05-31 06:59:18 -070043
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040044static 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
53static void draw_fins(SkCanvas* canvas, const SkPoint& offset, float angle, const SkPaint& paint,
54 bool useDrawPath) {
robertphillips0ee62202016-05-31 06:59:18 -070055 SkScalar cos, sin;
56
57 // first fin
Brian Osman4428f2c2019-04-02 10:59:28 -040058 sin = SkScalarSin(angle + (SK_ScalarPI/4));
59 cos = SkScalarCos(angle + (SK_ScalarPI/4));
robertphillips0ee62202016-05-31 06:59:18 -070060 sin *= kRadius / 2.0f;
61 cos *= kRadius / 2.0f;
62
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040063 draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -070064
65 // second fin
Brian Osman4428f2c2019-04-02 10:59:28 -040066 sin = SkScalarSin(angle - (SK_ScalarPI/4));
67 cos = SkScalarCos(angle - (SK_ScalarPI/4));
robertphillips0ee62202016-05-31 06:59:18 -070068 sin *= kRadius / 2.0f;
69 cos *= kRadius / 2.0f;
70
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040071 draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -070072}
73
74// draw a snowflake centered at the origin
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040075static void draw_snowflake(SkCanvas* canvas, const SkPaint& paint, bool useDrawPath) {
robertphillips0ee62202016-05-31 06:59:18 -070076
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)) {
Brian Osman4428f2c2019-04-02 10:59:28 -040081 sin = SkScalarSin(angle);
82 cos = SkScalarCos(angle);
robertphillips0ee62202016-05-31 06:59:18 -070083 sin *= kRadius;
84 cos *= kRadius;
85
86 // main spoke
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040087 draw_line(canvas, {-cos, -sin}, {cos, sin}, paint, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -070088
89 // fins on positive side
90 const SkPoint posOffset = SkPoint::Make(0.5f * cos, 0.5f * sin);
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040091 draw_fins(canvas, posOffset, angle, paint, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -070092
93 // fins on negative side
94 const SkPoint negOffset = SkPoint::Make(-0.5f * cos, -0.5f * sin);
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040095 draw_fins(canvas, negOffset, angle+SK_ScalarPI, paint, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -070096 }
97}
98
Michael Ludwig3a4dcd82023-03-28 21:31:03 -040099static void draw_row(SkCanvas* canvas, const SkPaint& paint, const SkMatrix& localMatrix,
100 bool useDrawPath) {
robertphillips0ee62202016-05-31 06:59:18 -0700101 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);
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400113 draw_snowflake(canvas, tmp, useDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -0700114 canvas->restoreToCount(saveCount);
115
116 canvas->translate(2*(kRadius+kPad), 0.0f);
117 }
118 }
119}
120
121namespace 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.
125class StrokedLinesGM : public GM {
126public:
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400127 StrokedLinesGM(bool useDrawPath) : fUseDrawPath(useDrawPath) {
128 this->setBGColor(ToolUtils::color_to_565(0xFF1A65D7));
129 }
robertphillips0ee62202016-05-31 06:59:18 -0700130
131protected:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000132 SkString getName() const override {
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400133 // To preserve history, useDrawPath==true has no suffix.
134 SkString name{"strokedlines"};
135 if (!fUseDrawPath) {
136 name.append("_drawPoints");
137 }
138 return name;
robertphillips0ee62202016-05-31 06:59:18 -0700139 }
140
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000141 SkISize getISize() override {
robertphillips0ee62202016-05-31 06:59:18 -0700142 return SkISize::Make(kNumColumns * (2*kRadius+2*kPad), kNumRows * (2*kRadius+2*kPad));
143 }
144
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;
Mike Reedfae8fce2019-04-03 10:27:45 -0400159 p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
robertphillips0ee62202016-05-31 06:59:18 -0700160
161 fPaints.push_back(p);
162 }
163 {
164 // dashing
165 SkScalar intervals[] = { kStrokeWidth, kStrokeWidth };
Herb Derbyc37b3862022-06-21 09:49:17 -0400166 int intervalCount = (int) std::size(intervals);
robertphillips0ee62202016-05-31 06:59:18 -0700167 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);
Brian Salomon23356442018-11-30 15:33:19 -0500182 m.preScale(3.0f, 3.0f);
robertphillips0ee62202016-05-31 06:59:18 -0700183
184 SkPaint p;
Mike Reedb41bd152020-12-12 11:18:31 -0500185 p.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
186 SkSamplingOptions(), m));
robertphillips0ee62202016-05-31 06:59:18 -0700187 fPaints.push_back(p);
188 }
189 {
190 // blur
191 SkPaint p;
192 p.setColor(SK_ColorWHITE);
Mike Reed1be1f8d2018-03-14 13:01:17 -0400193 p.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3.0f));
robertphillips0ee62202016-05-31 06:59:18 -0700194 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
Herb Derbyffacce52022-11-09 10:51:34 -0500222 SkASSERT(kNumRows == fPaints.size() + fMatrices.size());
robertphillips0ee62202016-05-31 06:59:18 -0700223 }
224
225 void onDraw(SkCanvas* canvas) override {
226 canvas->translate(0, kRadius+kPad);
227
Herb Derbyffacce52022-11-09 10:51:34 -0500228 for (int i = 0; i < fPaints.size(); ++i) {
robertphillips0ee62202016-05-31 06:59:18 -0700229 int saveCount = canvas->save();
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400230 draw_row(canvas, fPaints[i], SkMatrix::I(), fUseDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -0700231 canvas->restoreToCount(saveCount);
232
233 canvas->translate(0, 2*(kRadius+kPad));
234 }
235
Herb Derbyffacce52022-11-09 10:51:34 -0500236 for (int i = 0; i < fMatrices.size(); ++i) {
robertphillips0ee62202016-05-31 06:59:18 -0700237 int saveCount = canvas->save();
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400238 draw_row(canvas, fPaints[0], fMatrices[i], fUseDrawPath);
robertphillips0ee62202016-05-31 06:59:18 -0700239 canvas->restoreToCount(saveCount);
240
241 canvas->translate(0, 2*(kRadius+kPad));
242 }
243 }
244
245private:
Herb Derbyec96c212023-03-06 10:31:22 -0500246 TArray<SkPaint> fPaints;
247 TArray<SkMatrix> fMatrices;
robertphillips0ee62202016-05-31 06:59:18 -0700248
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400249 const bool fUseDrawPath;
250
John Stiles7571f9e2020-09-02 22:42:33 -0400251 using INHERITED = GM;
robertphillips0ee62202016-05-31 06:59:18 -0700252};
253
254//////////////////////////////////////////////////////////////////////////////
255
Michael Ludwig3a4dcd82023-03-28 21:31:03 -0400256DEF_GM(return new StrokedLinesGM(true);)
257DEF_GM(return new StrokedLinesGM(false);)
258
259//////////////////////////////////////////////////////////////////////////////
260
261static constexpr float kStrokeWidth = 20.f;
262
263static 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
300DEF_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
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
John Stilesa6841be2020-08-06 14:11:56 -0400335} // namespace skiagm