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/SkPoint.h"
19 #include "include/core/SkRect.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkShader.h"
22 #include "include/core/SkSize.h"
23 #include "include/core/SkString.h"
24 #include "include/core/SkTileMode.h"
25 #include "include/core/SkTypes.h"
26 #include "include/effects/SkDashPathEffect.h"
27 #include "include/effects/SkGradientShader.h"
28 #include "include/private/SkTArray.h"
29 #include "tools/ToolUtils.h"
30 
31 #include <initializer_list>
32 
33 constexpr int kNumColumns = 6;
34 constexpr int kNumRows = 8;
35 constexpr int kRadius = 40;  // radius of the snowflake
36 constexpr int kPad = 5;      // padding on both sides of the snowflake
37 constexpr int kNumSpokes = 6;
38 constexpr SkScalar kStrokeWidth = 5.0f;
39 
draw_fins(SkCanvas * canvas,const SkPoint & offset,float angle,const SkPaint & paint)40 static void draw_fins(SkCanvas* canvas, const SkPoint& offset, float angle, const SkPaint& paint) {
41     SkScalar cos, sin;
42 
43     // first fin
44     sin = SkScalarSin(angle + (SK_ScalarPI/4));
45     cos = SkScalarCos(angle + (SK_ScalarPI/4));
46     sin *= kRadius / 2.0f;
47     cos *= kRadius / 2.0f;
48 
49     SkPath p;
50     p.moveTo(offset.fX, offset.fY);
51     p.lineTo(offset.fX + cos, offset.fY + sin);
52     canvas->drawPath(p, paint);
53 
54     // second fin
55     sin = SkScalarSin(angle - (SK_ScalarPI/4));
56     cos = SkScalarCos(angle - (SK_ScalarPI/4));
57     sin *= kRadius / 2.0f;
58     cos *= kRadius / 2.0f;
59 
60     p.reset();
61     p.moveTo(offset.fX, offset.fY);
62     p.lineTo(offset.fX + cos, offset.fY + sin);
63     canvas->drawPath(p, paint);
64 }
65 
66 // draw a snowflake centered at the origin
draw_snowflake(SkCanvas * canvas,const SkPaint & paint)67 static void draw_snowflake(SkCanvas* canvas, const SkPaint& paint) {
68 
69     canvas->clipRect(SkRect::MakeLTRB(-kRadius-kPad, -kRadius-kPad, kRadius+kPad, kRadius+kPad));
70 
71     SkScalar sin, cos, angle = 0.0f;
72     for (int i = 0; i < kNumSpokes/2; ++i, angle += SK_ScalarPI/(kNumSpokes/2)) {
73         sin = SkScalarSin(angle);
74         cos = SkScalarCos(angle);
75         sin *= kRadius;
76         cos *= kRadius;
77 
78         // main spoke
79         SkPath p;
80         p.moveTo(-cos, -sin);
81         p.lineTo(cos, sin);
82         canvas->drawPath(p, paint);
83 
84         // fins on positive side
85         const SkPoint posOffset = SkPoint::Make(0.5f * cos, 0.5f * sin);
86         draw_fins(canvas, posOffset, angle, paint);
87 
88         // fins on negative side
89         const SkPoint negOffset = SkPoint::Make(-0.5f * cos, -0.5f * sin);
90         draw_fins(canvas, negOffset, angle+SK_ScalarPI, paint);
91     }
92 }
93 
draw_row(SkCanvas * canvas,const SkPaint & paint,const SkMatrix & localMatrix)94 static void draw_row(SkCanvas* canvas, const SkPaint& paint, const SkMatrix& localMatrix) {
95     canvas->translate(kRadius+kPad, 0.0f);
96 
97     for (auto cap : { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap }) {
98         for (auto isAA : { true, false }) {
99             SkPaint tmp(paint);
100             tmp.setStrokeWidth(kStrokeWidth);
101             tmp.setStyle(SkPaint::kStroke_Style);
102             tmp.setStrokeCap(cap);
103             tmp.setAntiAlias(isAA);
104 
105             int saveCount = canvas->save();
106             canvas->concat(localMatrix);
107             draw_snowflake(canvas, tmp);
108             canvas->restoreToCount(saveCount);
109 
110             canvas->translate(2*(kRadius+kPad), 0.0f);
111         }
112     }
113 }
114 
115 namespace skiagm {
116 
117 // This GM exercises the special case of a stroked lines.
118 // Various shaders are applied to ensure the coordinate spaces work out right.
119 class StrokedLinesGM : public GM {
120 public:
StrokedLinesGM()121     StrokedLinesGM() { this->setBGColor(ToolUtils::color_to_565(0xFF1A65D7)); }
122 
123 protected:
onShortName()124     SkString onShortName() override {
125         return SkString("strokedlines");
126     }
127 
onISize()128     SkISize onISize() override {
129         return SkISize::Make(kNumColumns * (2*kRadius+2*kPad), kNumRows * (2*kRadius+2*kPad));
130     }
131 
onOnceBeforeDraw()132     void onOnceBeforeDraw() override {
133         // paints
134         {
135             // basic white
136             SkPaint p;
137             p.setColor(SK_ColorWHITE);
138             fPaints.push_back(p);
139         }
140         {
141             // gradient
142             SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
143             SkPoint pts[] = { {-kRadius-kPad, -kRadius-kPad }, { kRadius+kPad, kRadius+kPad } };
144 
145             SkPaint p;
146             p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
147 
148             fPaints.push_back(p);
149         }
150         {
151             // dashing
152             SkScalar intervals[] = { kStrokeWidth, kStrokeWidth };
153             int intervalCount = (int) SK_ARRAY_COUNT(intervals);
154             SkPaint p;
155             p.setColor(SK_ColorWHITE);
156             p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, kStrokeWidth));
157 
158             fPaints.push_back(p);
159         }
160         {
161             // Bitmap shader
162             SkBitmap bm;
163             bm.allocN32Pixels(2, 2);
164             *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
165             *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = 0x0;
166 
167             SkMatrix m;
168             m.setRotate(12.0f);
169             m.preScale(3.0f, 3.0f);
170 
171             SkPaint p;
172             p.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &m));
173             fPaints.push_back(p);
174         }
175         {
176             // blur
177             SkPaint p;
178             p.setColor(SK_ColorWHITE);
179             p.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3.0f));
180             fPaints.push_back(p);
181         }
182 
183         // matrices
184         {
185             // rotation
186             SkMatrix m;
187             m.setRotate(12.0f);
188 
189             fMatrices.push_back(m);
190         }
191         {
192             // skew
193             SkMatrix m;
194             m.setSkew(0.3f, 0.5f);
195 
196             fMatrices.push_back(m);
197         }
198         {
199             // perspective
200             SkMatrix m;
201             m.reset();
202             m.setPerspX(-SK_Scalar1 / 300);
203             m.setPerspY(SK_Scalar1 / 300);
204 
205             fMatrices.push_back(m);
206         }
207 
208         SkASSERT(kNumRows == fPaints.count() + fMatrices.count());
209     }
210 
onDraw(SkCanvas * canvas)211     void onDraw(SkCanvas* canvas) override {
212         canvas->translate(0, kRadius+kPad);
213 
214         for (int i = 0; i < fPaints.count(); ++i) {
215             int saveCount = canvas->save();
216             draw_row(canvas, fPaints[i], SkMatrix::I());
217             canvas->restoreToCount(saveCount);
218 
219             canvas->translate(0, 2*(kRadius+kPad));
220         }
221 
222         for (int i = 0; i < fMatrices.count(); ++i) {
223             int saveCount = canvas->save();
224             draw_row(canvas, fPaints[0], fMatrices[i]);
225             canvas->restoreToCount(saveCount);
226 
227             canvas->translate(0, 2*(kRadius+kPad));
228         }
229     }
230 
231 private:
232     SkTArray<SkPaint> fPaints;
233     SkTArray<SkMatrix> fMatrices;
234 
235     using INHERITED = GM;
236 };
237 
238 //////////////////////////////////////////////////////////////////////////////
239 
240 DEF_GM(return new StrokedLinesGM;)
241 }  // namespace skiagm
242