1 
2 /*
3  * Copyright 2017 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 #include "Sample.h"
9 #include "SkAnimTimer.h"
10 #include "SkBlurMask.h"
11 #include "SkBlurMaskFilter.h"
12 #include "SkColorFilter.h"
13 #include "SkCamera.h"
14 #include "SkCanvas.h"
15 #include "SkPath.h"
16 #include "SkPathOps.h"
17 #include "SkPoint3.h"
18 #include "SkShadowUtils.h"
19 #include "SkUTF.h"
20 #include "sk_tool_utils.h"
21 
22 ////////////////////////////////////////////////////////////////////////////
23 
24 class ShadowUtilsView : public Sample {
25     SkTArray<SkPath> fConvexPaths;
26     SkTArray<SkPath> fConcavePaths;
27     SkScalar         fZDelta;
28 
29     bool      fShowAmbient;
30     bool      fShowSpot;
31     bool      fUseAlt;
32     bool      fShowObject;
33     bool      fIgnoreShadowAlpha;
34 
35 public:
ShadowUtilsView()36     ShadowUtilsView()
37         : fZDelta(0)
38         , fShowAmbient(true)
39         , fShowSpot(true)
40         , fUseAlt(false)
41         , fShowObject(false)
42         , fIgnoreShadowAlpha(false) {}
43 
44 protected:
onOnceBeforeDraw()45     void onOnceBeforeDraw() override {
46         fConvexPaths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
47         SkRRect oddRRect;
48         oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
49         fConvexPaths.push_back().addRRect(oddRRect);
50         fConvexPaths.push_back().addRect(SkRect::MakeWH(50, 50));
51         fConvexPaths.push_back().addCircle(25, 25, 25);
52         fConvexPaths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
53         fConvexPaths.push_back().addOval(SkRect::MakeWH(20, 60));
54 
55         // star
56         fConcavePaths.push_back().moveTo(0.0f, -33.3333f);
57         fConcavePaths.back().lineTo(9.62f, -16.6667f);
58         fConcavePaths.back().lineTo(28.867f, -16.6667f);
59         fConcavePaths.back().lineTo(19.24f, 0.0f);
60         fConcavePaths.back().lineTo(28.867f, 16.6667f);
61         fConcavePaths.back().lineTo(9.62f, 16.6667f);
62         fConcavePaths.back().lineTo(0.0f, 33.3333f);
63         fConcavePaths.back().lineTo(-9.62f, 16.6667f);
64         fConcavePaths.back().lineTo(-28.867f, 16.6667f);
65         fConcavePaths.back().lineTo(-19.24f, 0.0f);
66         fConcavePaths.back().lineTo(-28.867f, -16.6667f);
67         fConcavePaths.back().lineTo(-9.62f, -16.6667f);
68         fConcavePaths.back().close();
69 
70         // dumbbell
71         fConcavePaths.push_back().moveTo(50, 0);
72         fConcavePaths.back().cubicTo(100, 25, 60, 50, 50, 0);
73         fConcavePaths.back().cubicTo(0, -25, 40, -50, 50, 0);
74     }
75 
onQuery(Sample::Event * evt)76     bool onQuery(Sample::Event* evt) override {
77         if (Sample::TitleQ(*evt)) {
78             Sample::TitleR(evt, "ShadowUtils");
79             return true;
80         }
81 
82         SkUnichar uni;
83         if (Sample::CharQ(*evt, &uni)) {
84             bool handled = false;
85             switch (uni) {
86                 case 'W':
87                     fShowAmbient = !fShowAmbient;
88                     handled = true;
89                     break;
90                 case 'S':
91                     fShowSpot = !fShowSpot;
92                     handled = true;
93                     break;
94                 case 'T':
95                     fUseAlt = !fUseAlt;
96                     handled = true;
97                     break;
98                 case 'O':
99                     fShowObject = !fShowObject;
100                     handled = true;
101                     break;
102                 case '>':
103                     fZDelta += 0.5f;
104                     handled = true;
105                     break;
106                 case '<':
107                     fZDelta -= 0.5f;
108                     handled = true;
109                     break;
110                 case '?':
111                     fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
112                     handled = true;
113                     break;
114                 default:
115                     break;
116             }
117             if (handled) {
118                 return true;
119             }
120         }
121         return this->INHERITED::onQuery(evt);
122     }
123 
drawBG(SkCanvas * canvas)124     void drawBG(SkCanvas* canvas) {
125         canvas->drawColor(0xFFFFFFFF);
126     }
127 
drawShadowedPath(SkCanvas * canvas,const SkPath & path,const SkPoint3 & zPlaneParams,const SkPaint & paint,SkScalar ambientAlpha,const SkPoint3 & lightPos,SkScalar lightWidth,SkScalar spotAlpha,uint32_t flags)128     void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
129                           const SkPoint3& zPlaneParams,
130                           const SkPaint& paint, SkScalar ambientAlpha,
131                           const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha,
132                           uint32_t flags) {
133         if (fIgnoreShadowAlpha) {
134             ambientAlpha = 255;
135             spotAlpha = 255;
136         }
137         if (!fShowAmbient) {
138             ambientAlpha = 0;
139         }
140         if (!fShowSpot) {
141             spotAlpha = 0;
142         }
143         if (fUseAlt) {
144             flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
145         }
146 
147         SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 255, 0, 0);
148         SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 255);
149         SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
150                                   lightPos, lightWidth,
151                                   ambientColor, spotColor, flags);
152 
153         if (fShowObject) {
154             canvas->drawPath(path, paint);
155         } else {
156             SkPaint strokePaint;
157 
158             strokePaint.setColor(paint.getColor());
159             strokePaint.setStyle(SkPaint::kStroke_Style);
160 
161             canvas->drawPath(path, strokePaint);
162         }
163     }
164 
onDrawContent(SkCanvas * canvas)165     void onDrawContent(SkCanvas* canvas) override {
166         this->drawBG(canvas);
167 
168         static constexpr int kW = 800;
169         static constexpr SkScalar kPad = 15.f;
170         static constexpr SkScalar kLightR = 100.f;
171         static constexpr SkScalar kHeight = 50.f;
172         static constexpr SkScalar kAmbientAlpha = 0.5f;
173         static constexpr SkScalar kSpotAlpha = 0.5f;
174         static constexpr SkPoint3 lightPos = { 250, 400, 500 };
175 
176         canvas->translate(3 * kPad, 3 * kPad);
177         canvas->save();
178         SkScalar x = 0;
179         SkScalar dy = 0;
180         SkTDArray<SkMatrix> matrices;
181         matrices.push()->reset();
182         SkMatrix* m = matrices.push();
183         m->setRotate(33.f, 25.f, 25.f);
184         m->postScale(1.2f, 0.8f, 25.f, 25.f);
185         SkPaint paint;
186         paint.setColor(SK_ColorGREEN);
187         paint.setAntiAlias(true);
188         SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, SkTMax(1.0f, kHeight + fZDelta));
189 
190         // convex paths
191         for (auto& m : matrices) {
192             for (auto flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
193                 for (const auto& path : fConvexPaths) {
194                     SkRect postMBounds = path.getBounds();
195                     m.mapRect(&postMBounds);
196                     SkScalar w = postMBounds.width() + kHeight;
197                     SkScalar dx = w + kPad;
198                     if (x + dx > kW - 3 * kPad) {
199                         canvas->restore();
200                         canvas->translate(0, dy);
201                         canvas->save();
202                         x = 0;
203                         dy = 0;
204                     }
205 
206                     canvas->save();
207                     canvas->concat(m);
208                     this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha,
209                                            lightPos, kLightR, kSpotAlpha, flags);
210                     canvas->restore();
211 
212                     canvas->translate(dx, 0);
213                     x += dx;
214                     dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
215                 }
216             }
217         }
218 
219         // concave paths
220         canvas->restore();
221         canvas->translate(kPad, dy);
222         canvas->save();
223         x = kPad;
224         dy = 0;
225         for (auto& m : matrices) {
226             for (const auto& path : fConcavePaths) {
227                 SkRect postMBounds = path.getBounds();
228                 m.mapRect(&postMBounds);
229                 SkScalar w = postMBounds.width();
230                 SkScalar dx = w + kPad;
231 
232                 canvas->save();
233                 canvas->concat(m);
234                 this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha, lightPos,
235                                        kLightR, kSpotAlpha, kNone_ShadowFlag);
236                 canvas->restore();
237 
238                 canvas->translate(dx, 0);
239                 x += dx;
240                 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
241             }
242         }
243 
244         // Show where the light is in x,y as a circle (specified in device space).
245         SkMatrix invCanvasM = canvas->getTotalMatrix();
246         if (invCanvasM.invert(&invCanvasM)) {
247             canvas->save();
248             canvas->concat(invCanvasM);
249             SkPaint paint;
250             paint.setColor(SK_ColorBLACK);
251             paint.setAntiAlias(true);
252             canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
253             canvas->restore();
254         }
255     }
256 
257 private:
258     typedef Sample INHERITED;
259 };
260 
261 //////////////////////////////////////////////////////////////////////////////
262 
263 DEF_SAMPLE( return new ShadowUtilsView(); )
264