blob: c7a2f63db17ab9982ec7384607be5d453038e2f4 [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"
jvanverth6c177a12016-08-17 07:59:41 -070012#include "SkGaussianEdgeShader.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070013#include "SkPath.h"
14#include "SkPoint3.h"
15#include "SkUtils.h"
16#include "SkView.h"
17#include "sk_tool_utils.h"
18
jvanverth6c177a12016-08-17 07:59:41 -070019////////////////////////////////////////////////////////////////////////////
20
jvanverthe1a3bc62016-08-12 10:40:38 -070021class ShadowsView : public SampleView {
22 SkPath fRectPath;
23 SkPath fRRPath;
24 SkPath fCirclePath;
25 SkPoint3 fLightPos;
26
27 bool fShowAmbient;
28 bool fShowSpot;
jvanverthd7315f912016-08-17 10:06:18 -070029 bool fUseAlt;
jvanverthe1a3bc62016-08-12 10:40:38 -070030 bool fShowObject;
31
32public:
33 ShadowsView()
34 : fShowAmbient(true)
35 , fShowSpot(true)
jvanverthd7315f912016-08-17 10:06:18 -070036 , fUseAlt(true)
jvanverthe1a3bc62016-08-12 10:40:38 -070037 , fShowObject(true) {}
38
39protected:
40 void onOnceBeforeDraw() override {
41 fCirclePath.addCircle(0, 0, 50);
42 fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
43 fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
jvanverthd7315f912016-08-17 10:06:18 -070044 fLightPos = SkPoint3::Make(-2, -2, 6);
jvanverthe1a3bc62016-08-12 10:40:38 -070045 }
46
47 // overrides from SkEventSink
48 bool onQuery(SkEvent* evt) override {
49 if (SampleCode::TitleQ(*evt)) {
50 SampleCode::TitleR(evt, "AndroidShadows");
51 return true;
52 }
53
54 SkUnichar uni;
55 if (SampleCode::CharQ(*evt, &uni)) {
56 switch (uni) {
57 case 'B':
58 fShowAmbient = !fShowAmbient;
59 break;
60 case 'S':
61 fShowSpot = !fShowSpot;
62 break;
jvanverthd7315f912016-08-17 10:06:18 -070063 case 'T':
64 fUseAlt = !fUseAlt;
65 break;
jvanverthe1a3bc62016-08-12 10:40:38 -070066 case 'O':
67 fShowObject = !fShowObject;
68 break;
69 case '>':
70 fLightPos.fZ += 10;
71 break;
72 case '<':
73 fLightPos.fZ -= 10;
74 break;
75 default:
76 break;
77 }
78 this->inval(nullptr);
79 }
80 return this->INHERITED::onQuery(evt);
81 }
82
83 void drawBG(SkCanvas* canvas) {
84 canvas->drawColor(0xFFDDDDDD);
85 }
86
87 static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
88 SkRect pathRect;
89 SkRRect pathRRect;
90 if (path.isOval(&pathRect)) {
91 *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
92 } else if (path.isRRect(&pathRRect)) {
93 *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
94 } else if (path.isRect(occlRect)) {
95 // the inverse transform for the spot shadow occluder doesn't always get us
96 // back to exactly the same position, so deducting a little slop
97 occlRect->inset(1, 1);
98 } else {
99 *occlRect = SkRect::MakeEmpty();
100 }
101 }
102
103 void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
104 SkScalar ambientAlpha) {
105
106 if (ambientAlpha <= 0) {
107 return;
108 }
109
110 const SkScalar kHeightFactor = 1.f / 128.f;
111 const SkScalar kGeomFactor = 64;
112
113 SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
114 SkScalar radius = zValue*kHeightFactor*kGeomFactor;
115
116 // occlude blur
117 SkRect occlRect;
118 GetOcclRect(path, &occlRect);
119 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
120 SkBlurMask::ConvertRadiusToSigma(radius),
121 occlRect,
122 SkBlurMaskFilter::kNone_BlurFlag);
123
124 SkPaint paint;
125 paint.setAntiAlias(true);
126 paint.setMaskFilter(std::move(mf));
127 paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
128 canvas->drawPath(path, paint);
129
130 // draw occlusion rect
jvanverth6c177a12016-08-17 07:59:41 -0700131#if DRAW_OCCL_RECT
jvanverthe1a3bc62016-08-12 10:40:38 -0700132 SkPaint stroke;
133 stroke.setStyle(SkPaint::kStroke_Style);
134 stroke.setColor(SK_ColorBLUE);
135 canvas->drawRect(occlRect, stroke);
jvanverth6c177a12016-08-17 07:59:41 -0700136#endif
137 }
138
139 void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
140 SkScalar ambientAlpha) {
141
142 if (ambientAlpha <= 0) {
143 return;
144 }
145
jvanverthd7315f912016-08-17 10:06:18 -0700146 SkRect pathRect;
147 SkRRect pathRRect;
148 if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
149 (!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
150 !path.isRect(&pathRect)) {
151 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
152 return;
153 }
154
jvanverth6c177a12016-08-17 07:59:41 -0700155 const SkScalar kHeightFactor = 1.f / 128.f;
156 const SkScalar kGeomFactor = 64;
157
158 SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
159 SkScalar radius = zValue*kHeightFactor*kGeomFactor;
160
jvanverthd7315f912016-08-17 10:06:18 -0700161 // For all of these, we outset the rect by the radius to get our coverage shape.
162 if (path.isOval(nullptr)) {
163 pathRect.outset(radius, radius);
164 pathRRect = SkRRect::MakeOval(pathRect);
165 } else if (path.isRect(nullptr)) {
166 pathRect.outset(radius, radius);
167 pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
jvanverth6c177a12016-08-17 07:59:41 -0700168 } else {
jvanverthd7315f912016-08-17 10:06:18 -0700169 pathRRect.outset(radius, radius);
jvanverth6c177a12016-08-17 07:59:41 -0700170 }
171
jvanverthd7315f912016-08-17 10:06:18 -0700172 SkPaint paint;
173 paint.setAntiAlias(true);
174 // handle scale of radius due to CTM
175 SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
176 radius *= maxScale;
177 unsigned char gray = (unsigned char)(ambientAlpha*umbraAlpha*255.999f);
178 SkASSERT(radius < 256);
179 // convert the radius to fixed point 8.8 and
180 // place it in the G,B components of the color
181 unsigned char intPart = (unsigned char)radius;
182 SkScalar fracPart = radius - intPart;
183 paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
184
fmalita9da5dbd2016-08-17 11:33:29 -0700185 sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
jvanverthd7315f912016-08-17 10:06:18 -0700186 paint.setShader(gaussShader);
187 canvas->drawRRect(pathRRect, paint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700188 }
189
190 void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
191 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
192 if (spotAlpha <= 0) {
193 return;
194 }
195
196 SkScalar zRatio = zValue / (lightPos.fZ - zValue);
197 if (zRatio < 0.0f) {
198 zRatio = 0.0f;
199 } else if (zRatio > 0.95f) {
200 zRatio = 0.95f;
201 }
202 SkScalar radius = lightWidth*zRatio;
203
204 // compute the transformation params
205 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
206 canvas->getTotalMatrix().mapPoints(&center, 1);
207 SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
208 -zRatio*(lightPos.fY - center.fY));
209 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
210 if (scale < 1.0f) {
211 scale = 1.0f;
212 } else if (scale > 1024.f) {
213 scale = 1024.f;
214 }
215
216 SkAutoCanvasRestore acr(canvas, true);
217
218 SkRect occlRect;
219 GetOcclRect(path, &occlRect);
220 // apply inverse transform
221 occlRect.offset(-offset);
jvanverthd7315f912016-08-17 10:06:18 -0700222#if 0
223 // It looks like the scale may be invalid
224 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
225 if (scale < 1.0f) {
226 scale = 1.0f;
227 } else if (scale > 1024.f) {
228 scale = 1024.f;
229 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700230 occlRect.fLeft /= scale;
231 occlRect.fRight /= scale;
232 occlRect.fTop /= scale;
233 occlRect.fBottom /= scale;
jvanverthd7315f912016-08-17 10:06:18 -0700234#endif
jvanverthe1a3bc62016-08-12 10:40:38 -0700235 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
236 SkBlurMask::ConvertRadiusToSigma(radius),
237 occlRect,
238 SkBlurMaskFilter::kNone_BlurFlag);
239
240 SkPaint paint;
241 paint.setAntiAlias(true);
242 paint.setMaskFilter(std::move(mf));
243 paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
244
245 // apply transformation to shadow
246 canvas->translate(offset.fX, offset.fY);
jvanverthd7315f912016-08-17 10:06:18 -0700247#if 0
248 // It looks like the scale may be invalid
jvanverthe1a3bc62016-08-12 10:40:38 -0700249 canvas->scale(scale, scale);
jvanverthd7315f912016-08-17 10:06:18 -0700250#endif
jvanverthe1a3bc62016-08-12 10:40:38 -0700251 canvas->drawPath(path, paint);
252
253 // draw occlusion rect
jvanverth6c177a12016-08-17 07:59:41 -0700254#if DRAW_OCCL_RECT
jvanverthe1a3bc62016-08-12 10:40:38 -0700255 SkPaint stroke;
256 stroke.setStyle(SkPaint::kStroke_Style);
257 stroke.setColor(SK_ColorRED);
jvanverth6c177a12016-08-17 07:59:41 -0700258 canvas->drawRect(occlRect, stroke)
259#endif
jvanverthe1a3bc62016-08-12 10:40:38 -0700260 }
261
jvanverthd7315f912016-08-17 10:06:18 -0700262 void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
263 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
264 if (spotAlpha <= 0) {
265 return;
266 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700267
jvanverthd7315f912016-08-17 10:06:18 -0700268 SkRect pathRect;
269 SkRRect pathRRect;
270 if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
271 (!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
272 !path.isRect(&pathRect)) {
273 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
274 return;
275 }
276
277 SkScalar zRatio = zValue / (lightPos.fZ - zValue);
278 if (zRatio < 0.0f) {
279 zRatio = 0.0f;
280 } else if (zRatio > 0.95f) {
281 zRatio = 0.95f;
282 }
283 SkScalar radius = lightWidth*zRatio;
284
285 // For all of these, we outset the rect by the radius to get our coverage shape.
286 if (path.isOval(nullptr)) {
287 pathRect.outset(radius, radius);
288 pathRRect = SkRRect::MakeOval(pathRect);
289 } else if (path.isRect(nullptr)) {
290 pathRect.outset(radius, radius);
291 pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
292 } else {
293 pathRRect.outset(radius, radius);
294 }
295
296 // compute the transformation params
297 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
298 canvas->getTotalMatrix().mapPoints(&center, 1);
299 SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
300 -zRatio*(lightPos.fY - center.fY));
301 SkAutoCanvasRestore acr(canvas, true);
302
303 SkPaint paint;
304 paint.setAntiAlias(true);
fmalita9da5dbd2016-08-17 11:33:29 -0700305 sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
jvanverthd7315f912016-08-17 10:06:18 -0700306 paint.setShader(gaussShader);
307 // handle scale of radius due to CTM
308 SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
309 radius *= maxScale;
310 unsigned char gray = (unsigned char)(spotAlpha*255.999f);
311 SkASSERT(radius < 256);
312 // convert the radius to fixed point 8.8 and
313 // place it in the G,B components of the color
314 unsigned char intPart = (unsigned char)radius;
315 SkScalar fracPart = radius - intPart;
316 paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
317
318 // apply transformation to shadow
319 canvas->translate(offset.fX, offset.fY);
320#if 0
321 // It looks like the scale may be invalid
322 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
323 if (scale < 1.0f) {
324 scale = 1.0f;
325 } else if (scale > 1024.f) {
326 scale = 1024.f;
327 }
328 canvas->scale(scale, scale);
329#endif
330 canvas->drawRRect(pathRRect, paint);
331 }
332
333 void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
334 const SkPaint& paint, SkScalar ambientAlpha,
335 const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
jvanverthe1a3bc62016-08-12 10:40:38 -0700336 if (fShowAmbient) {
jvanverthd7315f912016-08-17 10:06:18 -0700337 if (fUseAlt) {
338 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700339 } else {
jvanverthd7315f912016-08-17 10:06:18 -0700340 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700341 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700342 }
343 if (fShowSpot) {
jvanverthd7315f912016-08-17 10:06:18 -0700344 if (fUseAlt) {
345 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
346 } else {
347 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
348 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700349 }
350 if (fShowObject) {
351 canvas->drawPath(path, paint);
352 }
353 }
354
355 void onDrawContent(SkCanvas* canvas) override {
356 this->drawBG(canvas);
jvanverthd7315f912016-08-17 10:06:18 -0700357 const SkScalar kLightWidth = 3;
358 const SkScalar kAmbientAlpha = 0.25f;
359 const SkScalar kSpotAlpha = 0.25f;
jvanverthe1a3bc62016-08-12 10:40:38 -0700360
361 SkPaint paint;
362 paint.setAntiAlias(true);
363
jvanverthd7315f912016-08-17 10:06:18 -0700364 SkPoint3 lightPos = fLightPos;
365
jvanverthe1a3bc62016-08-12 10:40:38 -0700366 paint.setColor(SK_ColorWHITE);
367 canvas->translate(200, 90);
jvanverthd7315f912016-08-17 10:06:18 -0700368 lightPos.fX += 200;
369 lightPos.fY += 90;
370 this->drawShadowedPath(canvas, fRectPath, 5, paint, kAmbientAlpha,
371 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700372
373 paint.setColor(SK_ColorRED);
374 canvas->translate(250, 0);
jvanverthd7315f912016-08-17 10:06:18 -0700375 lightPos.fX += 250;
376 this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
377 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700378
379 paint.setColor(SK_ColorBLUE);
380 canvas->translate(-250, 110);
jvanverthd7315f912016-08-17 10:06:18 -0700381 lightPos.fX -= 250;
382 lightPos.fY += 110;
383 this->drawShadowedPath(canvas, fCirclePath, 5, paint, 0.0f,
384 lightPos, kLightWidth, 0.5f);
jvanverth6c177a12016-08-17 07:59:41 -0700385
386 paint.setColor(SK_ColorGREEN);
387 canvas->translate(250, 0);
jvanverthd7315f912016-08-17 10:06:18 -0700388 lightPos.fX += 250;
389 this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
390 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700391 }
392
393protected:
394 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
395 return new SkView::Click(this);
396 }
397
398 bool onClick(Click *click) override {
399 SkScalar x = click->fCurr.fX;
400 SkScalar y = click->fCurr.fY;
401
402 SkScalar dx = x - click->fPrev.fX;
403 SkScalar dy = y - click->fPrev.fY;
404
405 if (dx != 0 || dy != 0) {
406 fLightPos.fX += dx;
407 fLightPos.fY += dy;
408 this->inval(nullptr);
409 }
410
411 return true;
412 }
413
414private:
415 typedef SkView INHERITED;
416};
417
418//////////////////////////////////////////////////////////////////////////////
419
420static SkView* MyFactory() { return new ShadowsView; }
421static SkViewRegister reg(MyFactory);