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 "include/core/SkCanvas.h"
9#include "include/core/SkColorFilter.h"
10#include "include/core/SkPath.h"
11#include "include/core/SkPoint3.h"
12#include "include/pathops/SkPathOps.h"
13#include "include/utils/SkCamera.h"
14#include "include/utils/SkShadowUtils.h"
15#include "samplecode/Sample.h"
16#include "src/core/SkBlurMask.h"
17#include "src/utils/SkUTF.h"
18#include "tools/ToolUtils.h"
19#include "tools/timer/TimeUtils.h"
20
21////////////////////////////////////////////////////////////////////////////
22
23class ShadowsView : public Sample {
24    SkPath    fRectPath;
25    SkPath    fRRPath;
26    SkPath    fCirclePath;
27    SkPath    fFunkyRRPath;
28    SkPath    fCubicPath;
29    SkPath    fStarPath;
30    SkPath    fSquareRRectPath;
31    SkPath    fWideRectPath;
32    SkPath    fWideOvalPath;
33    SkPath    fNotchPath;
34    SkPath    fTabPath;
35
36    SkPoint3  fLightPos;
37    SkScalar  fZDelta = 0;
38    SkScalar  fAnimTranslate = 0;
39    SkScalar  fAnimAngle = 0;
40    SkScalar  fAnimAlpha = 1;
41
42    bool      fShowAmbient = true;
43    bool      fShowSpot = true;
44    bool      fUseAlt = false;
45    bool      fShowObject = true;
46    bool      fIgnoreShadowAlpha = false;
47    bool      fDoAlphaAnimation = false;
48
49    void onOnceBeforeDraw() override {
50        fCirclePath.addCircle(0, 0, 50);
51        fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
52        fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
53        fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
54                                  40 * SK_Scalar1, 20 * SK_Scalar1,
55                                  SkPathDirection::kCW);
56        fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
57                           20 * SK_Scalar1, 100 * SK_Scalar1,
58                           0 * SK_Scalar1, 0 * SK_Scalar1);
59        fStarPath.moveTo(0.0f, -50.0f);
60        fStarPath.lineTo(14.43f, -25.0f);
61        fStarPath.lineTo(43.30f, -25.0f);
62        fStarPath.lineTo(28.86f, 0.0f);
63        fStarPath.lineTo(43.30f, 25.0f);
64        fStarPath.lineTo(14.43f, 25.0f);
65        fStarPath.lineTo(0.0f, 50.0f);
66        fStarPath.lineTo(-14.43f, 25.0f);
67        fStarPath.lineTo(-43.30f, 25.0f);
68        fStarPath.lineTo(-28.86f, 0.0f);
69        fStarPath.lineTo(-43.30f, -25.0f);
70        fStarPath.lineTo(-14.43f, -25.0f);
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        fNotchPath.moveTo(0, 80);
77        fNotchPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), -90, -90, false);
78        fNotchPath.lineTo(-75, 100);
79        fNotchPath.lineTo(-75, -100);
80        fNotchPath.lineTo(75, -100);
81        fNotchPath.lineTo(75, 100);
82        fNotchPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 0, -90, false);
83
84        fTabPath.moveTo(-75, -100);
85        fTabPath.lineTo(75, -100);
86        fTabPath.lineTo(75, 100);
87        fTabPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 0, 90, false);
88        fTabPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 90, 90, false);
89        fTabPath.lineTo(-75, 100);
90
91        fLightPos = SkPoint3::Make(350, 0, 600);
92    }
93
94    SkString name() override { return SkString("AndroidShadows"); }
95
96    bool onChar(SkUnichar uni) override {
97            bool handled = false;
98            switch (uni) {
99                case 'W':
100                    fShowAmbient = !fShowAmbient;
101                    handled = true;
102                    break;
103                case 'S':
104                    fShowSpot = !fShowSpot;
105                    handled = true;
106                    break;
107                case 'T':
108                    fUseAlt = !fUseAlt;
109                    handled = true;
110                    break;
111                case 'O':
112                    fShowObject = !fShowObject;
113                    handled = true;
114                    break;
115                case 'N':
116                    fDoAlphaAnimation = !fDoAlphaAnimation;
117                    if (!fDoAlphaAnimation) {
118                        fAnimAlpha = 1;
119                    }
120                    handled = true;
121                    break;
122                case '>':
123                    fZDelta += 0.5f;
124                    handled = true;
125                    break;
126                case '<':
127                    fZDelta -= 0.5f;
128                    handled = true;
129                    break;
130                case '?':
131                    fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
132                    handled = true;
133                    break;
134                default:
135                    break;
136            }
137            if (handled) {
138                return true;
139            }
140            return false;
141    }
142
143    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
144                          const SkPoint3& zPlaneParams,
145                          const SkPaint& paint, SkScalar ambientAlpha,
146                          const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
147        if (fIgnoreShadowAlpha) {
148            ambientAlpha = 1;
149            spotAlpha = 1;
150        }
151        if (!fShowAmbient) {
152            ambientAlpha = 0;
153        }
154        if (!fShowSpot) {
155            spotAlpha = 0;
156        }
157        uint32_t flags = 0;
158        if (fUseAlt) {
159            flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
160        }
161
162        SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 0, 0, 0);
163        SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 0);
164        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, lightPos, lightWidth,
165                                  ambientColor, spotColor, flags);
166
167        if (fShowObject) {
168            canvas->drawPath(path, paint);
169        } else {
170            SkPaint strokePaint;
171
172            strokePaint.setColor(paint.getColor());
173            strokePaint.setStyle(SkPaint::kStroke_Style);
174
175            canvas->drawPath(path, strokePaint);
176        }
177    }
178
179    void onDrawContent(SkCanvas* canvas) override {
180        canvas->drawColor(0xFFDDDDDD);
181
182        const SkScalar kLightWidth = 800;
183        const SkScalar kAmbientAlpha = 0.039f;
184        const SkScalar kSpotAlpha = 0.19f;
185
186        SkPaint paint;
187        paint.setAntiAlias(true);
188
189        SkPoint3 lightPos = fLightPos;
190        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, 0);
191
192        paint.setColor(SK_ColorWHITE);
193        canvas->translate(200, 90);
194        zPlaneParams.fZ = std::max(1.0f, 2 + fZDelta);
195        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
196                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
197
198        paint.setColor(SK_ColorRED);
199        canvas->translate(250, 0);
200        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
201        this->drawShadowedPath(canvas, fRectPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
202                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
203
204        paint.setColor(SK_ColorBLUE);
205        canvas->translate(-250, 110);
206        zPlaneParams.fZ = std::max(1.0f, 12 + fZDelta);
207        this->drawShadowedPath(canvas, fCirclePath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
208                               lightPos, kLightWidth, fAnimAlpha*0.5f);
209
210        paint.setColor(SK_ColorGREEN);
211        canvas->translate(250, 0);
212        zPlaneParams.fZ = std::max(1.0f, 64 + fZDelta);
213        this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
214                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
215
216        paint.setColor(SK_ColorYELLOW);
217        canvas->translate(-250, 110);
218        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
219        this->drawShadowedPath(canvas, fFunkyRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
220                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
221
222        paint.setColor(SK_ColorCYAN);
223        canvas->translate(250, 0);
224        zPlaneParams.fZ = std::max(1.0f, 16 + fZDelta);
225        this->drawShadowedPath(canvas, fCubicPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
226                               lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
227
228        paint.setColor(SK_ColorWHITE);
229        canvas->translate(250, -180);
230        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
231        this->drawShadowedPath(canvas, fStarPath, zPlaneParams, paint,
232                               kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
233
234        paint.setColor(SK_ColorWHITE);
235        canvas->translate(150, 0);
236        zPlaneParams.fZ = std::max(1.0f, 2 + fZDelta);
237        this->drawShadowedPath(canvas, fNotchPath, zPlaneParams, paint,
238                               kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
239
240        paint.setColor(SK_ColorWHITE);
241        canvas->translate(200, 0);
242        zPlaneParams.fZ = std::max(1.0f, 16 + fZDelta);
243        this->drawShadowedPath(canvas, fTabPath, zPlaneParams, paint,
244                               kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
245
246        // circular reveal
247        SkPath tmpPath;
248        SkPath tmpClipPath;
249        tmpClipPath.addCircle(fAnimTranslate, 0, 60);
250        Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
251
252        paint.setColor(SK_ColorMAGENTA);
253        canvas->translate(-725, 240);
254        zPlaneParams.fZ = std::max(1.0f, 32 + fZDelta);
255        this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
256                               lightPos, kLightWidth, .5f);
257
258        // path ops bug
259        SkPath tmpClipPathBug;
260        tmpClipPathBug.addCircle(88.0344925f, 0, 60);
261        Op(fSquareRRectPath, tmpClipPathBug, kIntersect_SkPathOp, &tmpPath);
262
263        canvas->translate(250, 0);
264        zPlaneParams.fZ = std::max(1.0f, 32 + fZDelta);
265        this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
266                               lightPos, kLightWidth, .5f);
267
268        // perspective paths
269        SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
270                                      fWideRectPath.getBounds().height()/2);
271        SkPoint translate = SkPoint::Make(100, 450);
272        paint.setColor(SK_ColorWHITE);
273        Sk3DView view;
274        view.save();
275        view.rotateX(fAnimAngle);
276        SkMatrix persp;
277        view.getMatrix(&persp);
278        persp.preTranslate(-pivot.fX, -pivot.fY);
279        persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
280        canvas->setMatrix(persp);
281        SkScalar radians = SkDegreesToRadians(fAnimAngle);
282        zPlaneParams = SkPoint3::Make(0,
283                                      SkScalarSin(radians),
284                                      std::max(1.0f, 16 + fZDelta) - SkScalarSin(radians)*pivot.fY);
285        this->drawShadowedPath(canvas, fWideRectPath, zPlaneParams, paint, .1f,
286                               lightPos, kLightWidth, .5f);
287
288        pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
289                              fWideOvalPath.getBounds().height() / 2);
290        translate = SkPoint::Make(100, 600);
291        view.restore();
292        view.save();
293        view.rotateY(fAnimAngle);
294        view.getMatrix(&persp);
295        persp.preTranslate(-pivot.fX, -pivot.fY);
296        persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
297        canvas->setMatrix(persp);
298        zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
299                                      0,
300                                      std::max(1.0f, 32 + fZDelta) + SkScalarSin(radians)*pivot.fX);
301        this->drawShadowedPath(canvas, fWideOvalPath, zPlaneParams, paint, .1f,
302                               lightPos, kLightWidth, .5f);
303
304        pivot = SkPoint::Make(fStarPath.getBounds().width() / 2,
305                              fStarPath.getBounds().height() / 2);
306        translate = SkPoint::Make(700, 250);
307        view.restore();
308        view.rotateY(fAnimAngle);
309        view.getMatrix(&persp);
310        persp.preTranslate(-pivot.fX, -pivot.fY);
311        persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
312        canvas->setMatrix(persp);
313        zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
314                                      0,
315                                      std::max(1.0f, 8 + fZDelta) + SkScalarSin(radians)*pivot.fX);
316        this->drawShadowedPath(canvas, fStarPath, zPlaneParams, paint, .1f,
317                               lightPos, kLightWidth, .5f);
318    }
319
320    bool onAnimate(double nanos) override {
321        fAnimTranslate = TimeUtils::PingPong(1e-9 * nanos, 30, 0, 125, -125);
322        fAnimAngle = TimeUtils::PingPong(1e-9 * nanos, 15, 0, 0, 20);
323        if (fDoAlphaAnimation) {
324            fAnimAlpha = TimeUtils::PingPong(1e-9 * nanos, 5, 0, 1, 0);
325        }
326        return true;
327    }
328
329private:
330    using INHERITED = Sample;
331};
332
333//////////////////////////////////////////////////////////////////////////////
334
335DEF_SAMPLE( return new ShadowsView(); )
336