blob: 02b494ff20d7b07a31b60b5d11181615d042a0a5 [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;
Jim Van Verthda965502017-04-11 15:29:14 -040040 SkScalar fAnimAngle;
jvanverthe1a3bc62016-08-12 10:40:38 -070041
42 bool fShowAmbient;
43 bool fShowSpot;
jvanverthd7315f912016-08-17 10:06:18 -070044 bool fUseAlt;
jvanverthe1a3bc62016-08-12 10:40:38 -070045 bool fShowObject;
Robert Phillips95304e32016-10-07 14:44:07 -040046 bool fIgnoreShadowAlpha;
jvanverthe1a3bc62016-08-12 10:40:38 -070047
48public:
Jim Van Verth2aa535e2017-02-06 14:36:01 -050049 ShadowsView()
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050050 : fZDelta(0)
51 , fAnimTranslate(0)
Jim Van Verthda965502017-04-11 15:29:14 -040052 , fAnimAngle(0)
Jim Van Verth2aa535e2017-02-06 14:36:01 -050053 , fShowAmbient(true)
jvanverthe1a3bc62016-08-12 10:40:38 -070054 , fShowSpot(true)
Jim Van Verthb6069df2017-04-28 11:00:35 -040055 , fUseAlt(false)
Robert Phillips95304e32016-10-07 14:44:07 -040056 , fShowObject(true)
57 , fIgnoreShadowAlpha(false) {}
jvanverthe1a3bc62016-08-12 10:40:38 -070058
59protected:
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));
Jim Van Verthbce74962017-01-25 09:39:46 -050064 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);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -050070 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));
Jim Van Verthbce74962017-01-25 09:39:46 -050074
Jim Van Verthb6069df2017-04-28 11:00:35 -040075 fLightPos = SkPoint3::Make(350, 0, 600);
jvanverthe1a3bc62016-08-12 10:40:38 -070076 }
77
78 // overrides from SkEventSink
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)) {
Jim Van Verth6f449692017-02-14 15:16:46 -050087 bool handled = false;
jvanverthe1a3bc62016-08-12 10:40:38 -070088 switch (uni) {
Jim Van Verth2aa535e2017-02-06 14:36:01 -050089 case 'W':
jvanverthe1a3bc62016-08-12 10:40:38 -070090 fShowAmbient = !fShowAmbient;
Jim Van Verth6f449692017-02-14 15:16:46 -050091 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -070092 break;
93 case 'S':
94 fShowSpot = !fShowSpot;
Jim Van Verth6f449692017-02-14 15:16:46 -050095 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -070096 break;
jvanverthd7315f912016-08-17 10:06:18 -070097 case 'T':
98 fUseAlt = !fUseAlt;
Jim Van Verth6f449692017-02-14 15:16:46 -050099 handled = true;
jvanverthd7315f912016-08-17 10:06:18 -0700100 break;
jvanverthe1a3bc62016-08-12 10:40:38 -0700101 case 'O':
102 fShowObject = !fShowObject;
Jim Van Verth6f449692017-02-14 15:16:46 -0500103 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700104 break;
105 case '>':
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500106 fZDelta += 0.5f;
Jim Van Verth6f449692017-02-14 15:16:46 -0500107 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700108 break;
109 case '<':
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500110 fZDelta -= 0.5f;
Jim Van Verth6f449692017-02-14 15:16:46 -0500111 handled = true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700112 break;
Robert Phillips95304e32016-10-07 14:44:07 -0400113 case '?':
114 fIgnoreShadowAlpha = !fIgnoreShadowAlpha;
Jim Van Verth6f449692017-02-14 15:16:46 -0500115 handled = true;
Robert Phillips95304e32016-10-07 14:44:07 -0400116 break;
jvanverthe1a3bc62016-08-12 10:40:38 -0700117 default:
118 break;
119 }
Jim Van Verth6f449692017-02-14 15:16:46 -0500120 if (handled) {
121 this->inval(nullptr);
122 return true;
123 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700124 }
125 return this->INHERITED::onQuery(evt);
126 }
127
128 void drawBG(SkCanvas* canvas) {
129 canvas->drawColor(0xFFDDDDDD);
130 }
131
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
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));
Robert Phillips95304e32016-10-07 14:44:07 -0400172 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
173 ? 255
174 : (unsigned char)(ambientAlpha*umbraAlpha*255.999f), 0, 0, 0));
jvanverthe1a3bc62016-08-12 10:40:38 -0700175 canvas->drawPath(path, paint);
176
177 // draw occlusion rect
jvanverth6c177a12016-08-17 07:59:41 -0700178#if DRAW_OCCL_RECT
jvanverthe1a3bc62016-08-12 10:40:38 -0700179 SkPaint stroke;
180 stroke.setStyle(SkPaint::kStroke_Style);
181 stroke.setColor(SK_ColorBLUE);
182 canvas->drawRect(occlRect, stroke);
jvanverth6c177a12016-08-17 07:59:41 -0700183#endif
184 }
185
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;
jvanverthd99858a2016-09-12 07:51:04 -0700198 // distance to outer of edge of geometry from original shape edge
199 SkScalar offset = radius*umbraAlpha;
jvanverth6c177a12016-08-17 07:59:41 -0700200
jvanvertha4f1af82016-08-29 07:17:47 -0700201 SkRect pathRect;
202 SkRRect pathRRect;
jvanverthd99858a2016-09-12 07:51:04 -0700203 SkScalar scaleFactors[2];
204 if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
205 return;
206 }
207 if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
jvanvertha4f1af82016-08-29 07:17:47 -0700208 !((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
jvanverthd99858a2016-09-12 07:51:04 -0700215 // 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 }
jvanverthd7315f912016-08-17 10:06:18 -0700221 if (path.isOval(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700222 pathRect.outset(strokeOutset, strokeOutset);
jvanverthd7315f912016-08-17 10:06:18 -0700223 pathRRect = SkRRect::MakeOval(pathRect);
224 } else if (path.isRect(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700225 pathRect.outset(strokeOutset, strokeOutset);
226 pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
jvanverth6c177a12016-08-17 07:59:41 -0700227 } else {
jvanverthd99858a2016-09-12 07:51:04 -0700228 pathRRect.outset(strokeOutset, strokeOutset);
jvanverth6c177a12016-08-17 07:59:41 -0700229 }
230
jvanverthd7315f912016-08-17 10:06:18 -0700231 SkPaint paint;
232 paint.setAntiAlias(true);
jvanvertha4f1af82016-08-29 07:17:47 -0700233 paint.setStyle(SkPaint::kStroke_Style);
234 // we outset the stroke a little to cover up AA on the interior edge
jvanverthd99858a2016-09-12 07:51:04 -0700235 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);
Robert Phillips95304e32016-10-07 14:44:07 -0400246 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
247 iRadius >> 8, iRadius & 0xff,
jvanverthd99858a2016-09-12 07:51:04 -0700248 (unsigned char)(4.0f*pad)));
jvanverthd7315f912016-08-17 10:06:18 -0700249
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500250 paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
jvanverthc20c0c02016-09-14 07:04:49 -0700251 paint.setShader(SkGaussianEdgeShader::Make());
jvanverthd7315f912016-08-17 10:06:18 -0700252 canvas->drawRRect(pathRRect, paint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700253 }
254
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 }
jvanverthd99858a2016-09-12 07:51:04 -0700267 SkScalar blurRadius = lightWidth*zRatio;
jvanverthe1a3bc62016-08-12 10:40:38 -0700268
269 // compute the transformation params
270 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
jvanverthd99858a2016-09-12 07:51:04 -0700271 SkMatrix ctmInverse;
272 if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
273 return;
jvanverthe1a3bc62016-08-12 10:40:38 -0700274 }
jvanverthd99858a2016-09-12 07:51:04 -0700275 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);
jvanverthe1a3bc62016-08-12 10:40:38 -0700280
281 SkAutoCanvasRestore acr(canvas, true);
282
jvanverthe1a3bc62016-08-12 10:40:38 -0700283 sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
jvanverthd99858a2016-09-12 07:51:04 -0700284 SkBlurMask::ConvertRadiusToSigma(blurRadius),
jvanverthe1a3bc62016-08-12 10:40:38 -0700285 SkBlurMaskFilter::kNone_BlurFlag);
286
287 SkPaint paint;
288 paint.setAntiAlias(true);
289 paint.setMaskFilter(std::move(mf));
Robert Phillips95304e32016-10-07 14:44:07 -0400290 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha
291 ? 255
292 : (unsigned char)(spotAlpha*255.999f), 0, 0, 0));
jvanverthe1a3bc62016-08-12 10:40:38 -0700293
294 // apply transformation to shadow
jvanverthe1a3bc62016-08-12 10:40:38 -0700295 canvas->scale(scale, scale);
jvanverthd99858a2016-09-12 07:51:04 -0700296 canvas->translate(offset.fX, offset.fY);
jvanverthe1a3bc62016-08-12 10:40:38 -0700297 canvas->drawPath(path, paint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700298 }
299
jvanverthd7315f912016-08-17 10:06:18 -0700300 void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
301 SkPoint3 lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
302 if (spotAlpha <= 0) {
303 return;
304 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700305
jvanverthd7315f912016-08-17 10:06:18 -0700306 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 }
jvanverthd99858a2016-09-12 07:51:04 -0700312 SkScalar radius = 2.0f*lightWidth*zRatio;
jvanverthd7315f912016-08-17 10:06:18 -0700313
jvanvertha4f1af82016-08-29 07:17:47 -0700314 SkRect pathRect;
315 SkRRect pathRRect;
jvanverthd99858a2016-09-12 07:51:04 -0700316 SkScalar scaleFactors[2];
317 if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
318 return;
319 }
320 if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
jvanvertha4f1af82016-08-29 07:17:47 -0700321 !((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
jvanverthd99858a2016-09-12 07:51:04 -0700328 // 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 -0400329 const SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
jvanverthd7315f912016-08-17 10:06:18 -0700330 if (path.isOval(nullptr)) {
jvanverthd7315f912016-08-17 10:06:18 -0700331 pathRRect = SkRRect::MakeOval(pathRect);
332 } else if (path.isRect(nullptr)) {
jvanverthd99858a2016-09-12 07:51:04 -0700333 pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
jvanverthd7315f912016-08-17 10:06:18 -0700334 } else {
jvanverthd99858a2016-09-12 07:51:04 -0700335 if (pathRRect.getSimpleRadii().fX < minRadius) {
336 pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
337 }
jvanverthd7315f912016-08-17 10:06:18 -0700338 }
339
jvanverthd99858a2016-09-12 07:51:04 -0700340 // 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));
jvanverthd7315f912016-08-17 10:06:18 -0700353 SkAutoCanvasRestore acr(canvas, true);
354
355 SkPaint paint;
356 paint.setAntiAlias(true);
Robert Phillips95304e32016-10-07 14:44:07 -0400357 // 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
jvanvertha4f1af82016-08-29 07:17:47 -0700367 // compute area
Robert Phillips95304e32016-10-07 14:44:07 -0400368 SkScalar strokeWidth = radius + insetAmount;
jvanverthd99858a2016-09-12 07:51:04 -0700369 SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
370 SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
jvanvertha4f1af82016-08-29 07:17:47 -0700371 // 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.
Robert Phillips95304e32016-10-07 14:44:07 -0400373 if (strokedArea > filledArea) {
jvanvertha4f1af82016-08-29 07:17:47 -0700374 paint.setStyle(SkPaint::kStrokeAndFill_Style);
375 paint.setStrokeWidth(radius);
376 } else {
Robert Phillips95304e32016-10-07 14:44:07 -0400377 // 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);
jvanvertha4f1af82016-08-29 07:17:47 -0700384 paint.setStyle(SkPaint::kStroke_Style);
385 paint.setStrokeWidth(strokeWidth);
386 }
Jim Van Verth2aa535e2017-02-06 14:36:01 -0500387 paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorBLACK, SkBlendMode::kModulate));
jvanverthc20c0c02016-09-14 07:04:49 -0700388 paint.setShader(SkGaussianEdgeShader::Make());
jvanverthd7315f912016-08-17 10:06:18 -0700389 // handle scale of radius due to CTM
jvanverthd99858a2016-09-12 07:51:04 -0700390 radius *= scaleFactors[0];
391 // don't need to scale pad as it was computed from the transformed offset
392 SkASSERT(radius < 16384);
Robert Phillips95304e32016-10-07 14:44:07 -0400393 SkScalar pad = 0;
jvanvertha4f1af82016-08-29 07:17:47 -0700394 SkASSERT(pad < 64);
jvanverthd99858a2016-09-12 07:51:04 -0700395 // 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);
Robert Phillips95304e32016-10-07 14:44:07 -0400399 paint.setColor(SkColorSetARGB(fIgnoreShadowAlpha ? 255 : alpha,
400 iRadius >> 8, iRadius & 0xff,
jvanverthd99858a2016-09-12 07:51:04 -0700401 (unsigned char)(4.0f*pad)));
jvanverthd7315f912016-08-17 10:06:18 -0700402
403 // apply transformation to shadow
404 canvas->translate(offset.fX, offset.fY);
jvanverthd99858a2016-09-12 07:51:04 -0700405 canvas->drawRRect(shadowRRect, paint);
jvanverthd7315f912016-08-17 10:06:18 -0700406 }
407
Jim Van Verthda965502017-04-11 15:29:14 -0400408 void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
409 std::function<SkScalar(SkScalar, SkScalar)> zFunc,
jvanverthd7315f912016-08-17 10:06:18 -0700410 const SkPaint& paint, SkScalar ambientAlpha,
411 const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
Jim Van Verth43475ad2017-01-13 14:37:37 -0500412#ifdef USE_SHADOW_UTILS
Jim Van Verthda965502017-04-11 15:29:14 -0400413 SkScalar zValue = zFunc(0, 0);
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400414 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 {
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400422 if (!fShowAmbient) {
423 ambientAlpha = 0;
424 }
425 if (!fShowSpot) {
426 spotAlpha = 0;
427 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400428
Jim Van Verthda965502017-04-11 15:29:14 -0400429 //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);
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400436 }
437#else
jvanverthe1a3bc62016-08-12 10:40:38 -0700438 if (fShowAmbient) {
jvanverthd7315f912016-08-17 10:06:18 -0700439 if (fUseAlt) {
440 this->drawAmbientShadowAlt(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700441 } else {
jvanverthd7315f912016-08-17 10:06:18 -0700442 this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
jvanverth6c177a12016-08-17 07:59:41 -0700443 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700444 }
445 if (fShowSpot) {
jvanverthd7315f912016-08-17 10:06:18 -0700446 if (fUseAlt) {
447 this->drawSpotShadowAlt(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
448 } else {
449 this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
450 }
jvanverthe1a3bc62016-08-12 10:40:38 -0700451 }
Jim Van Verth3c1b7db2016-10-31 16:06:51 -0400452#endif
453
jvanverthe1a3bc62016-08-12 10:40:38 -0700454 if (fShowObject) {
455 canvas->drawPath(path, paint);
Robert Phillips95304e32016-10-07 14:44:07 -0400456 } else {
457 SkPaint strokePaint;
458
459 strokePaint.setColor(paint.getColor());
460 strokePaint.setStyle(SkPaint::kStroke_Style);
461
462 canvas->drawPath(path, strokePaint);
jvanverthe1a3bc62016-08-12 10:40:38 -0700463 }
464 }
465
466 void onDrawContent(SkCanvas* canvas) override {
467 this->drawBG(canvas);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400468 const SkScalar kLightWidth = 800;
469 const SkScalar kAmbientAlpha = 0.1f;
jvanverthd7315f912016-08-17 10:06:18 -0700470 const SkScalar kSpotAlpha = 0.25f;
jvanverthe1a3bc62016-08-12 10:40:38 -0700471
472 SkPaint paint;
473 paint.setAntiAlias(true);
474
jvanverthd7315f912016-08-17 10:06:18 -0700475 SkPoint3 lightPos = fLightPos;
476
jvanverthe1a3bc62016-08-12 10:40:38 -0700477 paint.setColor(SK_ColorWHITE);
478 canvas->translate(200, 90);
Jim Van Verthda965502017-04-11 15:29:14 -0400479 SkScalar zValue = SkTMax(1.0f, 2 + fZDelta);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400480 std::function<SkScalar(SkScalar, SkScalar)> zFunc =
Jim Van Verthda965502017-04-11 15:29:14 -0400481 [zValue](SkScalar, SkScalar) { return zValue; };
482 this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700483 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700484
485 paint.setColor(SK_ColorRED);
486 canvas->translate(250, 0);
Jim Van Verthb6069df2017-04-28 11:00:35 -0400487 zValue = SkTMax(1.0f, 8 + fZDelta);
Jim Van Verthda965502017-04-11 15:29:14 -0400488 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
489 this->drawShadowedPath(canvas, fRectPath, zFunc, paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700490 lightPos, kLightWidth, kSpotAlpha);
jvanverthe1a3bc62016-08-12 10:40:38 -0700491
492 paint.setColor(SK_ColorBLUE);
493 canvas->translate(-250, 110);
Jim Van Verth8f7dc9f2017-04-20 15:48:37 -0400494 zValue = SkTMax(1.0f, 12 + fZDelta);
Jim Van Verthda965502017-04-11 15:29:14 -0400495 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
496 this->drawShadowedPath(canvas, fCirclePath, zFunc, paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700497 lightPos, kLightWidth, 0.5f);
jvanverth6c177a12016-08-17 07:59:41 -0700498
499 paint.setColor(SK_ColorGREEN);
500 canvas->translate(250, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400501 zValue = SkTMax(1.0f, 64 + fZDelta);
502 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
503 this->drawShadowedPath(canvas, fRRPath, zFunc, paint, kAmbientAlpha,
jvanverthd7315f912016-08-17 10:06:18 -0700504 lightPos, kLightWidth, kSpotAlpha);
Jim Van Verthbce74962017-01-25 09:39:46 -0500505
506 paint.setColor(SK_ColorYELLOW);
507 canvas->translate(-250, 110);
Jim Van Verthda965502017-04-11 15:29:14 -0400508 zValue = SkTMax(1.0f, 8 + fZDelta);
509 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
510 this->drawShadowedPath(canvas, fFunkyRRPath, zFunc, paint, kAmbientAlpha,
Jim Van Verthbce74962017-01-25 09:39:46 -0500511 lightPos, kLightWidth, kSpotAlpha);
512
513 paint.setColor(SK_ColorCYAN);
514 canvas->translate(250, 0);
Jim Van Verthda965502017-04-11 15:29:14 -0400515 zValue = SkTMax(1.0f, 16 + fZDelta);
516 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
517 this->drawShadowedPath(canvas, fCubicPath, zFunc, paint,
Brian Salomonab664fa2017-03-24 16:07:20 +0000518 kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500519
520 // circular reveal
521 SkPath tmpPath;
522 SkPath tmpClipPath;
523 tmpClipPath.addCircle(fAnimTranslate, 0, 60);
524 Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
525
526 paint.setColor(SK_ColorMAGENTA);
527 canvas->translate(-125, 60);
Jim Van Verthda965502017-04-11 15:29:14 -0400528 zValue = SkTMax(1.0f, 32 + fZDelta);
529 zFunc = [zValue](SkScalar, SkScalar) { return zValue; };
530 this->drawShadowedPath(canvas, tmpPath, zFunc, paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500531 lightPos, kLightWidth, .5f);
532
533 // perspective paths
534 SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
535 fWideRectPath.getBounds().height()/2);
Jim Van Verthda965502017-04-11 15:29:14 -0400536 SkPoint translate = SkPoint::Make(100, 450);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500537 paint.setColor(SK_ColorWHITE);
538 Sk3DView view;
539 view.save();
Jim Van Verthda965502017-04-11 15:29:14 -0400540 view.rotateX(fAnimAngle);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500541 SkMatrix persp;
542 view.getMatrix(&persp);
543 persp.preTranslate(-pivot.fX, -pivot.fY);
544 persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
545 canvas->setMatrix(persp);
Jim Van Verthda965502017-04-11 15:29:14 -0400546 zValue = SkTMax(1.0f, 16 + fZDelta);
547 SkScalar radians = SkDegreesToRadians(fAnimAngle);
548 zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
549 return SkScalarSin(-radians)*y +
550 zValue - SkScalarSin(-radians)*pivot.fY;
551 };
552 this->drawShadowedPath(canvas, fWideRectPath, zFunc, paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500553 lightPos, kLightWidth, .5f);
554
555 pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
556 fWideOvalPath.getBounds().height() / 2);
Jim Van Verthda965502017-04-11 15:29:14 -0400557 translate = SkPoint::Make(100, 600);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500558 view.restore();
Jim Van Verthda965502017-04-11 15:29:14 -0400559 view.rotateY(fAnimAngle);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500560 view.getMatrix(&persp);
561 persp.preTranslate(-pivot.fX, -pivot.fY);
562 persp.postTranslate(pivot.fX + translate.fX, pivot.fY + translate.fY);
563 canvas->setMatrix(persp);
Jim Van Verthda965502017-04-11 15:29:14 -0400564 zValue = SkTMax(1.0f, 32 + fZDelta);
565 zFunc = [zValue, pivot, radians](SkScalar x, SkScalar y) {
566 return -SkScalarSin(radians)*x +
567 zValue + SkScalarSin(radians)*pivot.fX;
568 };
569 this->drawShadowedPath(canvas, fWideOvalPath, zFunc, paint, .1f,
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500570 lightPos, kLightWidth, .5f);
571 }
572
573 bool onAnimate(const SkAnimTimer& timer) override {
Brian Salomonab664fa2017-03-24 16:07:20 +0000574 fAnimTranslate = timer.pingPong(30, 0, 200, -200);
Jim Van Verthda965502017-04-11 15:29:14 -0400575 fAnimAngle = timer.pingPong(15, 0, 0, 20);
Jim Van Verth2c1cd6d2017-03-10 14:34:51 -0500576
577 return true;
jvanverthe1a3bc62016-08-12 10:40:38 -0700578 }
579
580protected:
581 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
582 return new SkView::Click(this);
583 }
584
585 bool onClick(Click *click) override {
586 SkScalar x = click->fCurr.fX;
587 SkScalar y = click->fCurr.fY;
588
589 SkScalar dx = x - click->fPrev.fX;
590 SkScalar dy = y - click->fPrev.fY;
591
592 if (dx != 0 || dy != 0) {
593 fLightPos.fX += dx;
594 fLightPos.fY += dy;
595 this->inval(nullptr);
596 }
597
598 return true;
599 }
600
601private:
Jim Van Verth6f449692017-02-14 15:16:46 -0500602 typedef SampleView INHERITED;
jvanverthe1a3bc62016-08-12 10:40:38 -0700603};
604
605//////////////////////////////////////////////////////////////////////////////
606
607static SkView* MyFactory() { return new ShadowsView; }
608static SkViewRegister reg(MyFactory);