• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkMaskFilter.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkString.h"
13 #include "include/core/SkVertices.h"
14 #include "include/private/SkColorData.h"
15 #include "include/utils/SkRandom.h"
16 #include "include/utils/SkShadowUtils.h"
17 #include "src/core/SkBlurMask.h"
18 #include "src/core/SkDevice.h"
19 #include "src/core/SkDrawShadowInfo.h"
20 #include "src/core/SkEffectPriv.h"
21 #include "src/core/SkPathPriv.h"
22 #include "src/core/SkRasterPipeline.h"
23 #include "src/core/SkResourceCache.h"
24 #include "src/core/SkTLazy.h"
25 #include "src/utils/SkShadowTessellator.h"
26 #include <new>
27 #if SK_SUPPORT_GPU
28 #include "src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h"
29 #include "src/gpu/geometry/GrShape.h"
30 #endif
31 
32 /**
33 *  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
34 *                           then blends with the color's G value.
35 *                           Final result is black with alpha of Gaussian(B)*G.
36 *                           The assumption is that the original color's alpha is 1.
37 */
38 class SkGaussianColorFilter : public SkColorFilter {
39 public:
Make()40     static sk_sp<SkColorFilter> Make() {
41         return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
42     }
43 
44 #if SK_SUPPORT_GPU
45     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
46             GrRecordingContext*, const GrColorSpaceInfo&) const override;
47 #endif
48 
49 protected:
flatten(SkWriteBuffer &) const50     void flatten(SkWriteBuffer&) const override {}
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const51     bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
52         rec.fPipeline->append(SkRasterPipeline::gauss_a_to_rgba);
53         return true;
54     }
55 private:
56     SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
57 
SkGaussianColorFilter()58     SkGaussianColorFilter() : INHERITED() {}
59 
60     typedef SkColorFilter INHERITED;
61 };
62 
CreateProc(SkReadBuffer &)63 sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
64     return Make();
65 }
66 
67 #if SK_SUPPORT_GPU
68 
asFragmentProcessor(GrRecordingContext *,const GrColorSpaceInfo &) const69 std::unique_ptr<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(
70         GrRecordingContext*, const GrColorSpaceInfo&) const {
71     return GrBlurredEdgeFragmentProcessor::Make(GrBlurredEdgeFragmentProcessor::Mode::kGaussian);
72 }
73 #endif
74 
75 ///////////////////////////////////////////////////////////////////////////////////////////////////
76 
77 namespace {
78 
resource_cache_shared_id()79 uint64_t resource_cache_shared_id() {
80     return 0x2020776f64616873llu;  // 'shadow  '
81 }
82 
83 /** Factory for an ambient shadow mesh with particular shadow properties. */
84 struct AmbientVerticesFactory {
85     SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
86     bool fTransparent;
87     SkVector fOffset;
88 
isCompatible__anonb977c80b0111::AmbientVerticesFactory89     bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
90         if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
91             return false;
92         }
93         *translate = that.fOffset;
94         return true;
95     }
96 
makeVertices__anonb977c80b0111::AmbientVerticesFactory97     sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
98                                    SkVector* translate) const {
99         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
100         // pick a canonical place to generate shadow
101         SkMatrix noTrans(ctm);
102         if (!ctm.hasPerspective()) {
103             noTrans[SkMatrix::kMTransX] = 0;
104             noTrans[SkMatrix::kMTransY] = 0;
105         }
106         *translate = fOffset;
107         return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
108     }
109 };
110 
111 /** Factory for an spot shadow mesh with particular shadow properties. */
112 struct SpotVerticesFactory {
113     enum class OccluderType {
114         // The umbra cannot be dropped out because either the occluder is not opaque,
115         // or the center of the umbra is visible.
116         kTransparent,
117         // The umbra can be dropped where it is occluded.
118         kOpaquePartialUmbra,
119         // It is known that the entire umbra is occluded.
120         kOpaqueNoUmbra
121     };
122 
123     SkVector fOffset;
124     SkPoint  fLocalCenter;
125     SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
126     SkPoint3 fDevLightPos;
127     SkScalar fLightRadius;
128     OccluderType fOccluderType;
129 
isCompatible__anonb977c80b0111::SpotVerticesFactory130     bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
131         if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
132             fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
133             return false;
134         }
135         switch (fOccluderType) {
136             case OccluderType::kTransparent:
137             case OccluderType::kOpaqueNoUmbra:
138                 // 'this' and 'that' will either both have no umbra removed or both have all the
139                 // umbra removed.
140                 *translate = that.fOffset;
141                 return true;
142             case OccluderType::kOpaquePartialUmbra:
143                 // In this case we partially remove the umbra differently for 'this' and 'that'
144                 // if the offsets don't match.
145                 if (fOffset == that.fOffset) {
146                     translate->set(0, 0);
147                     return true;
148                 }
149                 return false;
150         }
151         SK_ABORT("Uninitialized occluder type?");
152     }
153 
makeVertices__anonb977c80b0111::SpotVerticesFactory154     sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
155                                    SkVector* translate) const {
156         bool transparent = OccluderType::kTransparent == fOccluderType;
157         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
158         if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
159             translate->set(0, 0);
160             return SkShadowTessellator::MakeSpot(path, ctm, zParams,
161                                                  fDevLightPos, fLightRadius, transparent);
162         } else {
163             // pick a canonical place to generate shadow, with light centered over path
164             SkMatrix noTrans(ctm);
165             noTrans[SkMatrix::kMTransX] = 0;
166             noTrans[SkMatrix::kMTransY] = 0;
167             SkPoint devCenter(fLocalCenter);
168             noTrans.mapPoints(&devCenter, 1);
169             SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
170             *translate = fOffset;
171             return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
172                                                  centerLightPos, fLightRadius, transparent);
173         }
174     }
175 };
176 
177 /**
178  * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
179  * records are immutable this is not itself a Rec. When we need to update it we return this on
180  * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
181  * a new Rec with an adjusted size for any deletions/additions.
182  */
183 class CachedTessellations : public SkRefCnt {
184 public:
size() const185     size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
186 
find(const AmbientVerticesFactory & ambient,const SkMatrix & matrix,SkVector * translate) const187     sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
188                            SkVector* translate) const {
189         return fAmbientSet.find(ambient, matrix, translate);
190     }
191 
add(const SkPath & devPath,const AmbientVerticesFactory & ambient,const SkMatrix & matrix,SkVector * translate)192     sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
193                           const SkMatrix& matrix, SkVector* translate) {
194         return fAmbientSet.add(devPath, ambient, matrix, translate);
195     }
196 
find(const SpotVerticesFactory & spot,const SkMatrix & matrix,SkVector * translate) const197     sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
198                            SkVector* translate) const {
199         return fSpotSet.find(spot, matrix, translate);
200     }
201 
add(const SkPath & devPath,const SpotVerticesFactory & spot,const SkMatrix & matrix,SkVector * translate)202     sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
203                           const SkMatrix& matrix, SkVector* translate) {
204         return fSpotSet.add(devPath, spot, matrix, translate);
205     }
206 
207 private:
208     template <typename FACTORY, int MAX_ENTRIES>
209     class Set {
210     public:
size() const211         size_t size() const { return fSize; }
212 
find(const FACTORY & factory,const SkMatrix & matrix,SkVector * translate) const213         sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
214                                SkVector* translate) const {
215             for (int i = 0; i < MAX_ENTRIES; ++i) {
216                 if (fEntries[i].fFactory.isCompatible(factory, translate)) {
217                     const SkMatrix& m = fEntries[i].fMatrix;
218                     if (matrix.hasPerspective() || m.hasPerspective()) {
219                         if (matrix != fEntries[i].fMatrix) {
220                             continue;
221                         }
222                     } else if (matrix.getScaleX() != m.getScaleX() ||
223                                matrix.getSkewX() != m.getSkewX() ||
224                                matrix.getScaleY() != m.getScaleY() ||
225                                matrix.getSkewY() != m.getSkewY()) {
226                         continue;
227                     }
228                     return fEntries[i].fVertices;
229                 }
230             }
231             return nullptr;
232         }
233 
add(const SkPath & path,const FACTORY & factory,const SkMatrix & matrix,SkVector * translate)234         sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
235                               SkVector* translate) {
236             sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
237             if (!vertices) {
238                 return nullptr;
239             }
240             int i;
241             if (fCount < MAX_ENTRIES) {
242                 i = fCount++;
243             } else {
244                 i = fRandom.nextULessThan(MAX_ENTRIES);
245                 fSize -= fEntries[i].fVertices->approximateSize();
246             }
247             fEntries[i].fFactory = factory;
248             fEntries[i].fVertices = vertices;
249             fEntries[i].fMatrix = matrix;
250             fSize += vertices->approximateSize();
251             return vertices;
252         }
253 
254     private:
255         struct Entry {
256             FACTORY fFactory;
257             sk_sp<SkVertices> fVertices;
258             SkMatrix fMatrix;
259         };
260         Entry fEntries[MAX_ENTRIES];
261         int fCount = 0;
262         size_t fSize = 0;
263         SkRandom fRandom;
264     };
265 
266     Set<AmbientVerticesFactory, 4> fAmbientSet;
267     Set<SpotVerticesFactory, 4> fSpotSet;
268 };
269 
270 /**
271  * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
272  * path. The key represents the path's geometry and not any shadow params.
273  */
274 class CachedTessellationsRec : public SkResourceCache::Rec {
275 public:
CachedTessellationsRec(const SkResourceCache::Key & key,sk_sp<CachedTessellations> tessellations)276     CachedTessellationsRec(const SkResourceCache::Key& key,
277                            sk_sp<CachedTessellations> tessellations)
278             : fTessellations(std::move(tessellations)) {
279         fKey.reset(new uint8_t[key.size()]);
280         memcpy(fKey.get(), &key, key.size());
281     }
282 
getKey() const283     const Key& getKey() const override {
284         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
285     }
286 
bytesUsed() const287     size_t bytesUsed() const override { return fTessellations->size(); }
288 
getCategory() const289     const char* getCategory() const override { return "tessellated shadow masks"; }
290 
refTessellations() const291     sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
292 
293     template <typename FACTORY>
find(const FACTORY & factory,const SkMatrix & matrix,SkVector * translate) const294     sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
295                            SkVector* translate) const {
296         return fTessellations->find(factory, matrix, translate);
297     }
298 
299 private:
300     std::unique_ptr<uint8_t[]> fKey;
301     sk_sp<CachedTessellations> fTessellations;
302 };
303 
304 /**
305  * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
306  * vertices and a translation vector. If the CachedTessellations does not contain a suitable
307  * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
308  * to the caller. The caller will update it and reinsert it back into the cache.
309  */
310 template <typename FACTORY>
311 struct FindContext {
FindContext__anonb977c80b0111::FindContext312     FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
313             : fViewMatrix(viewMatrix), fFactory(factory) {}
314     const SkMatrix* const fViewMatrix;
315     // If this is valid after Find is called then we found the vertices and they should be drawn
316     // with fTranslate applied.
317     sk_sp<SkVertices> fVertices;
318     SkVector fTranslate = {0, 0};
319 
320     // If this is valid after Find then the caller should add the vertices to the tessellation set
321     // and create a new CachedTessellationsRec and insert it into SkResourceCache.
322     sk_sp<CachedTessellations> fTessellationsOnFailure;
323 
324     const FACTORY* fFactory;
325 };
326 
327 /**
328  * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
329  * the FindContext are used to determine if the vertices are reusable. If so the vertices and
330  * necessary translation vector are set on the FindContext.
331  */
332 template <typename FACTORY>
FindVisitor(const SkResourceCache::Rec & baseRec,void * ctx)333 bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
334     FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
335     const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
336     findContext->fVertices =
337             rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
338     if (findContext->fVertices) {
339         return true;
340     }
341     // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
342     // manipulated we will add a new Rec.
343     findContext->fTessellationsOnFailure = rec.refTessellations();
344     return false;
345 }
346 
347 class ShadowedPath {
348 public:
ShadowedPath(const SkPath * path,const SkMatrix * viewMatrix)349     ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
350             : fPath(path)
351             , fViewMatrix(viewMatrix)
352 #if SK_SUPPORT_GPU
353             , fShapeForKey(*path, GrStyle::SimpleFill())
354 #endif
355     {}
356 
path() const357     const SkPath& path() const { return *fPath; }
viewMatrix() const358     const SkMatrix& viewMatrix() const { return *fViewMatrix; }
359 #if SK_SUPPORT_GPU
360     /** Negative means the vertices should not be cached for this path. */
keyBytes() const361     int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
writeKey(void * key) const362     void writeKey(void* key) const {
363         fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
364     }
isRRect(SkRRect * rrect)365     bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
366 #else
keyBytes() const367     int keyBytes() const { return -1; }
writeKey(void * key) const368     void writeKey(void* key) const { SK_ABORT("Should never be called"); }
isRRect(SkRRect * rrect)369     bool isRRect(SkRRect* rrect) { return false; }
370 #endif
371 
372 private:
373     const SkPath* fPath;
374     const SkMatrix* fViewMatrix;
375 #if SK_SUPPORT_GPU
376     GrShape fShapeForKey;
377 #endif
378 };
379 
380 // This creates a domain of keys in SkResourceCache used by this file.
381 static void* kNamespace;
382 
383 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
384 class ShadowInvalidator : public SkPathRef::GenIDChangeListener {
385 public:
ShadowInvalidator(const SkResourceCache::Key & key)386     ShadowInvalidator(const SkResourceCache::Key& key) {
387         fKey.reset(new uint8_t[key.size()]);
388         memcpy(fKey.get(), &key, key.size());
389     }
390 
391 private:
getKey() const392     const SkResourceCache::Key& getKey() const {
393         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
394     }
395 
396     // always purge
FindVisitor(const SkResourceCache::Rec &,void *)397     static bool FindVisitor(const SkResourceCache::Rec&, void*) {
398         return false;
399     }
400 
onChange()401     void onChange() override {
402         SkResourceCache::Find(this->getKey(), ShadowInvalidator::FindVisitor, nullptr);
403     }
404 
405     std::unique_ptr<uint8_t[]> fKey;
406 };
407 
408 /**
409  * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
410  * they are first found in SkResourceCache.
411  */
412 template <typename FACTORY>
draw_shadow(const FACTORY & factory,std::function<void (const SkVertices *,SkBlendMode,const SkPaint &,SkScalar tx,SkScalar ty,bool)> drawProc,ShadowedPath & path,SkColor color)413 bool draw_shadow(const FACTORY& factory,
414                  std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
415                  SkScalar tx, SkScalar ty, bool)> drawProc, ShadowedPath& path, SkColor color) {
416     FindContext<FACTORY> context(&path.viewMatrix(), &factory);
417 
418     SkResourceCache::Key* key = nullptr;
419     SkAutoSTArray<32 * 4, uint8_t> keyStorage;
420     int keyDataBytes = path.keyBytes();
421     if (keyDataBytes >= 0) {
422         keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
423         key = new (keyStorage.begin()) SkResourceCache::Key();
424         path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
425         key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
426         SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
427     }
428 
429     sk_sp<SkVertices> vertices;
430     bool foundInCache = SkToBool(context.fVertices);
431     if (foundInCache) {
432         vertices = std::move(context.fVertices);
433     } else {
434         // TODO: handle transforming the path as part of the tessellator
435         if (key) {
436             // Update or initialize a tessellation set and add it to the cache.
437             sk_sp<CachedTessellations> tessellations;
438             if (context.fTessellationsOnFailure) {
439                 tessellations = std::move(context.fTessellationsOnFailure);
440             } else {
441                 tessellations.reset(new CachedTessellations());
442             }
443             vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
444                                           &context.fTranslate);
445             if (!vertices) {
446                 return false;
447             }
448             auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
449             SkPathPriv::AddGenIDChangeListener(path.path(), sk_make_sp<ShadowInvalidator>(*key));
450             SkResourceCache::Add(rec);
451         } else {
452             vertices = factory.makeVertices(path.path(), path.viewMatrix(),
453                                             &context.fTranslate);
454             if (!vertices) {
455                 return false;
456             }
457         }
458     }
459 
460     SkPaint paint;
461     // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
462     // that against our 'color' param.
463     paint.setColorFilter(
464          SkColorFilters::Blend(color, SkBlendMode::kModulate)->makeComposed(
465                                                                 SkGaussianColorFilter::Make()));
466 
467     drawProc(vertices.get(), SkBlendMode::kModulate, paint,
468              context.fTranslate.fX, context.fTranslate.fY, path.viewMatrix().hasPerspective());
469 
470     return true;
471 }
472 }
473 
tilted(const SkPoint3 & zPlaneParams)474 static bool tilted(const SkPoint3& zPlaneParams) {
475     return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
476 }
477 
map(const SkMatrix & m,const SkPoint3 & pt)478 static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
479     SkPoint3 result;
480     m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
481     result.fZ = pt.fZ;
482     return result;
483 }
484 
ComputeTonalColors(SkColor inAmbientColor,SkColor inSpotColor,SkColor * outAmbientColor,SkColor * outSpotColor)485 void SkShadowUtils::ComputeTonalColors(SkColor inAmbientColor, SkColor inSpotColor,
486                                        SkColor* outAmbientColor, SkColor* outSpotColor) {
487     // For tonal color we only compute color values for the spot shadow.
488     // The ambient shadow is greyscale only.
489 
490     // Ambient
491     *outAmbientColor = SkColorSetARGB(SkColorGetA(inAmbientColor), 0, 0, 0);
492 
493     // Spot
494     int spotR = SkColorGetR(inSpotColor);
495     int spotG = SkColorGetG(inSpotColor);
496     int spotB = SkColorGetB(inSpotColor);
497     int max = SkTMax(SkTMax(spotR, spotG), spotB);
498     int min = SkTMin(SkTMin(spotR, spotG), spotB);
499     SkScalar luminance = 0.5f*(max + min)/255.f;
500     SkScalar origA = SkColorGetA(inSpotColor)/255.f;
501 
502     // We compute a color alpha value based on the luminance of the color, scaled by an
503     // adjusted alpha value. We want the following properties to match the UX examples
504     // (assuming a = 0.25) and to ensure that we have reasonable results when the color
505     // is black and/or the alpha is 0:
506     //     f(0, a) = 0
507     //     f(luminance, 0) = 0
508     //     f(1, 0.25) = .5
509     //     f(0.5, 0.25) = .4
510     //     f(1, 1) = 1
511     // The following functions match this as closely as possible.
512     SkScalar alphaAdjust = (2.6f + (-2.66667f + 1.06667f*origA)*origA)*origA;
513     SkScalar colorAlpha = (3.544762f + (-4.891428f + 2.3466f*luminance)*luminance)*luminance;
514     colorAlpha = SkTPin(alphaAdjust*colorAlpha, 0.0f, 1.0f);
515 
516     // Similarly, we set the greyscale alpha based on luminance and alpha so that
517     //     f(0, a) = a
518     //     f(luminance, 0) = 0
519     //     f(1, 0.25) = 0.15
520     SkScalar greyscaleAlpha = SkTPin(origA*(1 - 0.4f*luminance), 0.0f, 1.0f);
521 
522     // The final color we want to emulate is generated by rendering a color shadow (C_rgb) using an
523     // alpha computed from the color's luminance (C_a), and then a black shadow with alpha (S_a)
524     // which is an adjusted value of 'a'.  Assuming SrcOver, a background color of B_rgb, and
525     // ignoring edge falloff, this becomes
526     //
527     //      (C_a - S_a*C_a)*C_rgb + (1 - (S_a + C_a - S_a*C_a))*B_rgb
528     //
529     // Assuming premultiplied alpha, this means we scale the color by (C_a - S_a*C_a) and
530     // set the alpha to (S_a + C_a - S_a*C_a).
531     SkScalar colorScale = colorAlpha*(SK_Scalar1 - greyscaleAlpha);
532     SkScalar tonalAlpha = colorScale + greyscaleAlpha;
533     SkScalar unPremulScale = colorScale / tonalAlpha;
534     *outSpotColor = SkColorSetARGB(tonalAlpha*255.999f,
535                                    unPremulScale*spotR,
536                                    unPremulScale*spotG,
537                                    unPremulScale*spotB);
538 }
539 
540 // Draw an offset spot shadow and outlining ambient shadow for the given path.
DrawShadow(SkCanvas * canvas,const SkPath & path,const SkPoint3 & zPlaneParams,const SkPoint3 & devLightPos,SkScalar lightRadius,SkColor ambientColor,SkColor spotColor,uint32_t flags)541 void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
542                                const SkPoint3& devLightPos, SkScalar lightRadius,
543                                SkColor ambientColor, SkColor spotColor,
544                                uint32_t flags) {
545     SkMatrix inverse;
546     if (!canvas->getTotalMatrix().invert(&inverse)) {
547         return;
548     }
549     SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
550 
551     SkDrawShadowRec rec;
552     rec.fZPlaneParams   = zPlaneParams;
553     rec.fLightPos       = { pt.fX, pt.fY, devLightPos.fZ };
554     rec.fLightRadius    = lightRadius;
555     rec.fAmbientColor   = ambientColor;
556     rec.fSpotColor      = spotColor;
557     rec.fFlags          = flags;
558 
559     canvas->private_draw_shadow_rec(path, rec);
560 }
561 
validate_rec(const SkDrawShadowRec & rec)562 static bool validate_rec(const SkDrawShadowRec& rec) {
563     return rec.fLightPos.isFinite() && rec.fZPlaneParams.isFinite() &&
564            SkScalarIsFinite(rec.fLightRadius);
565 }
566 
drawShadow(const SkPath & path,const SkDrawShadowRec & rec)567 void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
568     auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
569                                 SkScalar tx, SkScalar ty, bool hasPerspective) {
570         if (vertices->vertexCount()) {
571             // For perspective shadows we've already computed the shadow in world space,
572             // and we can't translate it without changing it. Otherwise we concat the
573             // change in translation from the cached version.
574             SkAutoDeviceCTMRestore adr(
575                 this,
576                 hasPerspective ? SkMatrix::I()
577                                : SkMatrix::Concat(this->ctm(), SkMatrix::MakeTrans(tx, ty)));
578             this->drawVertices(vertices, nullptr, 0, mode, paint);
579         }
580     };
581 
582     if (!validate_rec(rec)) {
583         return;
584     }
585 
586     SkMatrix viewMatrix = this->ctm();
587     SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
588 
589     ShadowedPath shadowedPath(&path, &viewMatrix);
590 
591     bool tiltZPlane = tilted(rec.fZPlaneParams);
592     bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
593     bool uncached = tiltZPlane || path.isVolatile();
594 
595     SkPoint3 zPlaneParams = rec.fZPlaneParams;
596     SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
597     float lightRadius = rec.fLightRadius;
598 
599     if (SkColorGetA(rec.fAmbientColor) > 0) {
600         bool success = false;
601         if (uncached) {
602             sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
603                                                                           zPlaneParams,
604                                                                           transparent);
605             if (vertices) {
606                 SkPaint paint;
607                 // Run the vertex color through a GaussianColorFilter and then modulate the
608                 // grayscale result of that against our 'color' param.
609                 paint.setColorFilter(
610                     SkColorFilters::Blend(rec.fAmbientColor,
611                                                   SkBlendMode::kModulate)->makeComposed(
612                                                                    SkGaussianColorFilter::Make()));
613                 this->drawVertices(vertices.get(), nullptr, 0, SkBlendMode::kModulate, paint);
614                 success = true;
615             }
616         }
617 
618         if (!success) {
619             AmbientVerticesFactory factory;
620             factory.fOccluderHeight = zPlaneParams.fZ;
621             factory.fTransparent = transparent;
622             if (viewMatrix.hasPerspective()) {
623                 factory.fOffset.set(0, 0);
624             } else {
625                 factory.fOffset.fX = viewMatrix.getTranslateX();
626                 factory.fOffset.fY = viewMatrix.getTranslateY();
627             }
628 
629             if (!draw_shadow(factory, drawVertsProc, shadowedPath, rec.fAmbientColor)) {
630                 // Pretransform the path to avoid transforming the stroke, below.
631                 SkPath devSpacePath;
632                 path.transform(viewMatrix, &devSpacePath);
633                 devSpacePath.setIsVolatile(true);
634 
635                 // The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
636                 // the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
637                 //
638                 // We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
639                 // can be calculated by interpolating:
640                 //
641                 //            original edge        outer edge
642                 //         |       |<---------- r ------>|
643                 //         |<------|--- f -------------->|
644                 //         |       |                     |
645                 //    alpha = 1  alpha = a          alpha = 0
646                 //
647                 // Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
648                 //
649                 // We now need to outset the path to place the new edge in the center of the
650                 // blur region:
651                 //
652                 //             original   new
653                 //         |       |<------|--- r ------>|
654                 //         |<------|--- f -|------------>|
655                 //         |       |<- o ->|<--- f/2 --->|
656                 //
657                 //     r = o + f/2, so o = r - f/2
658                 //
659                 // We outset by using the stroker, so the strokeWidth is o/2.
660                 //
661                 SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
662                 SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
663                 SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
664                 SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
665 
666                 // Now draw with blur
667                 SkPaint paint;
668                 paint.setColor(rec.fAmbientColor);
669                 paint.setStrokeWidth(strokeWidth);
670                 paint.setStyle(SkPaint::kStrokeAndFill_Style);
671                 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
672                 bool respectCTM = false;
673                 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
674                 this->drawPath(devSpacePath, paint);
675             }
676         }
677     }
678 
679     if (SkColorGetA(rec.fSpotColor) > 0) {
680         bool success = false;
681         if (uncached) {
682             sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
683                                                                        zPlaneParams,
684                                                                        devLightPos, lightRadius,
685                                                                        transparent);
686             if (vertices) {
687                 SkPaint paint;
688                 // Run the vertex color through a GaussianColorFilter and then modulate the
689                 // grayscale result of that against our 'color' param.
690                 paint.setColorFilter(
691                     SkColorFilters::Blend(rec.fSpotColor,
692                                                   SkBlendMode::kModulate)->makeComposed(
693                                                       SkGaussianColorFilter::Make()));
694                 this->drawVertices(vertices.get(), nullptr, 0, SkBlendMode::kModulate, paint);
695                 success = true;
696             }
697         }
698 
699         if (!success) {
700             SpotVerticesFactory factory;
701             factory.fOccluderHeight = zPlaneParams.fZ;
702             factory.fDevLightPos = devLightPos;
703             factory.fLightRadius = lightRadius;
704 
705             SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
706             factory.fLocalCenter = center;
707             viewMatrix.mapPoints(&center, 1);
708             SkScalar radius, scale;
709             SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
710                                                devLightPos.fY - center.fY, devLightPos.fZ,
711                                                lightRadius, &radius, &scale, &factory.fOffset);
712             SkRect devBounds;
713             viewMatrix.mapRect(&devBounds, path.getBounds());
714             if (transparent ||
715                 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
716                 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
717                 // if the translation of the shadow is big enough we're going to end up
718                 // filling the entire umbra, so we can treat these as all the same
719                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
720             } else if (factory.fOffset.length()*scale + scale < radius) {
721                 // if we don't translate more than the blur distance, can assume umbra is covered
722                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
723             } else if (path.isConvex()) {
724                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
725             } else {
726                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
727             }
728             // need to add this after we classify the shadow
729             factory.fOffset.fX += viewMatrix.getTranslateX();
730             factory.fOffset.fY += viewMatrix.getTranslateY();
731 
732             SkColor color = rec.fSpotColor;
733 #ifdef DEBUG_SHADOW_CHECKS
734             switch (factory.fOccluderType) {
735                 case SpotVerticesFactory::OccluderType::kTransparent:
736                     color = 0xFFD2B48C;  // tan for transparent
737                     break;
738                 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
739                     color = 0xFFFFA500;   // orange for opaque
740                     break;
741                 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
742                     color = 0xFFE5E500;  // corn yellow for covered
743                     break;
744             }
745 #endif
746             if (!draw_shadow(factory, drawVertsProc, shadowedPath, color)) {
747                 // draw with blur
748                 SkMatrix shadowMatrix;
749                 if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
750                                                                  viewMatrix, zPlaneParams,
751                                                                  path.getBounds(),
752                                                                  &shadowMatrix, &radius)) {
753                     return;
754                 }
755                 SkAutoDeviceCTMRestore adr(this, shadowMatrix);
756 
757                 SkPaint paint;
758                 paint.setColor(rec.fSpotColor);
759                 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
760                 bool respectCTM = false;
761                 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
762                 this->drawPath(path, paint);
763             }
764         }
765     }
766 }
767