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