SkShadowUtils.cpp revision ab244f045a0740fa6106ed21a4e5824cd09f84f3
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 "SkShadowUtils.h"
9#include "SkCanvas.h"
10#include "SkColorFilter.h"
11#include "SkColorPriv.h"
12#include "SkPath.h"
13#include "SkRandom.h"
14#include "SkResourceCache.h"
15#include "SkShadowTessellator.h"
16#include "SkString.h"
17#include "SkTLazy.h"
18#include "SkVertices.h"
19#if SK_SUPPORT_GPU
20#include "GrShape.h"
21#include "effects/GrBlurredEdgeFragmentProcessor.h"
22#endif
23#include "../../src/effects/shadows/SkAmbientShadowMaskFilter.h"
24#include "../../src/effects/shadows/SkSpotShadowMaskFilter.h"
25
26/**
27*  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
28*                           then blends with the color's G value.
29*                           Final result is black with alpha of Gaussian(B)*G.
30*                           The assumption is that the original color's alpha is 1.
31*/
32class SK_API SkGaussianColorFilter : public SkColorFilter {
33public:
34    static sk_sp<SkColorFilter> Make() {
35        return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
36    }
37
38    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
39
40#if SK_SUPPORT_GPU
41    sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override;
42#endif
43
44    SK_TO_STRING_OVERRIDE()
45    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianColorFilter)
46
47protected:
48    void flatten(SkWriteBuffer&) const override {}
49
50private:
51    SkGaussianColorFilter() : INHERITED() {}
52
53    typedef SkColorFilter INHERITED;
54};
55
56static void build_table() {
57    SkDebugf("const uint16_t gByteExpU16Table[256] = {");
58    for (int i = 0; i <= 255; ++i) {
59        if (!(i % 8)) {
60            SkDebugf("\n");
61        }
62        SkScalar factor = SK_Scalar1 - i / 255.f;
63        factor = SkScalarExp(-factor * factor * 4) - 0.018f;
64        int v = (int)(factor * 65536);
65        SkDebugf(" 0x%04X,", v);
66    }
67    SkDebugf("\n};\n");
68}
69
70const uint16_t gByteExpU16Table[256] = {
71    0x0014, 0x003A, 0x0062, 0x008A, 0x00B3, 0x00DE, 0x010A, 0x0136,
72    0x0165, 0x0194, 0x01C4, 0x01F6, 0x0229, 0x025E, 0x0294, 0x02CB,
73    0x0304, 0x033E, 0x0379, 0x03B7, 0x03F5, 0x0435, 0x0477, 0x04BB,
74    0x0500, 0x0546, 0x058F, 0x05D9, 0x0625, 0x0673, 0x06C3, 0x0714,
75    0x0768, 0x07BD, 0x0814, 0x086E, 0x08C9, 0x0926, 0x0986, 0x09E8,
76    0x0A4B, 0x0AB1, 0x0B1A, 0x0B84, 0x0BF1, 0x0C60, 0x0CD2, 0x0D46,
77    0x0DBC, 0x0E35, 0x0EB0, 0x0F2E, 0x0FAF, 0x1032, 0x10B7, 0x1140,
78    0x11CB, 0x1258, 0x12E9, 0x137C, 0x1412, 0x14AB, 0x1547, 0x15E6,
79    0x1688, 0x172D, 0x17D5, 0x187F, 0x192D, 0x19DE, 0x1A92, 0x1B4A,
80    0x1C04, 0x1CC2, 0x1D83, 0x1E47, 0x1F0E, 0x1FD9, 0x20A7, 0x2178,
81    0x224D, 0x2325, 0x2401, 0x24E0, 0x25C2, 0x26A8, 0x2792, 0x287F,
82    0x296F, 0x2A63, 0x2B5A, 0x2C56, 0x2D54, 0x2E56, 0x2F5C, 0x3065,
83    0x3172, 0x3283, 0x3397, 0x34AE, 0x35CA, 0x36E9, 0x380B, 0x3931,
84    0x3A5B, 0x3B88, 0x3CB9, 0x3DED, 0x3F25, 0x4061, 0x41A0, 0x42E2,
85    0x4428, 0x4572, 0x46BF, 0x480F, 0x4963, 0x4ABA, 0x4C14, 0x4D72,
86    0x4ED3, 0x5038, 0x519F, 0x530A, 0x5478, 0x55E9, 0x575D, 0x58D4,
87    0x5A4F, 0x5BCC, 0x5D4C, 0x5ECF, 0x6054, 0x61DD, 0x6368, 0x64F6,
88    0x6686, 0x6819, 0x69AE, 0x6B45, 0x6CDF, 0x6E7B, 0x701A, 0x71BA,
89    0x735D, 0x7501, 0x76A7, 0x784F, 0x79F9, 0x7BA4, 0x7D51, 0x7F00,
90    0x80AF, 0x8260, 0x8413, 0x85C6, 0x877A, 0x8930, 0x8AE6, 0x8C9C,
91    0x8E54, 0x900C, 0x91C4, 0x937D, 0x9535, 0x96EE, 0x98A7, 0x9A60,
92    0x9C18, 0x9DD1, 0x9F88, 0xA13F, 0xA2F6, 0xA4AB, 0xA660, 0xA814,
93    0xA9C6, 0xAB78, 0xAD27, 0xAED6, 0xB082, 0xB22D, 0xB3D6, 0xB57D,
94    0xB722, 0xB8C5, 0xBA65, 0xBC03, 0xBD9E, 0xBF37, 0xC0CD, 0xC25F,
95    0xC3EF, 0xC57B, 0xC704, 0xC889, 0xCA0B, 0xCB89, 0xCD04, 0xCE7A,
96    0xCFEC, 0xD15A, 0xD2C4, 0xD429, 0xD58A, 0xD6E6, 0xD83D, 0xD990,
97    0xDADD, 0xDC25, 0xDD68, 0xDEA6, 0xDFDE, 0xE111, 0xE23E, 0xE365,
98    0xE486, 0xE5A2, 0xE6B7, 0xE7C6, 0xE8CF, 0xE9D1, 0xEACD, 0xEBC3,
99    0xECB2, 0xED9A, 0xEE7C, 0xEF56, 0xF02A, 0xF0F6, 0xF1BC, 0xF27A,
100    0xF332, 0xF3E1, 0xF48A, 0xF52B, 0xF5C5, 0xF657, 0xF6E1, 0xF764,
101    0xF7DF, 0xF852, 0xF8BE, 0xF922, 0xF97E, 0xF9D2, 0xFA1E, 0xFA62,
102    0xFA9F, 0xFAD3, 0xFAFF, 0xFB23, 0xFB40, 0xFB54, 0xFB60, 0xFB64,
103};
104
105void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
106    // to re-build the table, call build_table() which will dump it out using SkDebugf.
107    if (false) {
108        build_table();
109    }
110    for (int i = 0; i < count; ++i) {
111        SkPMColor c = src[i];
112        uint8_t a = gByteExpU16Table[SkGetPackedB32(c)] * SkGetPackedG32(c) >> 16;
113        dst[i] = SkPackARGB32(a, a, a, a);
114    }
115}
116
117sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
118    return Make();
119}
120
121#ifndef SK_IGNORE_TO_STRING
122void SkGaussianColorFilter::toString(SkString* str) const {
123    str->append("SkGaussianColorFilter ");
124}
125#endif
126
127#if SK_SUPPORT_GPU
128
129sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
130                                                                      SkColorSpace*) const {
131    return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
132}
133#endif
134
135///////////////////////////////////////////////////////////////////////////////////////////////////
136
137namespace {
138
139uint64_t resource_cache_shared_id() {
140    return 0x2020776f64616873llu;  // 'shadow  '
141}
142
143/** Factory for an ambient shadow mesh with particular shadow properties. */
144struct AmbientVerticesFactory {
145    SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
146    SkScalar fAmbientAlpha;
147    bool fTransparent;
148
149    bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
150        if (fOccluderHeight != that.fOccluderHeight || fAmbientAlpha != that.fAmbientAlpha ||
151            fTransparent != that.fTransparent) {
152            return false;
153        }
154        translate->set(0, 0);
155        return true;
156    }
157
158    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
159        SkScalar z = fOccluderHeight;
160        return SkShadowTessellator::MakeAmbient(path, ctm,
161                                                [z](SkScalar, SkScalar) { return z; },
162                                                fAmbientAlpha, fTransparent);
163    }
164};
165
166/** Factory for an spot shadow mesh with particular shadow properties. */
167struct SpotVerticesFactory {
168    enum class OccluderType {
169        // The umbra cannot be dropped out because the occluder is not opaque.
170        kTransparent,
171        // The umbra can be dropped where it is occluded.
172        kOpaque,
173        // It is known that the entire umbra is occluded.
174        kOpaqueCoversUmbra
175    };
176
177    SkVector fOffset;
178    SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
179    SkPoint3 fDevLightPos;
180    SkScalar fLightRadius;
181    SkScalar fSpotAlpha;
182    OccluderType fOccluderType;
183
184    bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
185        if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
186            fLightRadius != that.fLightRadius || fSpotAlpha != that.fSpotAlpha ||
187            fOccluderType != that.fOccluderType) {
188            return false;
189        }
190        switch (fOccluderType) {
191            case OccluderType::kTransparent:
192            case OccluderType::kOpaqueCoversUmbra:
193                // 'this' and 'that' will either both have no umbra removed or both have all the
194                // umbra removed.
195                *translate = that.fOffset - fOffset;
196                return true;
197            case OccluderType::kOpaque:
198                // In this case we partially remove the umbra differently for 'this' and 'that'
199                // if the offsets don't match.
200                if (fOffset == that.fOffset) {
201                    translate->set(0, 0);
202                    return true;
203                }
204                return false;
205        }
206        SkFAIL("Uninitialized occluder type?");
207        return false;
208    }
209
210    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
211        bool transparent = OccluderType::kTransparent == fOccluderType;
212        SkScalar z = fOccluderHeight;
213        return SkShadowTessellator::MakeSpot(path, ctm,
214                                             [z](SkScalar, SkScalar) -> SkScalar { return z; },
215                                             fDevLightPos, fLightRadius,
216                                             fSpotAlpha, transparent);
217    }
218};
219
220/**
221 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
222 * records are immutable this is not itself a Rec. When we need to update it we return this on
223 * the FindVisitor and let the cache destory the Rec. We'll update the tessellations and then add
224 * a new Rec with an adjusted size for any deletions/additions.
225 */
226class CachedTessellations : public SkRefCnt {
227public:
228    size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
229
230    sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
231                           SkVector* translate) const {
232        return fAmbientSet.find(ambient, matrix, translate);
233    }
234
235    sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
236                          const SkMatrix& matrix) {
237        return fAmbientSet.add(devPath, ambient, matrix);
238    }
239
240    sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
241                           SkVector* translate) const {
242        return fSpotSet.find(spot, matrix, translate);
243    }
244
245    sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
246                          const SkMatrix& matrix) {
247        return fSpotSet.add(devPath, spot, matrix);
248    }
249
250private:
251    template <typename FACTORY, int MAX_ENTRIES>
252    class Set {
253    public:
254        size_t size() const { return fSize; }
255
256        sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
257                               SkVector* translate) const {
258            for (int i = 0; i < MAX_ENTRIES; ++i) {
259                if (fEntries[i].fFactory.isCompatible(factory, translate)) {
260                    const SkMatrix& m = fEntries[i].fMatrix;
261                    if (matrix.hasPerspective() || m.hasPerspective()) {
262                        if (matrix != fEntries[i].fMatrix) {
263                            continue;
264                        }
265                    } else if (matrix.getScaleX() != m.getScaleX() ||
266                               matrix.getSkewX() != m.getSkewX() ||
267                               matrix.getScaleY() != m.getScaleY() ||
268                               matrix.getSkewY() != m.getSkewY()) {
269                        continue;
270                    }
271                    *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(),
272                                           matrix.getTranslateY() - m.getTranslateY()};
273                    return fEntries[i].fVertices;
274                }
275            }
276            return nullptr;
277        }
278
279        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) {
280            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix);
281            if (!vertices) {
282                return nullptr;
283            }
284            int i;
285            if (fCount < MAX_ENTRIES) {
286                i = fCount++;
287            } else {
288                i = gRandom.nextULessThan(MAX_ENTRIES);
289                fSize -= fEntries[i].fVertices->approximateSize();
290            }
291            fEntries[i].fFactory = factory;
292            fEntries[i].fVertices = vertices;
293            fEntries[i].fMatrix = matrix;
294            fSize += vertices->approximateSize();
295            return vertices;
296        }
297
298    private:
299        struct Entry {
300            FACTORY fFactory;
301            sk_sp<SkVertices> fVertices;
302            SkMatrix fMatrix;
303        };
304        Entry fEntries[MAX_ENTRIES];
305        int fCount = 0;
306        size_t fSize = 0;
307    };
308
309    Set<AmbientVerticesFactory, 4> fAmbientSet;
310    Set<SpotVerticesFactory, 4> fSpotSet;
311
312    static SkRandom gRandom;
313};
314
315SkRandom CachedTessellations::gRandom;
316
317/**
318 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
319 * path. The key represents the path's geometry and not any shadow params.
320 */
321class CachedTessellationsRec : public SkResourceCache::Rec {
322public:
323    CachedTessellationsRec(const SkResourceCache::Key& key,
324                           sk_sp<CachedTessellations> tessellations)
325            : fTessellations(std::move(tessellations)) {
326        fKey.reset(new uint8_t[key.size()]);
327        memcpy(fKey.get(), &key, key.size());
328    }
329
330    const Key& getKey() const override {
331        return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
332    }
333
334    size_t bytesUsed() const override { return fTessellations->size(); }
335
336    const char* getCategory() const override { return "tessellated shadow masks"; }
337
338    sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
339
340    template <typename FACTORY>
341    sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
342                           SkVector* translate) const {
343        return fTessellations->find(factory, matrix, translate);
344    }
345
346private:
347    std::unique_ptr<uint8_t[]> fKey;
348    sk_sp<CachedTessellations> fTessellations;
349};
350
351/**
352 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
353 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
354 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
355 * to the caller. The caller will update it and reinsert it back into the cache.
356 */
357template <typename FACTORY>
358struct FindContext {
359    FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
360            : fViewMatrix(viewMatrix), fFactory(factory) {}
361    const SkMatrix* const fViewMatrix;
362    // If this is valid after Find is called then we found the vertices and they should be drawn
363    // with fTranslate applied.
364    sk_sp<SkVertices> fVertices;
365    SkVector fTranslate = {0, 0};
366
367    // If this is valid after Find then the caller should add the vertices to the tessellation set
368    // and create a new CachedTessellationsRec and insert it into SkResourceCache.
369    sk_sp<CachedTessellations> fTessellationsOnFailure;
370
371    const FACTORY* fFactory;
372};
373
374/**
375 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
376 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
377 * necessary translation vector are set on the FindContext.
378 */
379template <typename FACTORY>
380bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
381    FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
382    const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
383    findContext->fVertices =
384            rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
385    if (findContext->fVertices) {
386        return true;
387    }
388    // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
389    // manipulated we will add a new Rec.
390    findContext->fTessellationsOnFailure = rec.refTessellations();
391    return false;
392}
393
394class ShadowedPath {
395public:
396    ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
397            : fPath(path)
398            , fViewMatrix(viewMatrix)
399#if SK_SUPPORT_GPU
400            , fShapeForKey(*path, GrStyle::SimpleFill())
401#endif
402    {}
403
404    const SkPath& path() const { return *fPath; }
405    const SkMatrix& viewMatrix() const { return *fViewMatrix; }
406#if SK_SUPPORT_GPU
407    /** Negative means the vertices should not be cached for this path. */
408    int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
409    void writeKey(void* key) const {
410        fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
411    }
412    bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
413#else
414    int keyBytes() const { return -1; }
415    void writeKey(void* key) const { SkFAIL("Should never be called"); }
416    bool isRRect(SkRRect* rrect) { return false; }
417#endif
418
419private:
420    const SkPath* fPath;
421    const SkMatrix* fViewMatrix;
422#if SK_SUPPORT_GPU
423    GrShape fShapeForKey;
424#endif
425};
426
427// This creates a domain of keys in SkResourceCache used by this file.
428static void* kNamespace;
429
430/**
431 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
432 * they are first found in SkResourceCache.
433 */
434template <typename FACTORY>
435void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color,
436                 SkResourceCache* cache) {
437    FindContext<FACTORY> context(&path.viewMatrix(), &factory);
438
439    SkResourceCache::Key* key = nullptr;
440    SkAutoSTArray<32 * 4, uint8_t> keyStorage;
441    int keyDataBytes = path.keyBytes();
442    if (keyDataBytes >= 0) {
443        keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
444        key = new (keyStorage.begin()) SkResourceCache::Key();
445        path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
446        key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
447        if (cache) {
448            cache->find(*key, FindVisitor<FACTORY>, &context);
449        } else {
450            SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
451        }
452    }
453
454    sk_sp<SkVertices> vertices;
455    const SkVector* translate;
456    static constexpr SkVector kZeroTranslate = {0, 0};
457    bool foundInCache = SkToBool(context.fVertices);
458    if (foundInCache) {
459        vertices = std::move(context.fVertices);
460        translate = &context.fTranslate;
461    } else {
462        // TODO: handle transforming the path as part of the tessellator
463        if (key) {
464            // Update or initialize a tessellation set and add it to the cache.
465            sk_sp<CachedTessellations> tessellations;
466            if (context.fTessellationsOnFailure) {
467                tessellations = std::move(context.fTessellationsOnFailure);
468            } else {
469                tessellations.reset(new CachedTessellations());
470            }
471            vertices = tessellations->add(path.path(), factory, path.viewMatrix());
472            if (!vertices) {
473                return;
474            }
475            auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
476            if (cache) {
477                cache->add(rec);
478            } else {
479                SkResourceCache::Add(rec);
480            }
481        } else {
482            vertices = factory.makeVertices(path.path(), path.viewMatrix());
483            if (!vertices) {
484                return;
485            }
486        }
487        translate = &kZeroTranslate;
488    }
489
490    SkPaint paint;
491    // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
492    // that against our 'color' param.
493    paint.setColorFilter(SkColorFilter::MakeComposeFilter(
494            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
495            SkGaussianColorFilter::Make()));
496    if (translate->fX || translate->fY) {
497        canvas->save();
498        canvas->translate(translate->fX, translate->fY);
499    }
500    canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
501    if (translate->fX || translate->fY) {
502        canvas->restore();
503    }
504}
505}
506
507static bool draw_analytic_shadows(SkCanvas* canvas, const SkPath& path, SkScalar occluderZ,
508                                  const SkPoint3& devLightPos, SkScalar lightRadius,
509                                  SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
510                                  uint32_t flags) {
511    // only supported in GPU code
512    if (!canvas->getGrContext()) {
513        return false;
514    }
515
516    SkRect rect;
517    SkRRect rrect;
518    if (canvas->getTotalMatrix().isSimilarity()) {
519        if (path.isRect(&rect)) {
520            SkPaint newPaint;
521            newPaint.setColor(color);
522            if (ambientAlpha > 0) {
523                newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
524                                                                       ambientAlpha, flags));
525                canvas->drawRect(rect, newPaint);
526            }
527            if (spotAlpha > 0) {
528                newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
529                                                                    lightRadius, spotAlpha,
530                                                                    flags));
531                canvas->drawRect(rect, newPaint);
532            }
533            return true;
534        } else if (path.isRRect(&rrect) && rrect.isSimpleCircular() &&
535                   rrect.radii(SkRRect::kUpperLeft_Corner).fX > SK_ScalarNearlyZero) {
536            SkPaint newPaint;
537            newPaint.setColor(color);
538            if (ambientAlpha > 0) {
539                newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
540                                                                       ambientAlpha, flags));
541                canvas->drawRRect(rrect, newPaint);
542            }
543            if (spotAlpha > 0) {
544                newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
545                                                                    lightRadius, spotAlpha,
546                                                                    flags));
547                canvas->drawRRect(rrect, newPaint);
548            }
549            return true;
550        } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) &&
551                   rect.width() > SK_ScalarNearlyZero) {
552            SkPaint newPaint;
553            newPaint.setColor(color);
554            if (ambientAlpha > 0) {
555                newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
556                                                                       ambientAlpha, flags));
557                canvas->drawOval(rect, newPaint);
558            }
559            if (spotAlpha > 0) {
560                newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
561                                                                    lightRadius, spotAlpha,
562                                                                    flags));
563                canvas->drawOval(rect, newPaint);
564            }
565            return true;
566        }
567    }
568
569    return false;
570}
571
572// Draw an offset spot shadow and outlining ambient shadow for the given path.
573void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
574                               const SkPoint3& devLightPos, SkScalar lightRadius,
575                               SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
576                               uint32_t flags, SkResourceCache* cache) {
577    // try fast paths
578    if (draw_analytic_shadows(canvas, path, occluderHeight, devLightPos, lightRadius,
579                              ambientAlpha, spotAlpha, color, flags)) {
580        return;
581    }
582
583    SkAutoCanvasRestore acr(canvas, true);
584    SkMatrix viewMatrix = canvas->getTotalMatrix();
585    canvas->resetMatrix();
586
587    ShadowedPath shadowedPath(&path, &viewMatrix);
588
589    bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
590
591    if (ambientAlpha > 0) {
592        ambientAlpha = SkTMin(ambientAlpha, 1.f);
593        AmbientVerticesFactory factory;
594        factory.fOccluderHeight = occluderHeight;
595        factory.fAmbientAlpha = ambientAlpha;
596        factory.fTransparent = transparent;
597
598        draw_shadow(factory, canvas, shadowedPath, color, cache);
599    }
600
601    if (spotAlpha > 0) {
602        spotAlpha = SkTMin(spotAlpha, 1.f);
603        SpotVerticesFactory factory;
604        float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
605        SkScalar radius = lightRadius * zRatio;
606
607        // Compute the scale and translation for the spot shadow.
608        SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
609
610        SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
611        viewMatrix.mapPoints(&center, 1);
612        factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
613                                         zRatio * (center.fY - devLightPos.fY));
614        factory.fOccluderHeight = occluderHeight;
615        factory.fDevLightPos = devLightPos;
616        factory.fLightRadius = lightRadius;
617        factory.fSpotAlpha = spotAlpha;
618
619        SkRRect rrect;
620        if (transparent) {
621            factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
622        } else {
623            factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque;
624            if (shadowedPath.isRRect(&rrect)) {
625                SkRRect devRRect;
626                if (rrect.transform(viewMatrix, &devRRect)) {
627                    SkScalar s = 1.f - scale;
628                    SkScalar w = devRRect.width();
629                    SkScalar h = devRRect.height();
630                    SkScalar hw = w / 2.f;
631                    SkScalar hh = h / 2.f;
632                    SkScalar umbraInsetX = s * hw + radius;
633                    SkScalar umbraInsetY = s * hh + radius;
634                    // The umbra is inset by radius along the diagonal, so adjust for that.
635                    SkScalar d = 1.f / SkScalarSqrt(hw * hw + hh * hh);
636                    umbraInsetX *= hw * d;
637                    umbraInsetY *= hh * d;
638                    if (umbraInsetX > hw || umbraInsetY > hh) {
639                        // There is no umbra to occlude.
640                        factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
641                    } else if (fabsf(factory.fOffset.fX) < umbraInsetX &&
642                               fabsf(factory.fOffset.fY) < umbraInsetY) {
643                        factory.fOccluderType =
644                                SpotVerticesFactory::OccluderType::kOpaqueCoversUmbra;
645                    } else if (factory.fOffset.fX > w - umbraInsetX ||
646                               factory.fOffset.fY > h - umbraInsetY) {
647                        // There umbra is fully exposed, there is nothing to omit.
648                        factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
649                    }
650                }
651            }
652        }
653        if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) {
654            factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
655        }
656        draw_shadow(factory, canvas, shadowedPath, color, cache);
657    }
658}
659
660// Draw an offset spot shadow and outlining ambient shadow for the given path,
661// without caching and using a function based on local position to compute the height.
662void SkShadowUtils::DrawUncachedShadow(SkCanvas* canvas, const SkPath& path,
663                                       std::function<SkScalar(SkScalar, SkScalar)> heightFunc,
664                                       const SkPoint3& lightPos, SkScalar lightRadius,
665                                       SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
666                                       uint32_t flags) {
667    // try fast paths
668    if (draw_analytic_shadows(canvas, path, heightFunc(0, 0), lightPos, lightRadius,
669                              ambientAlpha, spotAlpha, color, flags)) {
670        return;
671    }
672
673    SkAutoCanvasRestore acr(canvas, true);
674    SkMatrix viewMatrix = canvas->getTotalMatrix();
675    canvas->resetMatrix();
676
677    bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
678
679    if (ambientAlpha > 0) {
680        ambientAlpha = SkTMin(ambientAlpha, 1.f);
681        sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
682                                                                      heightFunc, ambientAlpha,
683                                                                      transparent);
684        SkPaint paint;
685        // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
686        // result of that against our 'color' param.
687        paint.setColorFilter(SkColorFilter::MakeComposeFilter(
688            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
689            SkGaussianColorFilter::Make()));
690        canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
691    }
692
693    if (spotAlpha > 0) {
694        spotAlpha = SkTMin(spotAlpha, 1.f);
695        sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, heightFunc,
696                                                                   lightPos, lightRadius,
697                                                                   spotAlpha, transparent);
698        SkPaint paint;
699        // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
700        // result of that against our 'color' param.
701        paint.setColorFilter(SkColorFilter::MakeComposeFilter(
702            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
703            SkGaussianColorFilter::Make()));
704        canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
705    }
706}
707