blob: bb56b7ed17197e63cfa513cf399c8e74856b2e1b [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"
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -05009#include "SkAnimTimer.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070010#include "SkBlurMask.h"
11#include "SkBlurMaskFilter.h"
Jim Van Verth2aa535e2017-02-06 14:36:01 -050012#include "SkColorFilter.h"
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050013#include "SkCamera.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070014#include "SkCanvas.h"
jvanverth6c177a12016-08-17 07:59:41 -070015#include "SkGaussianEdgeShader.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070016#include "SkPath.h"
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050017#include "SkPathOps.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070018#include "SkPoint3.h"
Jim Van Verth43475ad2017-01-13 14:37:37 -050019#include "SkShadowUtils.h"
jvanverthe1a3bc62016-08-12 10:40:38 -070020#include "SkUtils.h"
21#include "SkView.h"
22#include "sk_tool_utils.h"
23
Jim Van Verth43475ad2017-01-13 14:37:37 -050024#define USE_SHADOW_UTILS
25
jvanverth6c177a12016-08-17 07:59:41 -070026////////////////////////////////////////////////////////////////////////////
27
jvanverthe1a3bc62016-08-12 10:40:38 -070028class ShadowsView : public SampleView {
29 SkPath fRectPath;
30 SkPath fRRPath;
31 SkPath fCirclePath;
Jim Van Verthbce74962017-01-25 09:39:46 -050032 SkPath fFunkyRRPath;
33 SkPath fCubicPath;
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050034 SkPath fSquareRRectPath;
35 SkPath fWideRectPath;
36 SkPath fWideOvalPath;
jvanverthe1a3bc62016-08-12 10:40:38 -070037 SkPoint3 fLightPos;
Jim Van Verth2aa535e2017-02-06 14:36:01 -050038 SkScalar fZDelta;
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050039 SkScalar fAnimTranslate;
jvanverthe1a3bc62016-08-12 10:40:38 -070040
41 bool fShowAmbient;
42 bool fShowSpot;
jvanverthd7315f912016-08-17 10:06:18 -070043 bool fUseAlt;
jvanverthe1a3bc62016-08-12 10:40:38 -070044 bool fShowObject;
Robert Phillips95304e32016-10-07 14:44:07 -040045 bool fIgnoreShadowAlpha;
jvanverthe1a3bc62016-08-12 10:40:38 -070046
47public:
Jim Van Verth2aa535e2017-02-06 14:36:01 -050048 ShadowsView()
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050049 : fZDelta(0)
50 , fAnimTranslate(0)
Jim Van Verth2aa535e2017-02-06 14:36:01 -050051 , fShowAmbient(true)
jvanverthe1a3bc62016-08-12 10:40:38 -070052 , fShowSpot(true)
jvanverthd7315f912016-08-17 10:06:18 -070053 , fUseAlt(true)
Robert Phillips95304e32016-10-07 14:44:07 -040054 , fShowObject(true)
55 , fIgnoreShadowAlpha(false) {}
jvanverthe1a3bc62016-08-12 10:40:38 -070056
57protected:
58 void onOnceBeforeDraw() override {
59 fCirclePath.addCircle(0, 0, 50);
60 fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
61 fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
Jim Van Verthbce74962017-01-25 09:39:46 -050062 fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
63 40 * SK_Scalar1, 20 * SK_Scalar1,
64 SkPath::kCW_Direction);
65 fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
66 20 * SK_Scalar1, 100 * SK_Scalar1,
67 0 * SK_Scalar1, 0 * SK_Scalar1);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050068 fSquareRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-50, -50, 100, 100),
69 10, 10));
70 fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
71 fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
Jim Van Verthbce74962017-01-25 09:39:46 -050072
jvanverthd99858a2016-09-12 07:51:04 -070073 fLightPos = SkPoint3::Make(-700, -700, 2800);
jvanverthe1a3bc62016-08-12 10:40:38 -070074 }
75
76 // overrides from SkEventSink
77 bool onQuery(SkEvent* evt) override {
78 if (SampleCode::TitleQ(*evt)) {
79 SampleCode::TitleR(evt, "AndroidShadows");
80 return true;
81 }
82
83 SkUnichar uni;
84 if (SampleCode::CharQ(*evt, &uni)) {
Jim Van Verth6f449692017-02-14 15:16:46 -050085 bool handled = false;
jvanverthe1a3bc62016-08-12 10:40:38 -070086 switch (uni) {
Jim Van Verth2aa535e2017-02-06 14:36:01 -050087 case 'W':
jvanverthe1a3bc62016-08-12 10:40:38 -070088 fShowAmbient = !fShowAmbient;
Jim Van Verth6f449692017-02-14 15:16:46 -050089 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -070090 break;
91 case 'S':
92 fShowSpot = !fShowSpot;
Jim Van Verth6f449692017-02-14 15:16:46 -050093 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -070094 break;
jvanverthd7315f912016-08-17 10:06:18 -070095 case 'T':
96 fUseAlt = !fUseAlt;
Jim Van Verth6f449692017-02-14 15:16:46 -050097 handled = true;
jvanverthd7315f912016-08-17 10:06:18 -070098 break;
jvanverthe1a3bc62016-08-12 10:40:38 -070099 case 'O':
100 fShowObject = !fShowObject;
Jim Van Verth6f449692017-02-14 15:16:46 -0500101 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700102 break;
103 case '>':
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500104 fZDelta += 0.5f;
Jim Van Verth6f449692017-02-14 15:16:46 -0500105 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700106 break;
107 case '<':
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500108 fZDelta -= 0.5f;
Jim Van Verth6f449692017-02-14 15:16:46 -0500109 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700110 break;
Robert Phillips95304e32016-10-07 14:44:07 -0400111 case '?':
112 fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
Jim Van Verth6f449692017-02-14 15:16:46 -0500113 handled = true;
Robert Phillips95304e32016-10-07 14:44:07 -0400114 break;
jvanverthe1a3bc62016-08-12 10:40:38 -0700115 default:
116 break;
117 }
Jim Van Verth6f449692017-02-14 15:16:46 -0500118 if (handled) {
119 this->inval(nullptr);
120 return true;
121 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700122 }
123 return this->INHERITED::onQuery(evt);
124 }
125
126 void drawBG(SkCanvas* canvas) {
127 canvas->drawColor(0xFFDDDDDD);
128 }
129
130 static void GetOcclRect(const SkPath& path, SkRect* occlRect) {
131 SkRect pathRect;
132 SkRRect pathRRect;
133 if (path.isOval(&pathRect)) {
134 *occlRect = sk_tool_utils::compute_central_occluder(SkRRect::MakeOval(pathRect));
135 } else if (path.isRRect(&pathRRect)) {
136 *occlRect = sk_tool_utils::compute_central_occluder(pathRRect);
137 } else if (path.isRect(occlRect)) {
138 // the inverse transform for the spot shadow occluder doesn't always get us
139 // back to exactly the same position, so deducting a little slop
140 occlRect->inset(1, 1);
141 } else {
142 *occlRect = SkRect::MakeEmpty();
143 }
144 }
145
146 void drawAmbientShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
147 SkScalar ambientAlpha) {
148
149 if (ambientAlpha <= 0) {
150 return;
151 }
152
153 const SkScalar kHeightFactor = 1.f / 128.f;
154 const SkScalar kGeomFactor = 64;
155
156 SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
157 SkScalar radius = zValue*kHeightFactor*kGeomFactor;
158
159 // occlude blur
160 SkRect occlRect;
161 GetOcclRect(path, &occlRect);
162 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
163 SkBlurMask::ConvertRadiusToSigma(radius),
164 occlRect,
165 SkBlurMaskFilter::kNone_BlurFlag);
166
167 SkPaint paint;
168 paint.setAntiAlias(true);
169 paint.setMaskFilter(std::move(mf));
Robert Phillips95304e32016-10-07 14:44:07 -0400170 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
171 ? 255
172 : (unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
jvanverthe1a3bc62016-08-12 10:40:38 -0700173 canvas->drawPath(path, paint);
174
175 // draw occlusion rect
jvanverth6c177a12016-08-17 07:59:41 -0700176#if DRAW_OCCL_RECT
jvanverthe1a3bc62016-08-12 10:40:38 -0700177 SkPaint stroke;
178 stroke.setStyle(SkPaint::kStroke_Style);
179 stroke.setColor(SK_ColorBLUE);
180 canvas->drawRect(occlRect, stroke);
jvanverth6c177a12016-08-17 07:59:41 -0700181#endif
182 }
183
184 void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
185 SkScalar ambientAlpha) {
186
187 if (ambientAlpha <= 0) {
188 return;
189 }
190
191 const SkScalar kHeightFactor = 1.f / 128.f;
192 const SkScalar kGeomFactor = 64;
193
194 SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
195 SkScalar radius = zValue*kHeightFactor*kGeomFactor;
jvanverthd99858a2016-09-12 07:51:04 -0700196 // distance to outer of edge of geometry from original shape edge
197 SkScalar offset = radius*umbraAlpha;
jvanverth6c177a12016-08-17 07:59:41 -0700198
jvanvertha4f1af82016-08-29 07:17:47 -0700199 SkRect pathRect;
200 SkRRect pathRRect;
jvanverthd99858a2016-09-12 07:51:04 -0700201 SkScalar scaleFactors[2];
202 if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
203 return;
204 }
205 if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
jvanvertha4f1af82016-08-29 07:17:47 -0700206 !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
207 (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
208 path.isRect(&pathRect))) {
209 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
210 return;
211 }
212
jvanverthd99858a2016-09-12 07:51:04 -0700213 // For all of these, we inset the offset rect by half the radius to get our stroke shape.
214 SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
215 // Make sure we'll have a radius of at least 0.5 after xform
216 if (strokeOutset*scaleFactors[0] < 0.5f) {
217 strokeOutset = 0.5f / scaleFactors[0];
218 }
jvanverthd7315f912016-08-17 10:06:18 -0700219 if (path.isOval(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700220 pathRect.outset(strokeOutset, strokeOutset);
jvanverthd7315f912016-08-17 10:06:18 -0700221 pathRRect = SkRRect::MakeOval(pathRect);
222 } else if (path.isRect(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700223 pathRect.outset(strokeOutset, strokeOutset);
224 pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
jvanverth6c177a12016-08-17 07:59:41 -0700225 } else {
jvanverthd99858a2016-09-12 07:51:04 -0700226 pathRRect.outset(strokeOutset, strokeOutset);
jvanverth6c177a12016-08-17 07:59:41 -0700227 }
228
jvanverthd7315f912016-08-17 10:06:18 -0700229 SkPaint paint;
230 paint.setAntiAlias(true);
jvanvertha4f1af82016-08-29 07:17:47 -0700231 paint.setStyle(SkPaint::kStroke_Style);
232 // we outset the stroke a little to cover up AA on the interior edge
jvanverthd99858a2016-09-12 07:51:04 -0700233 SkScalar pad = 0.5f;
234 paint.setStrokeWidth(radius + 2*pad);
235 // handle scale of radius and pad due to CTM
236 radius *= scaleFactors[0];
237 pad *= scaleFactors[0];
238 SkASSERT(radius < 16384);
239 SkASSERT(pad < 64);
240 // Convert radius to 14.2 fixed point and place in the R & G components.
241 // Convert pad to 6.2 fixed point and place in the B component.
242 uint16_t iRadius = (uint16_t)(radius*4.0f);
243 unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
Robert Phillips95304e32016-10-07 14:44:07 -0400244 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
245 iRadius >> 8, iRadius & 0xff,
jvanverthd99858a2016-09-12 07:51:04 -0700246 (unsigned char)(4.0f*pad)));
jvanverthd7315f912016-08-17 10:06:18 -0700247
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500248 paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
jvanverthc20c0c02016-09-14 07:04:49 -0700249 paint.setShader(SkGaussianEdgeShader::Make());
jvanverthd7315f912016-08-17 10:06:18 -0700250 canvas->drawRRect(pathRRect, paint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700251 }
252
253 void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
254 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
255 if (spotAlpha <= 0) {
256 return;
257 }
258
259 SkScalar zRatio = zValue / (lightPos.fZ - zValue);
260 if (zRatio < 0.0f) {
261 zRatio = 0.0f;
262 } else if (zRatio > 0.95f) {
263 zRatio = 0.95f;
264 }
jvanverthd99858a2016-09-12 07:51:04 -0700265 SkScalar blurRadius = lightWidth*zRatio;
jvanverthe1a3bc62016-08-12 10:40:38 -0700266
267 // compute the transformation params
268 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
jvanverthd99858a2016-09-12 07:51:04 -0700269 SkMatrix ctmInverse;
270 if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
271 return;
jvanverthe1a3bc62016-08-12 10:40:38 -0700272 }
jvanverthd99858a2016-09-12 07:51:04 -0700273 SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
274 ctmInverse.mapPoints(&lightPos2D, 1);
275 SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
276 zRatio*(center.fY - lightPos2D.fY));
277 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
jvanverthe1a3bc62016-08-12 10:40:38 -0700278
279 SkAutoCanvasRestore acr(canvas, true);
280
jvanverthe1a3bc62016-08-12 10:40:38 -0700281 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
jvanverthd99858a2016-09-12 07:51:04 -0700282 SkBlurMask::ConvertRadiusToSigma(blurRadius),
jvanverthe1a3bc62016-08-12 10:40:38 -0700283 SkBlurMaskFilter::kNone_BlurFlag);
284
285 SkPaint paint;
286 paint.setAntiAlias(true);
287 paint.setMaskFilter(std::move(mf));
Robert Phillips95304e32016-10-07 14:44:07 -0400288 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
289 ? 255
290 : (unsigned char)(spotAlpha*255.999f), 0, 0, 0));
jvanverthe1a3bc62016-08-12 10:40:38 -0700291
292 // apply transformation to shadow
jvanverthe1a3bc62016-08-12 10:40:38 -0700293 canvas->scale(scale, scale);
jvanverthd99858a2016-09-12 07:51:04 -0700294 canvas->translate(offset.fX, offset.fY);
jvanverthe1a3bc62016-08-12 10:40:38 -0700295 canvas->drawPath(path, paint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700296 }
297
jvanverthd7315f912016-08-17 10:06:18 -0700298 void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
299 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
300 if (spotAlpha <= 0) {
301 return;
302 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700303
jvanverthd7315f912016-08-17 10:06:18 -0700304 SkScalar zRatio = zValue / (lightPos.fZ - zValue);
305 if (zRatio < 0.0f) {
306 zRatio = 0.0f;
307 } else if (zRatio > 0.95f) {
308 zRatio = 0.95f;
309 }
jvanverthd99858a2016-09-12 07:51:04 -0700310 SkScalar radius = 2.0f*lightWidth*zRatio;
jvanverthd7315f912016-08-17 10:06:18 -0700311
jvanvertha4f1af82016-08-29 07:17:47 -0700312 SkRect pathRect;
313 SkRRect pathRRect;
jvanverthd99858a2016-09-12 07:51:04 -0700314 SkScalar scaleFactors[2];
315 if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
316 return;
317 }
318 if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
jvanvertha4f1af82016-08-29 07:17:47 -0700319 !((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
320 (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
321 path.isRect(&pathRect))) {
322 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
323 return;
324 }
325
jvanverthd99858a2016-09-12 07:51:04 -0700326 // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
Robert Phillips95304e32016-10-07 14:44:07 -0400327 const SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
jvanverthd7315f912016-08-17 10:06:18 -0700328 if (path.isOval(nullptr)) {
jvanverthd7315f912016-08-17 10:06:18 -0700329 pathRRect = SkRRect::MakeOval(pathRect);
330 } else if (path.isRect(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700331 pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
jvanverthd7315f912016-08-17 10:06:18 -0700332 } else {
jvanverthd99858a2016-09-12 07:51:04 -0700333 if (pathRRect.getSimpleRadii().fX < minRadius) {
334 pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
335 }
jvanverthd7315f912016-08-17 10:06:18 -0700336 }
337
jvanverthd99858a2016-09-12 07:51:04 -0700338 // compute the scale and translation for the shadow
339 SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
340 SkRRect shadowRRect;
341 pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
342 SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
343 SkMatrix ctmInverse;
344 if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
345 return;
346 }
347 SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
348 ctmInverse.mapPoints(&lightPos2D, 1);
349 SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
350 zRatio*(center.fY - lightPos2D.fY));
jvanverthd7315f912016-08-17 10:06:18 -0700351 SkAutoCanvasRestore acr(canvas, true);
352
353 SkPaint paint;
354 paint.setAntiAlias(true);
Robert Phillips95304e32016-10-07 14:44:07 -0400355 // We want to extend the stroked area in so that it meets up with the caster
356 // geometry. The stroked geometry will, by definition already be inset half the
357 // stroke width but we also have to account for the scaling.
358 // We also add 1/2 to cover up AA on the interior edge.
359 SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(pathRect.fLeft),
360 SkTAbs(pathRect.fRight)),
361 SkTMax(SkTAbs(pathRect.fTop),
362 SkTAbs(pathRect.fBottom)));
363 SkScalar insetAmount = offset.length() - (0.5f * radius) + scaleOffset + 0.5f;
364
jvanvertha4f1af82016-08-29 07:17:47 -0700365 // compute area
Robert Phillips95304e32016-10-07 14:44:07 -0400366 SkScalar strokeWidth = radius + insetAmount;
jvanverthd99858a2016-09-12 07:51:04 -0700367 SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
368 SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
jvanvertha4f1af82016-08-29 07:17:47 -0700369 // If the area of the stroked geometry is larger than the fill geometry, or
370 // if our pad is too big to convert to 6.2 fixed point, just fill it.
Robert Phillips95304e32016-10-07 14:44:07 -0400371 if (strokedArea > filledArea) {
jvanvertha4f1af82016-08-29 07:17:47 -0700372 paint.setStyle(SkPaint::kStrokeAndFill_Style);
373 paint.setStrokeWidth(radius);
374 } else {
Robert Phillips95304e32016-10-07 14:44:07 -0400375 // Since we can't have unequal strokes, inset the shadow rect so the inner
376 // and outer edges of the stroke will land where we want.
377 SkRect insetRect = shadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
378 SkScalar insetRad = SkTMax(shadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
379 minRadius);
380
381 shadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
jvanvertha4f1af82016-08-29 07:17:47 -0700382 paint.setStyle(SkPaint::kStroke_Style);
383 paint.setStrokeWidth(strokeWidth);
384 }
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500385 paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
jvanverthc20c0c02016-09-14 07:04:49 -0700386 paint.setShader(SkGaussianEdgeShader::Make());
jvanverthd7315f912016-08-17 10:06:18 -0700387 // handle scale of radius due to CTM
jvanverthd99858a2016-09-12 07:51:04 -0700388 radius *= scaleFactors[0];
389 // don't need to scale pad as it was computed from the transformed offset
390 SkASSERT(radius < 16384);
Robert Phillips95304e32016-10-07 14:44:07 -0400391 SkScalar pad = 0;
jvanvertha4f1af82016-08-29 07:17:47 -0700392 SkASSERT(pad < 64);
jvanverthd99858a2016-09-12 07:51:04 -0700393 // Convert radius to 14.2 fixed point and place in the R & G components.
394 // Convert pad to 6.2 fixed point and place in the B component.
395 uint16_t iRadius = (uint16_t)(radius*4.0f);
396 unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
Robert Phillips95304e32016-10-07 14:44:07 -0400397 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
398 iRadius >> 8, iRadius & 0xff,
jvanverthd99858a2016-09-12 07:51:04 -0700399 (unsigned char)(4.0f*pad)));
jvanverthd7315f912016-08-17 10:06:18 -0700400
401 // apply transformation to shadow
402 canvas->translate(offset.fX, offset.fY);
jvanverthd99858a2016-09-12 07:51:04 -0700403 canvas->drawRRect(shadowRRect, paint);
jvanverthd7315f912016-08-17 10:06:18 -0700404 }
405
406 void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
407 const SkPaint& paint, SkScalar ambientAlpha,
408 const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
Jim Van Verth43475ad2017-01-13 14:37:37 -0500409#ifdef USE_SHADOW_UTILS
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400410 if (fUseAlt) {
411 if (fShowAmbient) {
412 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
413 }
414 if (fShowSpot) {
415 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
416 }
417 } else {
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400418 if (!fShowAmbient) {
419 ambientAlpha = 0;
420 }
421 if (!fShowSpot) {
422 spotAlpha = 0;
423 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400424
425 SkShadowUtils::DrawShadow(canvas, path,
426 zValue,
427 lightPos, lightWidth,
Jim Van Verth43475ad2017-01-13 14:37:37 -0500428 ambientAlpha, spotAlpha, SK_ColorBLACK);
Jim Van Verthb4366552017-03-27 14:25:29 -0400429 //SkShadowUtils::DrawUncachedShadow(canvas, path,
430 // [zValue](SkScalar, SkScalar) { return zValue; },
431 // lightPos, lightWidth,
432 // ambientAlpha, spotAlpha, SK_ColorBLACK);
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400433 }
434#else
jvanverthe1a3bc62016-08-12 10:40:38 -0700435 if (fShowAmbient) {
jvanverthd7315f912016-08-17 10:06:18 -0700436 if (fUseAlt) {
437 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700438 } else {
jvanverthd7315f912016-08-17 10:06:18 -0700439 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700440 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700441 }
442 if (fShowSpot) {
jvanverthd7315f912016-08-17 10:06:18 -0700443 if (fUseAlt) {
444 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
445 } else {
446 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
447 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700448 }
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400449#endif
450
jvanverthe1a3bc62016-08-12 10:40:38 -0700451 if (fShowObject) {
452 canvas->drawPath(path, paint);
Robert Phillips95304e32016-10-07 14:44:07 -0400453 } else {
454 SkPaint strokePaint;
455
456 strokePaint.setColor(paint.getColor());
457 strokePaint.setStyle(SkPaint::kStroke_Style);
458
459 canvas->drawPath(path, strokePaint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700460 }
461 }
462
463 void onDrawContent(SkCanvas* canvas) override {
464 this->drawBG(canvas);
jvanverthd99858a2016-09-12 07:51:04 -0700465 const SkScalar kLightWidth = 2800;
jvanverthd7315f912016-08-17 10:06:18 -0700466 const SkScalar kAmbientAlpha = 0.25f;
467 const SkScalar kSpotAlpha = 0.25f;
jvanverthe1a3bc62016-08-12 10:40:38 -0700468
469 SkPaint paint;
470 paint.setAntiAlias(true);
471
jvanverthd7315f912016-08-17 10:06:18 -0700472 SkPoint3 lightPos = fLightPos;
473
jvanverthe1a3bc62016-08-12 10:40:38 -0700474 paint.setColor(SK_ColorWHITE);
475 canvas->translate(200, 90);
jvanverthd7315f912016-08-17 10:06:18 -0700476 lightPos.fX += 200;
477 lightPos.fY += 90;
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500478 this->drawShadowedPath(canvas, fRRPath, SkTMax(1.0f, 2+fZDelta), paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700479 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700480
481 paint.setColor(SK_ColorRED);
482 canvas->translate(250, 0);
jvanverthd7315f912016-08-17 10:06:18 -0700483 lightPos.fX += 250;
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500484 this->drawShadowedPath(canvas, fRectPath, SkTMax(1.0f, 4+fZDelta), paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700485 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700486
487 paint.setColor(SK_ColorBLUE);
488 canvas->translate(-250, 110);
jvanverthd7315f912016-08-17 10:06:18 -0700489 lightPos.fX -= 250;
490 lightPos.fY += 110;
Jim Van Verthcf40e302017-03-02 11:28:43 -0500491 this->drawShadowedPath(canvas, fCirclePath, SkTMax(1.0f, 8+fZDelta), paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700492 lightPos, kLightWidth, 0.5f);
jvanverth6c177a12016-08-17 07:59:41 -0700493
494 paint.setColor(SK_ColorGREEN);
495 canvas->translate(250, 0);
jvanverthd7315f912016-08-17 10:06:18 -0700496 lightPos.fX += 250;
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500497 this->drawShadowedPath(canvas, fRRPath, SkTMax(1.0f, 64+fZDelta), paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700498 lightPos, kLightWidth, kSpotAlpha);
Jim Van Verthbce74962017-01-25 09:39:46 -0500499
500 paint.setColor(SK_ColorYELLOW);
501 canvas->translate(-250, 110);
502 lightPos.fX -= 250;
503 lightPos.fY += 110;
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500504 this->drawShadowedPath(canvas, fFunkyRRPath, SkTMax(1.0f, 8+fZDelta), paint, kAmbientAlpha,
Jim Van Verthbce74962017-01-25 09:39:46 -0500505 lightPos, kLightWidth, kSpotAlpha);
506
507 paint.setColor(SK_ColorCYAN);
508 canvas->translate(250, 0);
509 lightPos.fX += 250;
Brian Salomonab664fa2017-03-24 16:07:20 +0000510 this->drawShadowedPath(canvas, fCubicPath, SkTMax(1.0f, 16 + fZDelta), paint,
511 kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500512
513 // circular reveal
514 SkPath tmpPath;
515 SkPath tmpClipPath;
516 tmpClipPath.addCircle(fAnimTranslate, 0, 60);
517 Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
518
519 paint.setColor(SK_ColorMAGENTA);
520 canvas->translate(-125, 60);
521 lightPos.fX -= 125;
522 lightPos.fY += 60;
Brian Salomonab664fa2017-03-24 16:07:20 +0000523 this->drawShadowedPath(canvas, tmpPath, SkTMax(1.0f, 32 + fZDelta), paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500524 lightPos, kLightWidth, .5f);
525
526 // perspective paths
527 SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
528 fWideRectPath.getBounds().height()/2);
529 SkPoint translate = SkPoint::Make(50, 450);
530 paint.setColor(SK_ColorWHITE);
531 Sk3DView view;
532 view.save();
533 view.rotateX(10);
534 SkMatrix persp;
535 view.getMatrix(&persp);
536 persp.preTranslate(-pivot.fX, -pivot.fY);
537 persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
538 canvas->setMatrix(persp);
539 lightPos = fLightPos;
540 lightPos.fX += pivot.fX + translate.fX;
541 lightPos.fY += pivot.fY + translate.fY;
Brian Salomonab664fa2017-03-24 16:07:20 +0000542 this->drawShadowedPath(canvas, fWideRectPath, SkTMax(1.0f, 16 + fZDelta), paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500543 lightPos, kLightWidth, .5f);
544
545 pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
546 fWideOvalPath.getBounds().height() / 2);
547 translate = SkPoint::Make(50, 600);
548 view.restore();
549 view.rotateY(10);
550 view.getMatrix(&persp);
551 persp.preTranslate(-pivot.fX, -pivot.fY);
552 persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
553 canvas->setMatrix(persp);
554 lightPos = fLightPos;
555 lightPos.fX += pivot.fX + translate.fX;
556 lightPos.fY += pivot.fY + translate.fY;
Brian Salomonab664fa2017-03-24 16:07:20 +0000557 this->drawShadowedPath(canvas, fWideOvalPath, SkTMax(1.0f, 32 + fZDelta), paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500558 lightPos, kLightWidth, .5f);
559 }
560
561 bool onAnimate(const SkAnimTimer& timer) override {
Brian Salomonab664fa2017-03-24 16:07:20 +0000562 fAnimTranslate = timer.pingPong(30, 0, 200, -200);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500563
564 return true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700565 }
566
567protected:
568 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
569 return new SkView::Click(this);
570 }
571
572 bool onClick(Click *click) override {
573 SkScalar x = click->fCurr.fX;
574 SkScalar y = click->fCurr.fY;
575
576 SkScalar dx = x - click->fPrev.fX;
577 SkScalar dy = y - click->fPrev.fY;
578
579 if (dx != 0 || dy != 0) {
580 fLightPos.fX += dx;
581 fLightPos.fY += dy;
582 this->inval(nullptr);
583 }
584
585 return true;
586 }
587
588private:
Jim Van Verth6f449692017-02-14 15:16:46 -0500589 typedef SampleView INHERITED;
jvanverthe1a3bc62016-08-12 10:40:38 -0700590};
591
592//////////////////////////////////////////////////////////////////////////////
593
594static SkView* MyFactory() { return new ShadowsView; }
595static SkViewRegister reg(MyFactory);