• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkGaussianEdgeShader.h"
16 #include "SkPath.h"
17 #include "SkPathOps.h"
18 #include "SkPoint3.h"
19 #include "SkShadowUtils.h"
20 #include "SkUtils.h"
21 #include "SkView.h"
22 #include "sk_tool_utils.h"
23 
24 #define USE_SHADOW_UTILS
25 
26 ////////////////////////////////////////////////////////////////////////////
27 
28 class ShadowsView : public SampleView {
29     SkPath    fRectPath;
30     SkPath    fRRPath;
31     SkPath    fCirclePath;
32     SkPath    fFunkyRRPath;
33     SkPath    fCubicPath;
34     SkPath    fSquareRRectPath;
35     SkPath    fWideRectPath;
36     SkPath    fWideOvalPath;
37     SkPoint3  fLightPos;
38     SkScalar  fZDelta;
39     SkScalar  fAnimTranslate;
40     SkScalar  fAnimAngle;
41 
42     bool      fShowAmbient;
43     bool      fShowSpot;
44     bool      fUseAlt;
45     bool      fShowObject;
46     bool      fIgnoreShadowAlpha;
47 
48 public:
ShadowsView()49     ShadowsView()
50         : fZDelta(0)
51         , fAnimTranslate(0)
52         , fAnimAngle(0)
53         , fShowAmbient(true)
54         , fShowSpot(true)
55         , fUseAlt(true)
56         , fShowObject(true)
57         , fIgnoreShadowAlpha(false) {}
58 
59 protected:
onOnceBeforeDraw()60     void onOnceBeforeDraw() override {
61         fCirclePath.addCircle(0, 0, 50);
62         fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
63         fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
64         fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
65                                   40 * SK_Scalar1, 20 * SK_Scalar1,
66                                   SkPath::kCW_Direction);
67         fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
68                            20 * SK_Scalar1, 100 * SK_Scalar1,
69                            0 * SK_Scalar1, 0 * SK_Scalar1);
70         fSquareRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-50, -50, 100, 100),
71                                                       10, 10));
72         fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
73         fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
74 
75         fLightPos = SkPoint3::Make(-700, -700, 2800);
76     }
77 
78     // overrides from SkEventSink
onQuery(SkEvent * evt)79     bool onQuery(SkEvent* evt) override {
80         if (SampleCode::TitleQ(*evt)) {
81             SampleCode::TitleR(evt, "AndroidShadows");
82             return true;
83         }
84 
85         SkUnichar uni;
86         if (SampleCode::CharQ(*evt, &uni)) {
87             bool handled = false;
88             switch (uni) {
89                 case 'W':
90                     fShowAmbient = !fShowAmbient;
91                     handled = true;
92                     break;
93                 case 'S':
94                     fShowSpot = !fShowSpot;
95                     handled = true;
96                     break;
97                 case 'T':
98                     fUseAlt = !fUseAlt;
99                     handled = true;
100                     break;
101                 case 'O':
102                     fShowObject = !fShowObject;
103                     handled = true;
104                     break;
105                 case '>':
106                     fZDelta += 0.5f;
107                     handled = true;
108                     break;
109                 case '<':
110                     fZDelta -= 0.5f;
111                     handled = true;
112                     break;
113                 case '?':
114                     fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
115                     handled = true;
116                     break;
117                 default:
118                     break;
119             }
120             if (handled) {
121                 this->inval(nullptr);
122                 return true;
123             }
124         }
125         return this->INHERITED::onQuery(evt);
126     }
127 
drawBG(SkCanvas * canvas)128     void drawBG(SkCanvas* canvas) {
129         canvas->drawColor(0xFFDDDDDD);
130     }
131 
GetOcclRect(const SkPath & path,SkRect * occlRect)132     static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
133         SkRect pathRect;
134         SkRRect pathRRect;
135         if (path.isOval(&pathRect)) {
136             *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
137         } else if (path.isRRect(&pathRRect)) {
138             *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
139         } else if (path.isRect(occlRect)) {
140             // the inverse transform for the spot shadow occluder doesn't always get us
141             // back to exactly the same position, so deducting a little slop
142             occlRect->inset(1, 1);
143         } else {
144             *occlRect = SkRect::MakeEmpty();
145         }
146     }
147 
drawAmbientShadow(SkCanvas * canvas,const SkPath & path,SkScalar zValue,SkScalar ambientAlpha)148     void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
149                            SkScalar ambientAlpha) {
150 
151         if (ambientAlpha <= 0) {
152             return;
153         }
154 
155         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 
161         // occlude blur
162         SkRect occlRect;
163         GetOcclRect(path, &occlRect);
164         sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
165                                                         SkBlurMask::ConvertRadiusToSigma(radius),
166                                                         occlRect,
167                                                         SkBlurMaskFilter::kNone_BlurFlag);
168 
169         SkPaint paint;
170         paint.setAntiAlias(true);
171         paint.setMaskFilter(std::move(mf));
172         paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
173                                     ? 255
174                                     : (unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
175         canvas->drawPath(path, paint);
176 
177         // draw occlusion rect
178 #if DRAW_OCCL_RECT
179         SkPaint stroke;
180         stroke.setStyle(SkPaint::kStroke_Style);
181         stroke.setColor(SK_ColorBLUE);
182         canvas->drawRect(occlRect, stroke);
183 #endif
184     }
185 
drawAmbientShadowAlt(SkCanvas * canvas,const SkPath & path,SkScalar zValue,SkScalar ambientAlpha)186     void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
187                               SkScalar ambientAlpha) {
188 
189         if (ambientAlpha <= 0) {
190             return;
191         }
192 
193         const SkScalar kHeightFactor = 1.f / 128.f;
194         const SkScalar kGeomFactor = 64;
195 
196         SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
197         SkScalar radius = zValue*kHeightFactor*kGeomFactor;
198         // distance to outer of edge of geometry from original shape edge
199         SkScalar offset = radius*umbraAlpha;
200 
201         SkRect pathRect;
202         SkRRect pathRRect;
203         SkScalar scaleFactors[2];
204         if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
205             return;
206         }
207         if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
208             !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
209               (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
210               path.isRect(&pathRect))) {
211             this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
212             return;
213         }
214 
215         // For all of these, we inset the offset rect by half the radius to get our stroke shape.
216         SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
217         // Make sure we'll have a radius of at least 0.5 after xform
218         if (strokeOutset*scaleFactors[0] < 0.5f) {
219             strokeOutset = 0.5f / scaleFactors[0];
220         }
221         if (path.isOval(nullptr)) {
222             pathRect.outset(strokeOutset, strokeOutset);
223             pathRRect = SkRRect::MakeOval(pathRect);
224         } else if (path.isRect(nullptr)) {
225             pathRect.outset(strokeOutset, strokeOutset);
226             pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
227         } else {
228             pathRRect.outset(strokeOutset, strokeOutset);
229         }
230 
231         SkPaint paint;
232         paint.setAntiAlias(true);
233         paint.setStyle(SkPaint::kStroke_Style);
234         // we outset the stroke a little to cover up AA on the interior edge
235         SkScalar pad = 0.5f;
236         paint.setStrokeWidth(radius + 2*pad);
237         // handle scale of radius and pad due to CTM
238         radius *= scaleFactors[0];
239         pad *= scaleFactors[0];
240         SkASSERT(radius < 16384);
241         SkASSERT(pad < 64);
242         // Convert radius to 14.2 fixed point and place in the R & G components.
243         // Convert pad to 6.2 fixed point and place in the B component.
244         uint16_t iRadius = (uint16_t)(radius*4.0f);
245         unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
246         paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
247                                       iRadius >> 8, iRadius & 0xff,
248                                       (unsigned char)(4.0f*pad)));
249 
250         paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
251         paint.setShader(SkGaussianEdgeShader::Make());
252         canvas->drawRRect(pathRRect, paint);
253     }
254 
drawSpotShadow(SkCanvas * canvas,const SkPath & path,SkScalar zValue,SkPoint3 lightPos,SkScalar lightWidth,SkScalar spotAlpha)255     void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
256                         SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
257         if (spotAlpha <= 0) {
258             return;
259         }
260 
261         SkScalar zRatio = zValue / (lightPos.fZ - zValue);
262         if (zRatio < 0.0f) {
263             zRatio = 0.0f;
264         } else if (zRatio > 0.95f) {
265             zRatio = 0.95f;
266         }
267         SkScalar blurRadius = lightWidth*zRatio;
268 
269         // compute the transformation params
270         SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
271         SkMatrix ctmInverse;
272         if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
273             return;
274         }
275         SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
276         ctmInverse.mapPoints(&lightPos2D, 1);
277         SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
278                                        zRatio*(center.fY - lightPos2D.fY));
279         SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
280 
281         SkAutoCanvasRestore acr(canvas, true);
282 
283         sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
284                                                         SkBlurMask::ConvertRadiusToSigma(blurRadius),
285                                                         SkBlurMaskFilter::kNone_BlurFlag);
286 
287         SkPaint paint;
288         paint.setAntiAlias(true);
289         paint.setMaskFilter(std::move(mf));
290         paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
291                                                 ? 255
292                                                 : (unsigned char)(spotAlpha*255.999f), 0, 0, 0));
293 
294         // apply transformation to shadow
295         canvas->scale(scale, scale);
296         canvas->translate(offset.fX, offset.fY);
297         canvas->drawPath(path, paint);
298     }
299 
drawSpotShadowAlt(SkCanvas * canvas,const SkPath & path,SkScalar zValue,SkPoint3 lightPos,SkScalar lightWidth,SkScalar spotAlpha)300     void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
301                         SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
302         if (spotAlpha <= 0) {
303             return;
304         }
305 
306         SkScalar zRatio = zValue / (lightPos.fZ - zValue);
307         if (zRatio < 0.0f) {
308             zRatio = 0.0f;
309         } else if (zRatio > 0.95f) {
310             zRatio = 0.95f;
311         }
312         SkScalar radius = 2.0f*lightWidth*zRatio;
313 
314         SkRect pathRect;
315         SkRRect pathRRect;
316         SkScalar scaleFactors[2];
317         if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
318             return;
319         }
320         if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
321             !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
322               (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
323               path.isRect(&pathRect))) {
324             this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
325             return;
326         }
327 
328         // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
329         const SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
330         if (path.isOval(nullptr)) {
331             pathRRect = SkRRect::MakeOval(pathRect);
332         } else if (path.isRect(nullptr)) {
333             pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
334         } else {
335             if (pathRRect.getSimpleRadii().fX < minRadius) {
336                 pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
337             }
338         }
339 
340         // compute the scale and translation for the shadow
341         SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
342         SkRRect shadowRRect;
343         pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
344         SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
345         SkMatrix ctmInverse;
346         if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
347             return;
348         }
349         SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
350         ctmInverse.mapPoints(&lightPos2D, 1);
351         SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
352                                        zRatio*(center.fY - lightPos2D.fY));
353         SkAutoCanvasRestore acr(canvas, true);
354 
355         SkPaint paint;
356         paint.setAntiAlias(true);
357         // We want to extend the stroked area in so that it meets up with the caster
358         // geometry. The stroked geometry will, by definition already be inset half the
359         // stroke width but we also have to account for the scaling.
360         // We also add 1/2 to cover up AA on the interior edge.
361         SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(pathRect.fLeft),
362                                                               SkTAbs(pathRect.fRight)),
363                                                        SkTMax(SkTAbs(pathRect.fTop),
364                                                               SkTAbs(pathRect.fBottom)));
365         SkScalar insetAmount = offset.length() - (0.5f * radius) + scaleOffset + 0.5f;
366 
367         // compute area
368         SkScalar strokeWidth = radius + insetAmount;
369         SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
370         SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
371         // If the area of the stroked geometry is larger than the fill geometry, or
372         // if our pad is too big to convert to 6.2 fixed point, just fill it.
373         if (strokedArea > filledArea) {
374             paint.setStyle(SkPaint::kStrokeAndFill_Style);
375             paint.setStrokeWidth(radius);
376         } else {
377             // Since we can't have unequal strokes, inset the shadow rect so the inner
378             // and outer edges of the stroke will land where we want.
379             SkRect insetRect = shadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
380             SkScalar insetRad = SkTMax(shadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
381                                        minRadius);
382 
383             shadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
384             paint.setStyle(SkPaint::kStroke_Style);
385             paint.setStrokeWidth(strokeWidth);
386         }
387         paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
388         paint.setShader(SkGaussianEdgeShader::Make());
389         // handle scale of radius due to CTM
390         radius *= scaleFactors[0];
391         // don't need to scale pad as it was computed from the transformed offset
392         SkASSERT(radius < 16384);
393         SkScalar pad = 0;
394         SkASSERT(pad < 64);
395         // Convert radius to 14.2 fixed point and place in the R & G components.
396         // Convert pad to 6.2 fixed point and place in the B component.
397         uint16_t iRadius = (uint16_t)(radius*4.0f);
398         unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
399         paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
400                                       iRadius >> 8, iRadius & 0xff,
401                                       (unsigned char)(4.0f*pad)));
402 
403         // apply transformation to shadow
404         canvas->translate(offset.fX, offset.fY);
405         canvas->drawRRect(shadowRRect, paint);
406     }
407 
drawShadowedPath(SkCanvas * canvas,const SkPath & path,std::function<SkScalar (SkScalar,SkScalar)> zFunc,const SkPaint & paint,SkScalar ambientAlpha,const SkPoint3 & lightPos,SkScalar lightWidth,SkScalar spotAlpha)408     void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
409                           std::function<SkScalar(SkScalar, SkScalar)> zFunc,
410                           const SkPaint& paint, SkScalar ambientAlpha,
411                           const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
412 #ifdef USE_SHADOW_UTILS
413         SkScalar zValue = zFunc(0, 0);
414         if (fUseAlt) {
415             if (fShowAmbient) {
416                 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
417             }
418             if (fShowSpot) {
419                 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
420             }
421         } else {
422             if (!fShowAmbient) {
423                 ambientAlpha = 0;
424             }
425             if (!fShowSpot) {
426                 spotAlpha = 0;
427             }
428 
429             //SkShadowUtils::DrawShadow(canvas, path,
430             //                          zValue,
431             //                          lightPos, lightWidth,
432             //                          ambientAlpha, spotAlpha, SK_ColorBLACK);
433             SkShadowUtils::DrawUncachedShadow(canvas, path, zFunc,
434                                               lightPos, lightWidth,
435                                               ambientAlpha, spotAlpha, SK_ColorBLACK);
436         }
437 #else
438         if (fShowAmbient) {
439             if (fUseAlt) {
440                 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
441             } else {
442                 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
443             }
444         }
445         if (fShowSpot) {
446             if (fUseAlt) {
447                 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
448             } else {
449                 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
450             }
451         }
452 #endif
453 
454         if (fShowObject) {
455             canvas->drawPath(path, paint);
456         } else {
457             SkPaint strokePaint;
458 
459             strokePaint.setColor(paint.getColor());
460             strokePaint.setStyle(SkPaint::kStroke_Style);
461 
462             canvas->drawPath(path, strokePaint);
463         }
464     }
465 
onDrawContent(SkCanvas * canvas)466     void onDrawContent(SkCanvas* canvas) override {
467         this->drawBG(canvas);
468         const SkScalar kLightWidth = 2800;
469         const SkScalar kAmbientAlpha = 0.25f;
470         const SkScalar kSpotAlpha = 0.25f;
471 
472         SkPaint paint;
473         paint.setAntiAlias(true);
474 
475         SkPoint3 lightPos = fLightPos;
476 
477         paint.setColor(SK_ColorWHITE);
478         canvas->translate(200, 90);
479         lightPos.fX += 200;
480         lightPos.fY += 90;
481         SkScalar zValue = SkTMax(1.0f, 2 + fZDelta);
482         std::function<SkScalar(SkScalar, SkScalar)> zFunc =
483             [zValue](SkScalar, SkScalar) { return zValue; };
484         this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
485                                lightPos, kLightWidth, kSpotAlpha);
486 
487         paint.setColor(SK_ColorRED);
488         canvas->translate(250, 0);
489         lightPos.fX += 250;
490         zValue = SkTMax(1.0f, 4 + fZDelta);
491         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
492         this->drawShadowedPath(canvas, fRectPath, zFunc, paint, kAmbientAlpha,
493                                lightPos, kLightWidth, kSpotAlpha);
494 
495         paint.setColor(SK_ColorBLUE);
496         canvas->translate(-250, 110);
497         lightPos.fX -= 250;
498         lightPos.fY += 110;
499         zValue = SkTMax(1.0f, 8 + fZDelta);
500         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
501         this->drawShadowedPath(canvas, fCirclePath, zFunc, paint, kAmbientAlpha,
502                                lightPos, kLightWidth, 0.5f);
503 
504         paint.setColor(SK_ColorGREEN);
505         canvas->translate(250, 0);
506         lightPos.fX += 250;
507         zValue = SkTMax(1.0f, 64 + fZDelta);
508         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
509         this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
510                                lightPos, kLightWidth, kSpotAlpha);
511 
512         paint.setColor(SK_ColorYELLOW);
513         canvas->translate(-250, 110);
514         lightPos.fX -= 250;
515         lightPos.fY += 110;
516         zValue = SkTMax(1.0f, 8 + fZDelta);
517         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
518         this->drawShadowedPath(canvas, fFunkyRRPath, zFunc, paint, kAmbientAlpha,
519                                lightPos, kLightWidth, kSpotAlpha);
520 
521         paint.setColor(SK_ColorCYAN);
522         canvas->translate(250, 0);
523         lightPos.fX += 250;
524         zValue = SkTMax(1.0f, 16 + fZDelta);
525         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
526         this->drawShadowedPath(canvas, fCubicPath, zFunc, paint,
527                                kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
528 
529         // circular reveal
530         SkPath tmpPath;
531         SkPath tmpClipPath;
532         tmpClipPath.addCircle(fAnimTranslate, 0, 60);
533         Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
534 
535         paint.setColor(SK_ColorMAGENTA);
536         canvas->translate(-125, 60);
537         lightPos.fX -= 125;
538         lightPos.fY += 60;
539         zValue = SkTMax(1.0f, 32 + fZDelta);
540         zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
541         this->drawShadowedPath(canvas, tmpPath, zFunc, paint, .1f,
542                                lightPos, kLightWidth, .5f);
543 
544         // perspective paths
545         SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
546                                       fWideRectPath.getBounds().height()/2);
547         SkPoint translate = SkPoint::Make(100, 450);
548         paint.setColor(SK_ColorWHITE);
549         Sk3DView view;
550         view.save();
551         view.rotateX(fAnimAngle);
552         SkMatrix persp;
553         view.getMatrix(&persp);
554         persp.preTranslate(-pivot.fX, -pivot.fY);
555         persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
556         canvas->setMatrix(persp);
557         lightPos = fLightPos;
558         lightPos.fX += pivot.fX + translate.fX;
559         lightPos.fY += pivot.fY + translate.fY;
560         zValue = SkTMax(1.0f, 16 + fZDelta);
561         SkScalar radians = SkDegreesToRadians(fAnimAngle);
562         zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
563             return SkScalarSin(-radians)*y +
564                    zValue - SkScalarSin(-radians)*pivot.fY;
565         };
566         this->drawShadowedPath(canvas, fWideRectPath, zFunc, paint, .1f,
567                                lightPos, kLightWidth, .5f);
568 
569         pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
570                               fWideOvalPath.getBounds().height() / 2);
571         translate = SkPoint::Make(100, 600);
572         view.restore();
573         view.rotateY(fAnimAngle);
574         view.getMatrix(&persp);
575         persp.preTranslate(-pivot.fX, -pivot.fY);
576         persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
577         canvas->setMatrix(persp);
578         lightPos = fLightPos;
579         lightPos.fX += pivot.fX + translate.fX;
580         lightPos.fY += pivot.fY + translate.fY;
581         zValue = SkTMax(1.0f, 32 + fZDelta);
582         zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
583             return -SkScalarSin(radians)*x +
584                 zValue + SkScalarSin(radians)*pivot.fX;
585         };
586         this->drawShadowedPath(canvas, fWideOvalPath, zFunc, paint, .1f,
587                                lightPos, kLightWidth, .5f);
588     }
589 
onAnimate(const SkAnimTimer & timer)590     bool onAnimate(const SkAnimTimer& timer) override {
591         fAnimTranslate = timer.pingPong(30, 0, 200, -200);
592         fAnimAngle = timer.pingPong(15, 0, 0, 20);
593 
594         return true;
595     }
596 
597 protected:
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)598     SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
599         return new SkView::Click(this);
600     }
601 
onClick(Click * click)602     bool onClick(Click *click) override {
603         SkScalar x = click->fCurr.fX;
604         SkScalar y = click->fCurr.fY;
605 
606         SkScalar dx = x - click->fPrev.fX;
607         SkScalar dy = y - click->fPrev.fY;
608 
609         if (dx != 0 || dy != 0) {
610             fLightPos.fX += dx;
611             fLightPos.fY += dy;
612             this->inval(nullptr);
613         }
614 
615         return true;
616     }
617 
618 private:
619     typedef SampleView INHERITED;
620 };
621 
622 //////////////////////////////////////////////////////////////////////////////
623 
MyFactory()624 static SkView* MyFactory() { return new ShadowsView; }
625 static SkViewRegister reg(MyFactory);
626