blob: 9f93906247b103091a355173862783bc45f6f699 [file] [log] [blame]
Jim Van Verth43475ad2017-01-13 14:37:37 -05001/*
2* Copyright 2017 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
Brian Salomon71fe9452020-03-02 16:59:40 -05008#include "include/utils/SkShadowUtils.h"
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
11#include "include/core/SkColorFilter.h"
12#include "include/core/SkMaskFilter.h"
13#include "include/core/SkPath.h"
14#include "include/core/SkString.h"
15#include "include/core/SkVertices.h"
16#include "include/private/SkColorData.h"
Brian Salomon71fe9452020-03-02 16:59:40 -050017#include "include/private/SkIDChangeListener.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/utils/SkRandom.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/core/SkBlurMask.h"
20#include "src/core/SkDevice.h"
21#include "src/core/SkDrawShadowInfo.h"
22#include "src/core/SkEffectPriv.h"
Jim Van Verthee90eb42019-04-26 12:07:13 -040023#include "src/core/SkPathPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/core/SkRasterPipeline.h"
25#include "src/core/SkResourceCache.h"
26#include "src/core/SkTLazy.h"
Mike Reedba962562020-03-12 20:33:21 -040027#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/utils/SkShadowTessellator.h"
Mike Klein79aea6a2018-06-11 10:45:26 -040029#include <new>
Brian Salomon5e689522017-02-01 12:07:17 -050030#if SK_SUPPORT_GPU
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h"
Michael Ludwig663afe52019-06-03 16:46:19 -040032#include "src/gpu/geometry/GrShape.h"
Mike Reed4204da22017-05-17 08:53:36 -040033#endif
Jim Van Verthefe3ded2017-01-30 13:11:45 -050034
35/**
36* Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
37* then blends with the color's G value.
38* Final result is black with alpha of Gaussian(B)*G.
39* The assumption is that the original color's alpha is 1.
40*/
Mike Reed57c2b8b2017-12-31 15:23:54 -050041class SkGaussianColorFilter : public SkColorFilter {
Jim Van Verthefe3ded2017-01-30 13:11:45 -050042public:
43 static sk_sp<SkColorFilter> Make() {
44 return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
45 }
46
Jim Van Verthefe3ded2017-01-30 13:11:45 -050047#if SK_SUPPORT_GPU
Brian Salomon4bc0c1f2019-09-30 15:12:27 -040048 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*,
49 const GrColorInfo&) const override;
Jim Van Verthefe3ded2017-01-30 13:11:45 -050050#endif
51
Mike Klein40f91382019-08-01 21:07:29 +000052protected:
Jim Van Verthefe3ded2017-01-30 13:11:45 -050053 void flatten(SkWriteBuffer&) const override {}
Mike Klein40f91382019-08-01 21:07:29 +000054 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
Mike Reed1386b2d2019-03-13 21:15:05 -040055 rec.fPipeline->append(SkRasterPipeline::gauss_a_to_rgba);
Mike Reed2fdbeae2019-03-30 14:27:53 -040056 return true;
Mike Reed65331592017-05-24 16:45:34 -040057 }
Mike Klein40f91382019-08-01 21:07:29 +000058private:
Mike Klein4fee3232018-10-18 17:27:16 -040059 SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
60
Jim Van Verthefe3ded2017-01-30 13:11:45 -050061 SkGaussianColorFilter() : INHERITED() {}
62
63 typedef SkColorFilter INHERITED;
64};
65
Jim Van Verthefe3ded2017-01-30 13:11:45 -050066sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
67 return Make();
68}
69
Jim Van Verthefe3ded2017-01-30 13:11:45 -050070#if SK_SUPPORT_GPU
Jim Van Verthefe3ded2017-01-30 13:11:45 -050071
Brian Salomonaff329b2017-08-11 09:40:37 -040072std::unique_ptr<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(
Brian Salomon4bc0c1f2019-09-30 15:12:27 -040073 GrRecordingContext*, const GrColorInfo&) const {
Ethan Nicholasaae47c82017-11-10 15:34:03 -050074 return GrBlurredEdgeFragmentProcessor::Make(GrBlurredEdgeFragmentProcessor::Mode::kGaussian);
Jim Van Verthefe3ded2017-01-30 13:11:45 -050075}
76#endif
77
78///////////////////////////////////////////////////////////////////////////////////////////////////
Brian Salomon5e689522017-02-01 12:07:17 -050079
80namespace {
81
Brian Salomonbc9956d2017-02-22 13:49:09 -050082uint64_t resource_cache_shared_id() {
83 return 0x2020776f64616873llu; // 'shadow '
84}
85
Brian Salomond1ac9822017-02-03 14:25:02 -050086/** Factory for an ambient shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -050087struct AmbientVerticesFactory {
Jim Van Verthb4366552017-03-27 14:25:29 -040088 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
Brian Salomon5e689522017-02-01 12:07:17 -050089 bool fTransparent;
Jim Van Verth8793e382017-05-22 15:52:21 -040090 SkVector fOffset;
Brian Salomon5e689522017-02-01 12:07:17 -050091
Brian Salomond1ac9822017-02-03 14:25:02 -050092 bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
Jim Van Verth060d9822017-05-04 09:58:17 -040093 if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
Brian Salomond1ac9822017-02-03 14:25:02 -050094 return false;
95 }
Jim Van Verth8793e382017-05-22 15:52:21 -040096 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -050097 return true;
Brian Salomon5e689522017-02-01 12:07:17 -050098 }
Brian Salomon5e689522017-02-01 12:07:17 -050099
Jim Van Verth8793e382017-05-22 15:52:21 -0400100 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
101 SkVector* translate) const {
Jim Van Verthe308a122017-05-08 14:19:30 -0400102 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400103 // pick a canonical place to generate shadow
104 SkMatrix noTrans(ctm);
105 if (!ctm.hasPerspective()) {
106 noTrans[SkMatrix::kMTransX] = 0;
107 noTrans[SkMatrix::kMTransY] = 0;
108 }
109 *translate = fOffset;
110 return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
Brian Salomon5e689522017-02-01 12:07:17 -0500111 }
112};
113
Brian Salomond1ac9822017-02-03 14:25:02 -0500114/** Factory for an spot shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500115struct SpotVerticesFactory {
Brian Salomond1ac9822017-02-03 14:25:02 -0500116 enum class OccluderType {
Jim Van Verth8793e382017-05-22 15:52:21 -0400117 // The umbra cannot be dropped out because either the occluder is not opaque,
118 // or the center of the umbra is visible.
Brian Salomond1ac9822017-02-03 14:25:02 -0500119 kTransparent,
120 // The umbra can be dropped where it is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400121 kOpaquePartialUmbra,
Brian Salomond1ac9822017-02-03 14:25:02 -0500122 // It is known that the entire umbra is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400123 kOpaqueNoUmbra
Brian Salomond1ac9822017-02-03 14:25:02 -0500124 };
125
Brian Salomon5e689522017-02-01 12:07:17 -0500126 SkVector fOffset;
Jim Van Verth8793e382017-05-22 15:52:21 -0400127 SkPoint fLocalCenter;
Jim Van Verthb4366552017-03-27 14:25:29 -0400128 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
129 SkPoint3 fDevLightPos;
130 SkScalar fLightRadius;
Brian Salomond1ac9822017-02-03 14:25:02 -0500131 OccluderType fOccluderType;
Brian Salomon5e689522017-02-01 12:07:17 -0500132
Brian Salomond1ac9822017-02-03 14:25:02 -0500133 bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
Jim Van Verthb4366552017-03-27 14:25:29 -0400134 if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
Jim Van Verth060d9822017-05-04 09:58:17 -0400135 fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500136 return false;
137 }
138 switch (fOccluderType) {
139 case OccluderType::kTransparent:
Jim Van Verth78c8f302017-05-15 10:44:22 -0400140 case OccluderType::kOpaqueNoUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500141 // 'this' and 'that' will either both have no umbra removed or both have all the
142 // umbra removed.
Jim Van Verth8793e382017-05-22 15:52:21 -0400143 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -0500144 return true;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400145 case OccluderType::kOpaquePartialUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500146 // In this case we partially remove the umbra differently for 'this' and 'that'
147 // if the offsets don't match.
148 if (fOffset == that.fOffset) {
149 translate->set(0, 0);
150 return true;
151 }
152 return false;
153 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400154 SK_ABORT("Uninitialized occluder type?");
Brian Salomon5e689522017-02-01 12:07:17 -0500155 }
Brian Salomon5e689522017-02-01 12:07:17 -0500156
Jim Van Verth8793e382017-05-22 15:52:21 -0400157 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
158 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500159 bool transparent = OccluderType::kTransparent == fOccluderType;
Jim Van Verthe308a122017-05-08 14:19:30 -0400160 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400161 if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
162 translate->set(0, 0);
163 return SkShadowTessellator::MakeSpot(path, ctm, zParams,
164 fDevLightPos, fLightRadius, transparent);
165 } else {
166 // pick a canonical place to generate shadow, with light centered over path
167 SkMatrix noTrans(ctm);
168 noTrans[SkMatrix::kMTransX] = 0;
169 noTrans[SkMatrix::kMTransY] = 0;
170 SkPoint devCenter(fLocalCenter);
171 noTrans.mapPoints(&devCenter, 1);
172 SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
173 *translate = fOffset;
174 return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
175 centerLightPos, fLightRadius, transparent);
176 }
Brian Salomon5e689522017-02-01 12:07:17 -0500177 }
178};
179
180/**
Brian Salomond1ac9822017-02-03 14:25:02 -0500181 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
182 * records are immutable this is not itself a Rec. When we need to update it we return this on
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400183 * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
Brian Salomond1ac9822017-02-03 14:25:02 -0500184 * a new Rec with an adjusted size for any deletions/additions.
Brian Salomon5e689522017-02-01 12:07:17 -0500185 */
Brian Salomond1ac9822017-02-03 14:25:02 -0500186class CachedTessellations : public SkRefCnt {
Brian Salomon5e689522017-02-01 12:07:17 -0500187public:
Brian Salomond1ac9822017-02-03 14:25:02 -0500188 size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
189
Brian Salomonaff27a22017-02-06 15:47:44 -0500190 sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
191 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500192 return fAmbientSet.find(ambient, matrix, translate);
193 }
194
Brian Salomonaff27a22017-02-06 15:47:44 -0500195 sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
Jim Van Verth8793e382017-05-22 15:52:21 -0400196 const SkMatrix& matrix, SkVector* translate) {
197 return fAmbientSet.add(devPath, ambient, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500198 }
199
Brian Salomonaff27a22017-02-06 15:47:44 -0500200 sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
201 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500202 return fSpotSet.find(spot, matrix, translate);
203 }
204
Brian Salomonaff27a22017-02-06 15:47:44 -0500205 sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
Jim Van Verth8793e382017-05-22 15:52:21 -0400206 const SkMatrix& matrix, SkVector* translate) {
207 return fSpotSet.add(devPath, spot, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500208 }
209
210private:
211 template <typename FACTORY, int MAX_ENTRIES>
212 class Set {
213 public:
214 size_t size() const { return fSize; }
215
Brian Salomonaff27a22017-02-06 15:47:44 -0500216 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
217 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500218 for (int i = 0; i < MAX_ENTRIES; ++i) {
219 if (fEntries[i].fFactory.isCompatible(factory, translate)) {
220 const SkMatrix& m = fEntries[i].fMatrix;
221 if (matrix.hasPerspective() || m.hasPerspective()) {
222 if (matrix != fEntries[i].fMatrix) {
223 continue;
224 }
225 } else if (matrix.getScaleX() != m.getScaleX() ||
226 matrix.getSkewX() != m.getSkewX() ||
227 matrix.getScaleY() != m.getScaleY() ||
228 matrix.getSkewY() != m.getSkewY()) {
229 continue;
230 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500231 return fEntries[i].fVertices;
232 }
233 }
234 return nullptr;
235 }
236
Jim Van Verth8793e382017-05-22 15:52:21 -0400237 sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
238 SkVector* translate) {
239 sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500240 if (!vertices) {
241 return nullptr;
242 }
243 int i;
244 if (fCount < MAX_ENTRIES) {
245 i = fCount++;
246 } else {
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400247 i = fRandom.nextULessThan(MAX_ENTRIES);
Mike Reedaa9e3322017-03-16 14:38:48 -0400248 fSize -= fEntries[i].fVertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500249 }
250 fEntries[i].fFactory = factory;
251 fEntries[i].fVertices = vertices;
252 fEntries[i].fMatrix = matrix;
Mike Reedaa9e3322017-03-16 14:38:48 -0400253 fSize += vertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500254 return vertices;
255 }
256
257 private:
258 struct Entry {
259 FACTORY fFactory;
Brian Salomonaff27a22017-02-06 15:47:44 -0500260 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500261 SkMatrix fMatrix;
262 };
263 Entry fEntries[MAX_ENTRIES];
264 int fCount = 0;
265 size_t fSize = 0;
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400266 SkRandom fRandom;
Brian Salomond1ac9822017-02-03 14:25:02 -0500267 };
268
269 Set<AmbientVerticesFactory, 4> fAmbientSet;
270 Set<SpotVerticesFactory, 4> fSpotSet;
Brian Salomond1ac9822017-02-03 14:25:02 -0500271};
272
Brian Salomond1ac9822017-02-03 14:25:02 -0500273/**
274 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
275 * path. The key represents the path's geometry and not any shadow params.
276 */
277class CachedTessellationsRec : public SkResourceCache::Rec {
278public:
279 CachedTessellationsRec(const SkResourceCache::Key& key,
280 sk_sp<CachedTessellations> tessellations)
281 : fTessellations(std::move(tessellations)) {
Brian Salomon5e689522017-02-01 12:07:17 -0500282 fKey.reset(new uint8_t[key.size()]);
283 memcpy(fKey.get(), &key, key.size());
284 }
285
286 const Key& getKey() const override {
287 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
288 }
Brian Salomon5e689522017-02-01 12:07:17 -0500289
Brian Salomond1ac9822017-02-03 14:25:02 -0500290 size_t bytesUsed() const override { return fTessellations->size(); }
Brian Salomon5e689522017-02-01 12:07:17 -0500291
Brian Salomond1ac9822017-02-03 14:25:02 -0500292 const char* getCategory() const override { return "tessellated shadow masks"; }
Brian Salomon5e689522017-02-01 12:07:17 -0500293
Brian Salomond1ac9822017-02-03 14:25:02 -0500294 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
Brian Salomon5e689522017-02-01 12:07:17 -0500295
Brian Salomond1ac9822017-02-03 14:25:02 -0500296 template <typename FACTORY>
Brian Salomonaff27a22017-02-06 15:47:44 -0500297 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
298 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500299 return fTessellations->find(factory, matrix, translate);
300 }
Brian Salomon5e689522017-02-01 12:07:17 -0500301
302private:
303 std::unique_ptr<uint8_t[]> fKey;
Brian Salomond1ac9822017-02-03 14:25:02 -0500304 sk_sp<CachedTessellations> fTessellations;
Brian Salomon5e689522017-02-01 12:07:17 -0500305};
306
307/**
308 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
Brian Salomond1ac9822017-02-03 14:25:02 -0500309 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
310 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
311 * to the caller. The caller will update it and reinsert it back into the cache.
Brian Salomon5e689522017-02-01 12:07:17 -0500312 */
313template <typename FACTORY>
314struct FindContext {
315 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
316 : fViewMatrix(viewMatrix), fFactory(factory) {}
Brian Salomond1ac9822017-02-03 14:25:02 -0500317 const SkMatrix* const fViewMatrix;
318 // If this is valid after Find is called then we found the vertices and they should be drawn
319 // with fTranslate applied.
Brian Salomonaff27a22017-02-06 15:47:44 -0500320 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500321 SkVector fTranslate = {0, 0};
322
323 // If this is valid after Find then the caller should add the vertices to the tessellation set
324 // and create a new CachedTessellationsRec and insert it into SkResourceCache.
325 sk_sp<CachedTessellations> fTessellationsOnFailure;
326
Brian Salomon5e689522017-02-01 12:07:17 -0500327 const FACTORY* fFactory;
328};
329
330/**
331 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
332 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
333 * necessary translation vector are set on the FindContext.
334 */
335template <typename FACTORY>
336bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
337 FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
Brian Salomond1ac9822017-02-03 14:25:02 -0500338 const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
339 findContext->fVertices =
340 rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
341 if (findContext->fVertices) {
342 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500343 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500344 // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
345 // manipulated we will add a new Rec.
346 findContext->fTessellationsOnFailure = rec.refTessellations();
347 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500348}
349
350class ShadowedPath {
351public:
352 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
Jim Van Vertha84898d2017-02-06 13:38:23 -0500353 : fPath(path)
Brian Salomon5e689522017-02-01 12:07:17 -0500354 , fViewMatrix(viewMatrix)
355#if SK_SUPPORT_GPU
356 , fShapeForKey(*path, GrStyle::SimpleFill())
357#endif
358 {}
359
Jim Van Vertha84898d2017-02-06 13:38:23 -0500360 const SkPath& path() const { return *fPath; }
Brian Salomon5e689522017-02-01 12:07:17 -0500361 const SkMatrix& viewMatrix() const { return *fViewMatrix; }
362#if SK_SUPPORT_GPU
363 /** Negative means the vertices should not be cached for this path. */
364 int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
365 void writeKey(void* key) const {
366 fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
367 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500368 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
Brian Salomon5e689522017-02-01 12:07:17 -0500369#else
370 int keyBytes() const { return -1; }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400371 void writeKey(void* key) const { SK_ABORT("Should never be called"); }
Brian Salomond1ac9822017-02-03 14:25:02 -0500372 bool isRRect(SkRRect* rrect) { return false; }
Brian Salomon5e689522017-02-01 12:07:17 -0500373#endif
374
375private:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500376 const SkPath* fPath;
Brian Salomon5e689522017-02-01 12:07:17 -0500377 const SkMatrix* fViewMatrix;
378#if SK_SUPPORT_GPU
379 GrShape fShapeForKey;
380#endif
Brian Salomon5e689522017-02-01 12:07:17 -0500381};
382
Brian Salomond1ac9822017-02-03 14:25:02 -0500383// This creates a domain of keys in SkResourceCache used by this file.
384static void* kNamespace;
385
Jim Van Verthee90eb42019-04-26 12:07:13 -0400386// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
Brian Salomon99a813c2020-03-02 12:50:47 -0500387class ShadowInvalidator : public SkIDChangeListener {
Jim Van Verthee90eb42019-04-26 12:07:13 -0400388public:
389 ShadowInvalidator(const SkResourceCache::Key& key) {
390 fKey.reset(new uint8_t[key.size()]);
391 memcpy(fKey.get(), &key, key.size());
392 }
393
394private:
395 const SkResourceCache::Key& getKey() const {
396 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
397 }
398
399 // always purge
400 static bool FindVisitor(const SkResourceCache::Rec&, void*) {
401 return false;
402 }
403
Brian Salomon99a813c2020-03-02 12:50:47 -0500404 void changed() override {
Jim Van Verthee90eb42019-04-26 12:07:13 -0400405 SkResourceCache::Find(this->getKey(), ShadowInvalidator::FindVisitor, nullptr);
406 }
407
408 std::unique_ptr<uint8_t[]> fKey;
409};
410
Brian Salomon5e689522017-02-01 12:07:17 -0500411/**
412 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
413 * they are first found in SkResourceCache.
414 */
415template <typename FACTORY>
Jim Van Verth22526362018-02-28 14:51:19 -0500416bool draw_shadow(const FACTORY& factory,
417 std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
Jim Van Verth1aaad022019-03-14 14:21:51 -0400418 SkScalar tx, SkScalar ty, bool)> drawProc, ShadowedPath& path, SkColor color) {
Brian Salomon5e689522017-02-01 12:07:17 -0500419 FindContext<FACTORY> context(&path.viewMatrix(), &factory);
Brian Salomon5e689522017-02-01 12:07:17 -0500420
421 SkResourceCache::Key* key = nullptr;
422 SkAutoSTArray<32 * 4, uint8_t> keyStorage;
423 int keyDataBytes = path.keyBytes();
424 if (keyDataBytes >= 0) {
425 keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
426 key = new (keyStorage.begin()) SkResourceCache::Key();
427 path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
Brian Salomonbc9956d2017-02-22 13:49:09 -0500428 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400429 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
Brian Salomon5e689522017-02-01 12:07:17 -0500430 }
431
Brian Salomonaff27a22017-02-06 15:47:44 -0500432 sk_sp<SkVertices> vertices;
Brian Salomon5e689522017-02-01 12:07:17 -0500433 bool foundInCache = SkToBool(context.fVertices);
434 if (foundInCache) {
435 vertices = std::move(context.fVertices);
Brian Salomon5e689522017-02-01 12:07:17 -0500436 } else {
437 // TODO: handle transforming the path as part of the tessellator
Brian Salomond1ac9822017-02-03 14:25:02 -0500438 if (key) {
439 // Update or initialize a tessellation set and add it to the cache.
440 sk_sp<CachedTessellations> tessellations;
441 if (context.fTessellationsOnFailure) {
442 tessellations = std::move(context.fTessellationsOnFailure);
443 } else {
444 tessellations.reset(new CachedTessellations());
445 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400446 vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
447 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500448 if (!vertices) {
Jim Van Verth22526362018-02-28 14:51:19 -0500449 return false;
Brian Salomond1ac9822017-02-03 14:25:02 -0500450 }
Brian Salomon804e0912017-02-23 09:34:03 -0500451 auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
Jim Van Verthee90eb42019-04-26 12:07:13 -0400452 SkPathPriv::AddGenIDChangeListener(path.path(), sk_make_sp<ShadowInvalidator>(*key));
Jim Van Verth37c5a962017-05-10 14:13:24 -0400453 SkResourceCache::Add(rec);
Brian Salomond1ac9822017-02-03 14:25:02 -0500454 } else {
Jim Van Verth8793e382017-05-22 15:52:21 -0400455 vertices = factory.makeVertices(path.path(), path.viewMatrix(),
456 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500457 if (!vertices) {
Jim Van Verth22526362018-02-28 14:51:19 -0500458 return false;
Brian Salomond1ac9822017-02-03 14:25:02 -0500459 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500460 }
Brian Salomon5e689522017-02-01 12:07:17 -0500461 }
462
463 SkPaint paint;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500464 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
465 // that against our 'color' param.
Mike Reed19d7bd62018-02-19 14:10:57 -0500466 paint.setColorFilter(
Mike Reedb286bc22019-04-08 16:23:20 -0400467 SkColorFilters::Blend(color, SkBlendMode::kModulate)->makeComposed(
468 SkGaussianColorFilter::Make()));
Mike Reed4204da22017-05-17 08:53:36 -0400469
Jim Van Verth8793e382017-05-22 15:52:21 -0400470 drawProc(vertices.get(), SkBlendMode::kModulate, paint,
Jim Van Verth1aaad022019-03-14 14:21:51 -0400471 context.fTranslate.fX, context.fTranslate.fY, path.viewMatrix().hasPerspective());
Jim Van Verth22526362018-02-28 14:51:19 -0500472
473 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500474}
475}
476
Mike Reed4204da22017-05-17 08:53:36 -0400477static bool tilted(const SkPoint3& zPlaneParams) {
478 return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
479}
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400480
Mike Reed4204da22017-05-17 08:53:36 -0400481static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
482 SkPoint3 result;
483 m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
484 result.fZ = pt.fZ;
485 return result;
486}
487
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500488void SkShadowUtils::ComputeTonalColors(SkColor inAmbientColor, SkColor inSpotColor,
489 SkColor* outAmbientColor, SkColor* outSpotColor) {
490 // For tonal color we only compute color values for the spot shadow.
491 // The ambient shadow is greyscale only.
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400492
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500493 // Ambient
494 *outAmbientColor = SkColorSetARGB(SkColorGetA(inAmbientColor), 0, 0, 0);
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400495
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500496 // Spot
497 int spotR = SkColorGetR(inSpotColor);
498 int spotG = SkColorGetG(inSpotColor);
499 int spotB = SkColorGetB(inSpotColor);
Brian Osman788b9162020-02-07 10:36:46 -0500500 int max = std::max(std::max(spotR, spotG), spotB);
501 int min = std::min(std::min(spotR, spotG), spotB);
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500502 SkScalar luminance = 0.5f*(max + min)/255.f;
503 SkScalar origA = SkColorGetA(inSpotColor)/255.f;
504
505 // We compute a color alpha value based on the luminance of the color, scaled by an
506 // adjusted alpha value. We want the following properties to match the UX examples
507 // (assuming a = 0.25) and to ensure that we have reasonable results when the color
508 // is black and/or the alpha is 0:
509 // f(0, a) = 0
510 // f(luminance, 0) = 0
511 // f(1, 0.25) = .5
512 // f(0.5, 0.25) = .4
513 // f(1, 1) = 1
514 // The following functions match this as closely as possible.
515 SkScalar alphaAdjust = (2.6f + (-2.66667f + 1.06667f*origA)*origA)*origA;
516 SkScalar colorAlpha = (3.544762f + (-4.891428f + 2.3466f*luminance)*luminance)*luminance;
517 colorAlpha = SkTPin(alphaAdjust*colorAlpha, 0.0f, 1.0f);
518
519 // Similarly, we set the greyscale alpha based on luminance and alpha so that
520 // f(0, a) = a
521 // f(luminance, 0) = 0
522 // f(1, 0.25) = 0.15
523 SkScalar greyscaleAlpha = SkTPin(origA*(1 - 0.4f*luminance), 0.0f, 1.0f);
524
525 // The final color we want to emulate is generated by rendering a color shadow (C_rgb) using an
526 // alpha computed from the color's luminance (C_a), and then a black shadow with alpha (S_a)
527 // which is an adjusted value of 'a'. Assuming SrcOver, a background color of B_rgb, and
528 // ignoring edge falloff, this becomes
529 //
530 // (C_a - S_a*C_a)*C_rgb + (1 - (S_a + C_a - S_a*C_a))*B_rgb
531 //
532 // Assuming premultiplied alpha, this means we scale the color by (C_a - S_a*C_a) and
533 // set the alpha to (S_a + C_a - S_a*C_a).
534 SkScalar colorScale = colorAlpha*(SK_Scalar1 - greyscaleAlpha);
535 SkScalar tonalAlpha = colorScale + greyscaleAlpha;
536 SkScalar unPremulScale = colorScale / tonalAlpha;
537 *outSpotColor = SkColorSetARGB(tonalAlpha*255.999f,
538 unPremulScale*spotR,
539 unPremulScale*spotG,
540 unPremulScale*spotB);
Jim Van Verth060d9822017-05-04 09:58:17 -0400541}
542
Jim Van Verth43475ad2017-01-13 14:37:37 -0500543// Draw an offset spot shadow and outlining ambient shadow for the given path.
Jim Van Verth37c5a962017-05-10 14:13:24 -0400544void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
Brian Salomon0bd699e2017-02-01 12:23:25 -0500545 const SkPoint3& devLightPos, SkScalar lightRadius,
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500546 SkColor ambientColor, SkColor spotColor,
Jim Van Verth37c5a962017-05-10 14:13:24 -0400547 uint32_t flags) {
Mike Reed4204da22017-05-17 08:53:36 -0400548 SkMatrix inverse;
549 if (!canvas->getTotalMatrix().invert(&inverse)) {
Jim Van Verthcf40e302017-03-02 11:28:43 -0500550 return;
551 }
Mike Reed4204da22017-05-17 08:53:36 -0400552 SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
Jim Van Verthcf40e302017-03-02 11:28:43 -0500553
Mike Reed4204da22017-05-17 08:53:36 -0400554 SkDrawShadowRec rec;
555 rec.fZPlaneParams = zPlaneParams;
556 rec.fLightPos = { pt.fX, pt.fY, devLightPos.fZ };
557 rec.fLightRadius = lightRadius;
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500558 rec.fAmbientColor = ambientColor;
559 rec.fSpotColor = spotColor;
Mike Reed4204da22017-05-17 08:53:36 -0400560 rec.fFlags = flags;
561
562 canvas->private_draw_shadow_rec(path, rec);
563}
564
Jim Van Vertha947e292018-02-26 13:54:34 -0500565static bool validate_rec(const SkDrawShadowRec& rec) {
566 return rec.fLightPos.isFinite() && rec.fZPlaneParams.isFinite() &&
567 SkScalarIsFinite(rec.fLightRadius);
568}
569
Mike Reed4204da22017-05-17 08:53:36 -0400570void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
571 auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
Jim Van Verth1aaad022019-03-14 14:21:51 -0400572 SkScalar tx, SkScalar ty, bool hasPerspective) {
Mike Reedba962562020-03-12 20:33:21 -0400573 if (SkVerticesPriv::VertexCount(vertices)) {
Jim Van Verth1aaad022019-03-14 14:21:51 -0400574 // For perspective shadows we've already computed the shadow in world space,
575 // and we can't translate it without changing it. Otherwise we concat the
576 // change in translation from the cached version.
Michael Ludwigc89d1b52019-10-18 11:32:56 -0400577 SkAutoDeviceTransformRestore adr(
578 this,
579 hasPerspective ? SkMatrix::I()
580 : SkMatrix::Concat(this->localToDevice(),
581 SkMatrix::MakeTrans(tx, ty)));
Mike Reed5caf9352020-03-02 14:57:09 -0500582 this->drawVertices(vertices, mode, paint);
Jim Van Verth8664a1d2018-06-28 16:26:50 -0400583 }
Mike Reed4204da22017-05-17 08:53:36 -0400584 };
585
Jim Van Vertha947e292018-02-26 13:54:34 -0500586 if (!validate_rec(rec)) {
587 return;
588 }
589
Michael Ludwigc89d1b52019-10-18 11:32:56 -0400590 SkMatrix viewMatrix = this->localToDevice();
591 SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500592
Brian Salomon5e689522017-02-01 12:07:17 -0500593 ShadowedPath shadowedPath(&path, &viewMatrix);
594
Mike Reed4204da22017-05-17 08:53:36 -0400595 bool tiltZPlane = tilted(rec.fZPlaneParams);
596 bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400597 bool uncached = tiltZPlane || path.isVolatile();
Brian Salomon958fbc42017-01-30 17:01:28 -0500598
Mike Reed4204da22017-05-17 08:53:36 -0400599 SkPoint3 zPlaneParams = rec.fZPlaneParams;
600 SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
601 float lightRadius = rec.fLightRadius;
602
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500603 if (SkColorGetA(rec.fAmbientColor) > 0) {
Jim Van Verth22526362018-02-28 14:51:19 -0500604 bool success = false;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400605 if (uncached) {
606 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
607 zPlaneParams,
608 transparent);
Jim Van Verth7d8955e2017-07-13 15:13:52 -0400609 if (vertices) {
610 SkPaint paint;
611 // Run the vertex color through a GaussianColorFilter and then modulate the
612 // grayscale result of that against our 'color' param.
Mike Reed19d7bd62018-02-19 14:10:57 -0500613 paint.setColorFilter(
Mike Reedb286bc22019-04-08 16:23:20 -0400614 SkColorFilters::Blend(rec.fAmbientColor,
Mike Reed19d7bd62018-02-19 14:10:57 -0500615 SkBlendMode::kModulate)->makeComposed(
616 SkGaussianColorFilter::Make()));
Mike Reed5caf9352020-03-02 14:57:09 -0500617 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Jim Van Verth22526362018-02-28 14:51:19 -0500618 success = true;
Jim Van Verth7d8955e2017-07-13 15:13:52 -0400619 }
Jim Van Verth22526362018-02-28 14:51:19 -0500620 }
621
622 if (!success) {
Jim Van Verth37c5a962017-05-10 14:13:24 -0400623 AmbientVerticesFactory factory;
624 factory.fOccluderHeight = zPlaneParams.fZ;
625 factory.fTransparent = transparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400626 if (viewMatrix.hasPerspective()) {
627 factory.fOffset.set(0, 0);
628 } else {
629 factory.fOffset.fX = viewMatrix.getTranslateX();
630 factory.fOffset.fY = viewMatrix.getTranslateY();
631 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400632
Jim Van Verth22526362018-02-28 14:51:19 -0500633 if (!draw_shadow(factory, drawVertsProc, shadowedPath, rec.fAmbientColor)) {
634 // Pretransform the path to avoid transforming the stroke, below.
635 SkPath devSpacePath;
636 path.transform(viewMatrix, &devSpacePath);
Robert Phillipsed3dbf42019-03-18 12:20:15 -0400637 devSpacePath.setIsVolatile(true);
Jim Van Verth22526362018-02-28 14:51:19 -0500638
639 // The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
Jim Van Verth3a039d52018-09-14 17:14:47 -0400640 // the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
Jim Van Verth22526362018-02-28 14:51:19 -0500641 //
642 // We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
643 // can be calculated by interpolating:
644 //
645 // original edge outer edge
646 // | |<---------- r ------>|
647 // |<------|--- f -------------->|
648 // | | |
649 // alpha = 1 alpha = a alpha = 0
650 //
651 // Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
652 //
653 // We now need to outset the path to place the new edge in the center of the
654 // blur region:
655 //
656 // original new
657 // | |<------|--- r ------>|
658 // |<------|--- f -|------------>|
659 // | |<- o ->|<--- f/2 --->|
660 //
661 // r = o + f/2, so o = r - f/2
662 //
663 // We outset by using the stroker, so the strokeWidth is o/2.
664 //
665 SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
666 SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
667 SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
668 SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
669
670 // Now draw with blur
671 SkPaint paint;
672 paint.setColor(rec.fAmbientColor);
673 paint.setStrokeWidth(strokeWidth);
674 paint.setStyle(SkPaint::kStrokeAndFill_Style);
Mike Reed8e03f692018-03-09 16:18:56 -0500675 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
Mike Reed18e75562018-03-12 14:03:47 -0400676 bool respectCTM = false;
677 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
Jim Van Verth22526362018-02-28 14:51:19 -0500678 this->drawPath(devSpacePath, paint);
679 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500680 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400681 }
682
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500683 if (SkColorGetA(rec.fSpotColor) > 0) {
Jim Van Verth22526362018-02-28 14:51:19 -0500684 bool success = false;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400685 if (uncached) {
686 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
687 zPlaneParams,
688 devLightPos, lightRadius,
689 transparent);
Jim Van Verth7d8955e2017-07-13 15:13:52 -0400690 if (vertices) {
691 SkPaint paint;
692 // Run the vertex color through a GaussianColorFilter and then modulate the
693 // grayscale result of that against our 'color' param.
Mike Reed19d7bd62018-02-19 14:10:57 -0500694 paint.setColorFilter(
Mike Reedb286bc22019-04-08 16:23:20 -0400695 SkColorFilters::Blend(rec.fSpotColor,
Mike Reed19d7bd62018-02-19 14:10:57 -0500696 SkBlendMode::kModulate)->makeComposed(
Jim Van Verth22526362018-02-28 14:51:19 -0500697 SkGaussianColorFilter::Make()));
Mike Reed5caf9352020-03-02 14:57:09 -0500698 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Jim Van Verth22526362018-02-28 14:51:19 -0500699 success = true;
Jim Van Verth7d8955e2017-07-13 15:13:52 -0400700 }
Jim Van Verth22526362018-02-28 14:51:19 -0500701 }
Jim Van Vertha783c362017-05-11 17:05:28 -0400702
Jim Van Verth22526362018-02-28 14:51:19 -0500703 if (!success) {
704 SpotVerticesFactory factory;
705 factory.fOccluderHeight = zPlaneParams.fZ;
706 factory.fDevLightPos = devLightPos;
707 factory.fLightRadius = lightRadius;
708
Jim Van Verth37c5a962017-05-10 14:13:24 -0400709 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verth8793e382017-05-22 15:52:21 -0400710 factory.fLocalCenter = center;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400711 viewMatrix.mapPoints(&center, 1);
Jim Van Verth22526362018-02-28 14:51:19 -0500712 SkScalar radius, scale;
713 SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
714 devLightPos.fY - center.fY, devLightPos.fZ,
715 lightRadius, &radius, &scale, &factory.fOffset);
Jim Van Vertha783c362017-05-11 17:05:28 -0400716 SkRect devBounds;
717 viewMatrix.mapRect(&devBounds, path.getBounds());
Jim Van Verth8793e382017-05-22 15:52:21 -0400718 if (transparent ||
719 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
720 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400721 // if the translation of the shadow is big enough we're going to end up
722 // filling the entire umbra, so we can treat these as all the same
Jim Van Verth8793e382017-05-22 15:52:21 -0400723 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400724 } else if (factory.fOffset.length()*scale + scale < radius) {
Jim Van Vertha783c362017-05-11 17:05:28 -0400725 // if we don't translate more than the blur distance, can assume umbra is covered
Jim Van Verth78c8f302017-05-15 10:44:22 -0400726 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400727 } else if (path.isConvex()) {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400728 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
Jim Van Verth8760e2f2018-06-12 14:21:38 -0400729 } else {
730 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
Jim Van Vertha783c362017-05-11 17:05:28 -0400731 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400732 // need to add this after we classify the shadow
733 factory.fOffset.fX += viewMatrix.getTranslateX();
734 factory.fOffset.fY += viewMatrix.getTranslateY();
Jim Van Verth22526362018-02-28 14:51:19 -0500735
736 SkColor color = rec.fSpotColor;
Jim Van Vertha783c362017-05-11 17:05:28 -0400737#ifdef DEBUG_SHADOW_CHECKS
738 switch (factory.fOccluderType) {
739 case SpotVerticesFactory::OccluderType::kTransparent:
740 color = 0xFFD2B48C; // tan for transparent
741 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400742 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
Jim Van Vertha783c362017-05-11 17:05:28 -0400743 color = 0xFFFFA500; // orange for opaque
744 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400745 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
746 color = 0xFFE5E500; // corn yellow for covered
Jim Van Vertha783c362017-05-11 17:05:28 -0400747 break;
748 }
749#endif
Jim Van Verth22526362018-02-28 14:51:19 -0500750 if (!draw_shadow(factory, drawVertsProc, shadowedPath, color)) {
751 // draw with blur
Jim Van Verth22526362018-02-28 14:51:19 -0500752 SkMatrix shadowMatrix;
Jim Van Verth3a039d52018-09-14 17:14:47 -0400753 if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
754 viewMatrix, zPlaneParams,
755 path.getBounds(),
756 &shadowMatrix, &radius)) {
757 return;
758 }
Michael Ludwigc89d1b52019-10-18 11:32:56 -0400759 SkAutoDeviceTransformRestore adr(this, shadowMatrix);
Jim Van Verth22526362018-02-28 14:51:19 -0500760
761 SkPaint paint;
762 paint.setColor(rec.fSpotColor);
Mike Reed8e03f692018-03-09 16:18:56 -0500763 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
Mike Reed18e75562018-03-12 14:03:47 -0400764 bool respectCTM = false;
765 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
Jim Van Verth22526362018-02-28 14:51:19 -0500766 this->drawPath(path, paint);
767 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400768 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400769 }
770}