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