--- /dev/null
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SampleCode.h"
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPoint3.h"
+#include "SkUtils.h"
+#include "SkView.h"
+#include "sk_tool_utils.h"
+
+class ShadowsView : public SampleView {
+ SkPath fRectPath;
+ SkPath fRRPath;
+ SkPath fCirclePath;
+ SkPoint3 fLightPos;
+
+ bool fShowAmbient;
+ bool fShowSpot;
+ bool fShowObject;
+
+public:
+ ShadowsView()
+ : fShowAmbient(true)
+ , fShowSpot(true)
+ , fShowObject(true) {}
+
+protected:
+ void onOnceBeforeDraw() override {
+ fCirclePath.addCircle(0, 0, 50);
+ fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
+ fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
+ fLightPos = SkPoint3::Make(-220, -330, 150);
+ }
+
+ // overrides from SkEventSink
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "AndroidShadows");
+ return true;
+ }
+
+ SkUnichar uni;
+ if (SampleCode::CharQ(*evt, &uni)) {
+ switch (uni) {
+ case 'B':
+ fShowAmbient = !fShowAmbient;
+ break;
+ case 'S':
+ fShowSpot = !fShowSpot;
+ break;
+ case 'O':
+ fShowObject = !fShowObject;
+ break;
+ case '>':
+ fLightPos.fZ += 10;
+ break;
+ case '<':
+ fLightPos.fZ -= 10;
+ break;
+ default:
+ break;
+ }
+ this->inval(nullptr);
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+ }
+
+ static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
+ SkRect pathRect;
+ SkRRect pathRRect;
+ if (path.isOval(&pathRect)) {
+ *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
+ } else if (path.isRRect(&pathRRect)) {
+ *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
+ } else if (path.isRect(occlRect)) {
+ // the inverse transform for the spot shadow occluder doesn't always get us
+ // back to exactly the same position, so deducting a little slop
+ occlRect->inset(1, 1);
+ } else {
+ *occlRect = SkRect::MakeEmpty();
+ }
+ }
+
+ void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
+ SkScalar ambientAlpha) {
+
+ if (ambientAlpha <= 0) {
+ return;
+ }
+
+ const SkScalar kHeightFactor = 1.f / 128.f;
+ const SkScalar kGeomFactor = 64;
+
+ SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
+ SkScalar radius = zValue*kHeightFactor*kGeomFactor;
+
+ // occlude blur
+ SkRect occlRect;
+ GetOcclRect(path, &occlRect);
+ sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+ SkBlurMask::ConvertRadiusToSigma(radius),
+ occlRect,
+ SkBlurMaskFilter::kNone_BlurFlag);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setMaskFilter(std::move(mf));
+ paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
+ canvas->drawPath(path, paint);
+
+ // draw occlusion rect
+ SkPaint stroke;
+ stroke.setStyle(SkPaint::kStroke_Style);
+ stroke.setColor(SK_ColorBLUE);
+ canvas->drawRect(occlRect, stroke);
+ }
+
+ void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
+ SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
+ if (spotAlpha <= 0) {
+ return;
+ }
+
+ SkScalar zRatio = zValue / (lightPos.fZ - zValue);
+ if (zRatio < 0.0f) {
+ zRatio = 0.0f;
+ } else if (zRatio > 0.95f) {
+ zRatio = 0.95f;
+ }
+ SkScalar radius = lightWidth*zRatio;
+
+ // compute the transformation params
+ SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
+ canvas->getTotalMatrix().mapPoints(¢er, 1);
+ SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
+ -zRatio*(lightPos.fY - center.fY));
+ SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+ if (scale < 1.0f) {
+ scale = 1.0f;
+ } else if (scale > 1024.f) {
+ scale = 1024.f;
+ }
+
+ SkAutoCanvasRestore acr(canvas, true);
+
+ SkRect occlRect;
+ GetOcclRect(path, &occlRect);
+ // apply inverse transform
+ occlRect.offset(-offset);
+ occlRect.fLeft /= scale;
+ occlRect.fRight /= scale;
+ occlRect.fTop /= scale;
+ occlRect.fBottom /= scale;
+ sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+ SkBlurMask::ConvertRadiusToSigma(radius),
+ occlRect,
+ SkBlurMaskFilter::kNone_BlurFlag);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setMaskFilter(std::move(mf));
+ paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
+
+ // apply transformation to shadow
+ canvas->translate(offset.fX, offset.fY);
+ canvas->scale(scale, scale);
+ canvas->drawPath(path, paint);
+
+ // draw occlusion rect
+ SkPaint stroke;
+ stroke.setStyle(SkPaint::kStroke_Style);
+ stroke.setColor(SK_ColorRED);
+ canvas->drawRect(occlRect, stroke);
+ }
+
+ void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
+ const SkPaint& paint) {
+ const SkScalar kLightWidth = 3;
+ const SkScalar kAmbientAlpha = 0.25f;
+ const SkScalar kSpotAlpha = 0.25f;
+
+ if (fShowAmbient) {
+ this->drawAmbientShadow(canvas, path, zValue, kAmbientAlpha);
+ }
+ if (fShowSpot) {
+ this->drawSpotShadow(canvas, path, zValue, fLightPos, kLightWidth, kSpotAlpha);
+ }
+ if (fShowObject) {
+ canvas->drawPath(path, paint);
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ this->drawBG(canvas);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorWHITE);
+ canvas->translate(200, 90);
+ this->drawShadowedPath(canvas, fRectPath, 5, paint);
+
+ paint.setColor(SK_ColorRED);
+ canvas->translate(250, 0);
+ this->drawShadowedPath(canvas, fRRPath, 5, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ canvas->translate(-250, 110);
+ this->drawShadowedPath(canvas, fCirclePath, 5, paint);
+ }
+
+protected:
+ SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
+ return new SkView::Click(this);
+ }
+
+ bool onClick(Click *click) override {
+ SkScalar x = click->fCurr.fX;
+ SkScalar y = click->fCurr.fY;
+
+ SkScalar dx = x - click->fPrev.fX;
+ SkScalar dy = y - click->fPrev.fY;
+
+ if (dx != 0 || dy != 0) {
+ fLightPos.fX += dx;
+ fLightPos.fY += dy;
+ this->inval(nullptr);
+ }
+
+ return true;
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShadowsView; }
+static SkViewRegister reg(MyFactory);