blob: 9ab69a670c8f98a8d977c9eadd98fdbadaddbb07 [file] [log] [blame]
jvanverthe1a3bc62016-08-12 10:40:38 -07001
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 "SkBlurMask.h"
10#include "SkBlurMaskFilter.h"
11#include "SkCanvas.h"
12#include "SkPath.h"
13#include "SkPoint3.h"
14#include "SkUtils.h"
15#include "SkView.h"
16#include "sk_tool_utils.h"
17
18class ShadowsView : public SampleView {
19 SkPath fRectPath;
20 SkPath fRRPath;
21 SkPath fCirclePath;
22 SkPoint3 fLightPos;
23
24 bool fShowAmbient;
25 bool fShowSpot;
26 bool fShowObject;
27
28public:
29 ShadowsView()
30 : fShowAmbient(true)
31 , fShowSpot(true)
32 , fShowObject(true) {}
33
34protected:
35 void onOnceBeforeDraw() override {
36 fCirclePath.addCircle(0, 0, 50);
37 fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
38 fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
39 fLightPos = SkPoint3::Make(-220, -330, 150);
40 }
41
42 // overrides from SkEventSink
43 bool onQuery(SkEvent* evt) override {
44 if (SampleCode::TitleQ(*evt)) {
45 SampleCode::TitleR(evt, "AndroidShadows");
46 return true;
47 }
48
49 SkUnichar uni;
50 if (SampleCode::CharQ(*evt, &uni)) {
51 switch (uni) {
52 case 'B':
53 fShowAmbient = !fShowAmbient;
54 break;
55 case 'S':
56 fShowSpot = !fShowSpot;
57 break;
58 case 'O':
59 fShowObject = !fShowObject;
60 break;
61 case '>':
62 fLightPos.fZ += 10;
63 break;
64 case '<':
65 fLightPos.fZ -= 10;
66 break;
67 default:
68 break;
69 }
70 this->inval(nullptr);
71 }
72 return this->INHERITED::onQuery(evt);
73 }
74
75 void drawBG(SkCanvas* canvas) {
76 canvas->drawColor(0xFFDDDDDD);
77 }
78
79 static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
80 SkRect pathRect;
81 SkRRect pathRRect;
82 if (path.isOval(&pathRect)) {
83 *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
84 } else if (path.isRRect(&pathRRect)) {
85 *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
86 } else if (path.isRect(occlRect)) {
87 // the inverse transform for the spot shadow occluder doesn't always get us
88 // back to exactly the same position, so deducting a little slop
89 occlRect->inset(1, 1);
90 } else {
91 *occlRect = SkRect::MakeEmpty();
92 }
93 }
94
95 void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
96 SkScalar ambientAlpha) {
97
98 if (ambientAlpha <= 0) {
99 return;
100 }
101
102 const SkScalar kHeightFactor = 1.f / 128.f;
103 const SkScalar kGeomFactor = 64;
104
105 SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
106 SkScalar radius = zValue*kHeightFactor*kGeomFactor;
107
108 // occlude blur
109 SkRect occlRect;
110 GetOcclRect(path, &occlRect);
111 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
112 SkBlurMask::ConvertRadiusToSigma(radius),
113 occlRect,
114 SkBlurMaskFilter::kNone_BlurFlag);
115
116 SkPaint paint;
117 paint.setAntiAlias(true);
118 paint.setMaskFilter(std::move(mf));
119 paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
120 canvas->drawPath(path, paint);
121
122 // draw occlusion rect
123 SkPaint stroke;
124 stroke.setStyle(SkPaint::kStroke_Style);
125 stroke.setColor(SK_ColorBLUE);
126 canvas->drawRect(occlRect, stroke);
127 }
128
129 void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
130 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
131 if (spotAlpha <= 0) {
132 return;
133 }
134
135 SkScalar zRatio = zValue / (lightPos.fZ - zValue);
136 if (zRatio < 0.0f) {
137 zRatio = 0.0f;
138 } else if (zRatio > 0.95f) {
139 zRatio = 0.95f;
140 }
141 SkScalar radius = lightWidth*zRatio;
142
143 // compute the transformation params
144 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
145 canvas->getTotalMatrix().mapPoints(&center, 1);
146 SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
147 -zRatio*(lightPos.fY - center.fY));
148 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
149 if (scale < 1.0f) {
150 scale = 1.0f;
151 } else if (scale > 1024.f) {
152 scale = 1024.f;
153 }
154
155 SkAutoCanvasRestore acr(canvas, true);
156
157 SkRect occlRect;
158 GetOcclRect(path, &occlRect);
159 // apply inverse transform
160 occlRect.offset(-offset);
161 occlRect.fLeft /= scale;
162 occlRect.fRight /= scale;
163 occlRect.fTop /= scale;
164 occlRect.fBottom /= scale;
165 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
166 SkBlurMask::ConvertRadiusToSigma(radius),
167 occlRect,
168 SkBlurMaskFilter::kNone_BlurFlag);
169
170 SkPaint paint;
171 paint.setAntiAlias(true);
172 paint.setMaskFilter(std::move(mf));
173 paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
174
175 // apply transformation to shadow
176 canvas->translate(offset.fX, offset.fY);
177 canvas->scale(scale, scale);
178 canvas->drawPath(path, paint);
179
180 // draw occlusion rect
181 SkPaint stroke;
182 stroke.setStyle(SkPaint::kStroke_Style);
183 stroke.setColor(SK_ColorRED);
184 canvas->drawRect(occlRect, stroke);
185 }
186
187 void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
188 const SkPaint& paint) {
189 const SkScalar kLightWidth = 3;
190 const SkScalar kAmbientAlpha = 0.25f;
191 const SkScalar kSpotAlpha = 0.25f;
192
193 if (fShowAmbient) {
194 this->drawAmbientShadow(canvas, path, zValue, kAmbientAlpha);
195 }
196 if (fShowSpot) {
197 this->drawSpotShadow(canvas, path, zValue, fLightPos, kLightWidth, kSpotAlpha);
198 }
199 if (fShowObject) {
200 canvas->drawPath(path, paint);
201 }
202 }
203
204 void onDrawContent(SkCanvas* canvas) override {
205 this->drawBG(canvas);
206
207 SkPaint paint;
208 paint.setAntiAlias(true);
209
210 paint.setColor(SK_ColorWHITE);
211 canvas->translate(200, 90);
212 this->drawShadowedPath(canvas, fRectPath, 5, paint);
213
214 paint.setColor(SK_ColorRED);
215 canvas->translate(250, 0);
216 this->drawShadowedPath(canvas, fRRPath, 5, paint);
217
218 paint.setColor(SK_ColorBLUE);
219 canvas->translate(-250, 110);
220 this->drawShadowedPath(canvas, fCirclePath, 5, paint);
221 }
222
223protected:
224 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
225 return new SkView::Click(this);
226 }
227
228 bool onClick(Click *click) override {
229 SkScalar x = click->fCurr.fX;
230 SkScalar y = click->fCurr.fY;
231
232 SkScalar dx = x - click->fPrev.fX;
233 SkScalar dy = y - click->fPrev.fY;
234
235 if (dx != 0 || dy != 0) {
236 fLightPos.fX += dx;
237 fLightPos.fY += dy;
238 this->inval(nullptr);
239 }
240
241 return true;
242 }
243
244private:
245 typedef SkView INHERITED;
246};
247
248//////////////////////////////////////////////////////////////////////////////
249
250static SkView* MyFactory() { return new ShadowsView; }
251static SkViewRegister reg(MyFactory);