1
2/*
3 * Copyright 2016 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 "SampleCode.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 "SkUtils.h"
20#include "SkView.h"
21#include "sk_tool_utils.h"
22
23////////////////////////////////////////////////////////////////////////////
24
25class ShadowsView : public SampleView {
26    SkPath    fRectPath;
27    SkPath    fRRPath;
28    SkPath    fCirclePath;
29    SkPath    fFunkyRRPath;
30    SkPath    fCubicPath;
31    SkPath    fSquareRRectPath;
32    SkPath    fWideRectPath;
33    SkPath    fWideOvalPath;
34    SkPoint3  fLightPos;
35    SkScalar  fZDelta;
36    SkScalar  fAnimTranslate;
37    SkScalar  fAnimAngle;
38    SkScalar  fAnimAlpha;
39
40    bool      fShowAmbient;
41    bool      fShowSpot;
42    bool      fUseAlt;
43    bool      fShowObject;
44    bool      fIgnoreShadowAlpha;
45    bool      fDoAlphaAnimation;
46
47public:
48    ShadowsView()
49        : fZDelta(0)
50        , fAnimTranslate(0)
51        , fAnimAngle(0)
52        , fAnimAlpha(1)
53        , fShowAmbient(true)
54        , fShowSpot(true)
55        , fUseAlt(false)
56        , fShowObject(true)
57        , fIgnoreShadowAlpha(false)
58        , fDoAlphaAnimation(false) {}
59
60protected:
61    void onOnceBeforeDraw() override {
62        fCirclePath.addCircle(0, 0, 50);
63        fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
64        fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
65        fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
66                                  40 * SK_Scalar1, 20 * SK_Scalar1,
67                                  SkPath::kCW_Direction);
68        fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
69                           20 * SK_Scalar1, 100 * SK_Scalar1,
70                           0 * SK_Scalar1, 0 * SK_Scalar1);
71        fSquareRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-50, -50, 100, 100),
72                                                      10, 10));
73        fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
74        fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
75
76        fLightPos = SkPoint3::Make(350, 0, 600);
77    }
78
79    // overrides from SkEventSink
80    bool onQuery(SkEvent* evt) override {
81        if (SampleCode::TitleQ(*evt)) {
82            SampleCode::TitleR(evt, "AndroidShadows");
83            return true;
84        }
85
86        SkUnichar uni;
87        if (SampleCode::CharQ(*evt, &uni)) {
88            bool handled = false;
89            switch (uni) {
90                case 'W':
91                    fShowAmbient = !fShowAmbient;
92                    handled = true;
93                    break;
94                case 'S':
95                    fShowSpot = !fShowSpot;
96                    handled = true;
97                    break;
98                case 'T':
99                    fUseAlt = !fUseAlt;
100                    handled = true;
101                    break;
102                case 'O':
103                    fShowObject = !fShowObject;
104                    handled = true;
105                    break;
106                case 'N':
107                    fDoAlphaAnimation = !fDoAlphaAnimation;
108                    if (!fDoAlphaAnimation) {
109                        fAnimAlpha = 1;
110                    }
111                    handled = true;
112                    break;
113                case '>':
114                    fZDelta += 0.5f;
115                    handled = true;
116                    break;
117                case '<':
118                    fZDelta -= 0.5f;
119                    handled = true;
120                    break;
121                case '?':
122                    fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
123                    handled = true;
124                    break;
125                default:
126                    break;
127            }
128            if (handled) {
129                return true;
130            }
131        }
132        return this->INHERITED::onQuery(evt);
133    }
134
135    void drawBG(SkCanvas* canvas) {
136        canvas->drawColor(0xFFDDDDDD);
137    }
138
139    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
140                          const SkPoint3& zPlaneParams,
141                          const SkPaint& paint, SkScalar ambientAlpha,
142                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
143        if (fIgnoreShadowAlpha) {
144            ambientAlpha = 255;
145            spotAlpha = 255;
146        }
147        if (!fShowAmbient) {
148            ambientAlpha = 0;
149        }
150        if (!fShowSpot) {
151            spotAlpha = 0;
152        }
153        uint32_t flags = 0;
154        if (fUseAlt) {
155            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
156        }
157
158        SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 0, 0, 0);
159        SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 0);
160        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, lightPos, lightWidth,
161                                  ambientColor, spotColor, flags);
162
163        if (fShowObject) {
164            canvas->drawPath(path, paint);
165        } else {
166            SkPaint strokePaint;
167
168            strokePaint.setColor(paint.getColor());
169            strokePaint.setStyle(SkPaint::kStroke_Style);
170
171            canvas->drawPath(path, strokePaint);
172        }
173    }
174
175    void onDrawContent(SkCanvas* canvas) override {
176        this->drawBG(canvas);
177        const SkScalar kLightWidth = 800;
178        const SkScalar kAmbientAlpha = 0.1f;
179        const SkScalar kSpotAlpha = 0.25f;
180
181        SkPaint paint;
182        paint.setAntiAlias(true);
183
184        SkPoint3 lightPos = fLightPos;
185        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, 0);
186
187        paint.setColor(SK_ColorWHITE);
188        canvas->translate(200, 90);
189        zPlaneParams.fZ = SkTMax(1.0f, 2 + fZDelta);
190        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
191                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
192
193        paint.setColor(SK_ColorRED);
194        canvas->translate(250, 0);
195        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
196        this->drawShadowedPath(canvas, fRectPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
197                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
198
199        paint.setColor(SK_ColorBLUE);
200        canvas->translate(-250, 110);
201        zPlaneParams.fZ = SkTMax(1.0f, 12 + fZDelta);
202        this->drawShadowedPath(canvas, fCirclePath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
203                               lightPos, kLightWidth, fAnimAlpha*0.5f);
204
205        paint.setColor(SK_ColorGREEN);
206        canvas->translate(250, 0);
207        zPlaneParams.fZ = SkTMax(1.0f, 64 + fZDelta);
208        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
209                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
210
211        paint.setColor(SK_ColorYELLOW);
212        canvas->translate(-250, 110);
213        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
214        this->drawShadowedPath(canvas, fFunkyRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
215                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
216
217        paint.setColor(SK_ColorCYAN);
218        canvas->translate(250, 0);
219        zPlaneParams.fZ = SkTMax(1.0f, 16 + fZDelta);
220        this->drawShadowedPath(canvas, fCubicPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
221                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
222
223        // circular reveal
224        SkPath tmpPath;
225        SkPath tmpClipPath;
226        tmpClipPath.addCircle(fAnimTranslate, 0, 60);
227        Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
228
229        paint.setColor(SK_ColorMAGENTA);
230        canvas->translate(-125, 60);
231        zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
232        this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
233                               lightPos, kLightWidth, .5f);
234
235        // perspective paths
236        SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
237                                      fWideRectPath.getBounds().height()/2);
238        SkPoint translate = SkPoint::Make(100, 450);
239        paint.setColor(SK_ColorWHITE);
240        Sk3DView view;
241        view.save();
242        view.rotateX(fAnimAngle);
243        SkMatrix persp;
244        view.getMatrix(&persp);
245        persp.preTranslate(-pivot.fX, -pivot.fY);
246        persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
247        canvas->setMatrix(persp);
248        SkScalar radians = SkDegreesToRadians(fAnimAngle);
249        zPlaneParams = SkPoint3::Make(0,
250                                      SkScalarSin(-radians),
251                                      SkTMax(1.0f, 16 + fZDelta) - SkScalarSin(-radians)*pivot.fY);
252        this->drawShadowedPath(canvas, fWideRectPath, zPlaneParams, paint, .1f,
253                               lightPos, kLightWidth, .5f);
254
255        pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
256                              fWideOvalPath.getBounds().height() / 2);
257        translate = SkPoint::Make(100, 600);
258        view.restore();
259        view.rotateY(fAnimAngle);
260        view.getMatrix(&persp);
261        persp.preTranslate(-pivot.fX, -pivot.fY);
262        persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
263        canvas->setMatrix(persp);
264        zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
265                                      0,
266                                      SkTMax(1.0f, 32 + fZDelta) + SkScalarSin(radians)*pivot.fX);
267        this->drawShadowedPath(canvas, fWideOvalPath, zPlaneParams, paint, .1f,
268                               lightPos, kLightWidth, .5f);
269    }
270
271    bool onAnimate(const SkAnimTimer& timer) override {
272        fAnimTranslate = timer.pingPong(30, 0, 200, -200);
273        fAnimAngle = timer.pingPong(15, 0, 0, 20);
274        if (fDoAlphaAnimation) {
275            fAnimAlpha = timer.pingPong(5, 0, 1, 0);
276        }
277        return true;
278    }
279
280private:
281    typedef SampleView INHERITED;
282};
283
284//////////////////////////////////////////////////////////////////////////////
285
286static SkView* MyFactory() { return new ShadowsView; }
287static SkViewRegister reg(MyFactory);
288