Home | History | Annotate | Download | only in utils
      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 "SkBlurMask.h"
     10 #include "SkCanvas.h"
     11 #include "SkColorFilter.h"
     12 #include "SkColorData.h"
     13 #include "SkDevice.h"
     14 #include "SkDrawShadowInfo.h"
     15 #include "SkMaskFilter.h"
     16 #include "SkPath.h"
     17 #include "SkPathPriv.h"
     18 #include "SkRandom.h"
     19 #include "SkRasterPipeline.h"
     20 #include "SkResourceCache.h"
     21 #include "SkShadowTessellator.h"
     22 #include "SkString.h"
     23 #include "SkTLazy.h"
     24 #include "SkVertices.h"
     25 #include <new>
     26 #if SK_SUPPORT_GPU
     27 #include "GrShape.h"
     28 #include "effects/GrBlurredEdgeFragmentProcessor.h"
     29 #endif
     30 
     31 /**
     32 *  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
     33 *                           then blends with the color's G value.
     34 *                           Final result is black with alpha of Gaussian(B)*G.
     35 *                           The assumption is that the original color's alpha is 1.
     36 */
     37 class SkGaussianColorFilter : public SkColorFilter {
     38 public:
     39     static sk_sp<SkColorFilter> Make() {
     40         return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
     41     }
     42 
     43 #if SK_SUPPORT_GPU
     44     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
     45             GrRecordingContext*, const GrColorSpaceInfo&) const override;
     46 #endif
     47 
     48 protected:
     49     void flatten(SkWriteBuffer&) const override {}
     50     void onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
     51                         bool shaderIsOpaque) const override {
     52         pipeline->append(SkRasterPipeline::gauss_a_to_rgba);
     53     }
     54 private:
     55     SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
     56 
     57     SkGaussianColorFilter() : INHERITED() {}
     58 
     59     typedef SkColorFilter INHERITED;
     60 };
     61 
     62 sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
     63     return Make();
     64 }
     65 
     66 #if SK_SUPPORT_GPU
     67 
     68 std::unique_ptr<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(
     69         GrRecordingContext*, const GrColorSpaceInfo&) const {
     70     return GrBlurredEdgeFragmentProcessor::Make(GrBlurredEdgeFragmentProcessor::Mode::kGaussian);
     71 }
     72 #endif
     73 
     74 ///////////////////////////////////////////////////////////////////////////////////////////////////
     75 
     76 namespace {
     77 
     78 uint64_t resource_cache_shared_id() {
     79     return 0x2020776f64616873llu;  // 'shadow  '
     80 }
     81 
     82 /** Factory for an ambient shadow mesh with particular shadow properties. */
     83 struct AmbientVerticesFactory {
     84     SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
     85     bool fTransparent;
     86     SkVector fOffset;
     87 
     88     bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
     89         if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
     90             return false;
     91         }
     92         *translate = that.fOffset;
     93         return true;
     94     }
     95 
     96     sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
     97                                    SkVector* translate) const {
     98         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
     99         // pick a canonical place to generate shadow
    100         SkMatrix noTrans(ctm);
    101         if (!ctm.hasPerspective()) {
    102             noTrans[SkMatrix::kMTransX] = 0;
    103             noTrans[SkMatrix::kMTransY] = 0;
    104         }
    105         *translate = fOffset;
    106         return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
    107     }
    108 };
    109 
    110 /** Factory for an spot shadow mesh with particular shadow properties. */
    111 struct SpotVerticesFactory {
    112     enum class OccluderType {
    113         // The umbra cannot be dropped out because either the occluder is not opaque,
    114         // or the center of the umbra is visible.
    115         kTransparent,
    116         // The umbra can be dropped where it is occluded.
    117         kOpaquePartialUmbra,
    118         // It is known that the entire umbra is occluded.
    119         kOpaqueNoUmbra
    120     };
    121 
    122     SkVector fOffset;
    123     SkPoint  fLocalCenter;
    124     SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
    125     SkPoint3 fDevLightPos;
    126     SkScalar fLightRadius;
    127     OccluderType fOccluderType;
    128 
    129     bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
    130         if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
    131             fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
    132             return false;
    133         }
    134         switch (fOccluderType) {
    135             case OccluderType::kTransparent:
    136             case OccluderType::kOpaqueNoUmbra:
    137                 // 'this' and 'that' will either both have no umbra removed or both have all the
    138                 // umbra removed.
    139                 *translate = that.fOffset;
    140                 return true;
    141             case OccluderType::kOpaquePartialUmbra:
    142                 // In this case we partially remove the umbra differently for 'this' and 'that'
    143                 // if the offsets don't match.
    144                 if (fOffset == that.fOffset) {
    145                     translate->set(0, 0);
    146                     return true;
    147                 }
    148                 return false;
    149         }
    150         SK_ABORT("Uninitialized occluder type?");
    151         return false;
    152     }
    153 
    154     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:
    185     size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
    186 
    187     sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
    188                            SkVector* translate) const {
    189         return fAmbientSet.find(ambient, matrix, translate);
    190     }
    191 
    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 
    197     sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
    198                            SkVector* translate) const {
    199         return fSpotSet.find(spot, matrix, translate);
    200     }
    201 
    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:
    211         size_t size() const { return fSize; }
    212 
    213         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 
    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:
    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 
    283     const Key& getKey() const override {
    284         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
    285     }
    286 
    287     size_t bytesUsed() const override { return fTessellations->size(); }
    288 
    289     const char* getCategory() const override { return "tessellated shadow masks"; }
    290 
    291     sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
    292 
    293     template <typename FACTORY>
    294     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 {
    312     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>
    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:
    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 
    357     const SkPath& path() const { return *fPath; }
    358     const SkMatrix& viewMatrix() const { return *fViewMatrix; }
    359 #if SK_SUPPORT_GPU
    360     /** Negative means the vertices should not be cached for this path. */
    361     int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
    362     void writeKey(void* key) const {
    363         fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
    364     }
    365     bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
    366 #else
    367     int keyBytes() const { return -1; }
    368     void writeKey(void* key) const { SK_ABORT("Should never be called"); }
    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:
    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:
    392     const SkResourceCache::Key& getKey() const {
    393         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
    394     }
    395 
    396     // always purge
    397     static bool FindVisitor(const SkResourceCache::Rec&, void*) {
    398         return false;
    399     }
    400 
    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>
    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          SkColorFilter::MakeModeFilter(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 
    474 static bool tilted(const SkPoint3& zPlaneParams) {
    475     return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
    476 }
    477 
    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 
    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.
    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 
    562 static bool validate_rec(const SkDrawShadowRec& rec) {
    563     return rec.fLightPos.isFinite() && rec.fZPlaneParams.isFinite() &&
    564            SkScalarIsFinite(rec.fLightRadius);
    565 }
    566 
    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                     SkColorFilter::MakeModeFilter(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 
    634                 // The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
    635                 // the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
    636                 //
    637                 // We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
    638                 // can be calculated by interpolating:
    639                 //
    640                 //            original edge        outer edge
    641                 //         |       |<---------- r ------>|
    642                 //         |<------|--- f -------------->|
    643                 //         |       |                     |
    644                 //    alpha = 1  alpha = a          alpha = 0
    645                 //
    646                 // Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
    647                 //
    648                 // We now need to outset the path to place the new edge in the center of the
    649                 // blur region:
    650                 //
    651                 //             original   new
    652                 //         |       |<------|--- r ------>|
    653                 //         |<------|--- f -|------------>|
    654                 //         |       |<- o ->|<--- f/2 --->|
    655                 //
    656                 //     r = o + f/2, so o = r - f/2
    657                 //
    658                 // We outset by using the stroker, so the strokeWidth is o/2.
    659                 //
    660                 SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
    661                 SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
    662                 SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
    663                 SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
    664 
    665                 // Now draw with blur
    666                 SkPaint paint;
    667                 paint.setColor(rec.fAmbientColor);
    668                 paint.setStrokeWidth(strokeWidth);
    669                 paint.setStyle(SkPaint::kStrokeAndFill_Style);
    670                 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
    671                 bool respectCTM = false;
    672                 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
    673                 this->drawPath(devSpacePath, paint);
    674             }
    675         }
    676     }
    677 
    678     if (SkColorGetA(rec.fSpotColor) > 0) {
    679         bool success = false;
    680         if (uncached) {
    681             sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
    682                                                                        zPlaneParams,
    683                                                                        devLightPos, lightRadius,
    684                                                                        transparent);
    685             if (vertices) {
    686                 SkPaint paint;
    687                 // Run the vertex color through a GaussianColorFilter and then modulate the
    688                 // grayscale result of that against our 'color' param.
    689                 paint.setColorFilter(
    690                     SkColorFilter::MakeModeFilter(rec.fSpotColor,
    691                                                   SkBlendMode::kModulate)->makeComposed(
    692                                                       SkGaussianColorFilter::Make()));
    693                 this->drawVertices(vertices.get(), nullptr, 0, SkBlendMode::kModulate, paint);
    694                 success = true;
    695             }
    696         }
    697 
    698         if (!success) {
    699             SpotVerticesFactory factory;
    700             factory.fOccluderHeight = zPlaneParams.fZ;
    701             factory.fDevLightPos = devLightPos;
    702             factory.fLightRadius = lightRadius;
    703 
    704             SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
    705             factory.fLocalCenter = center;
    706             viewMatrix.mapPoints(&center, 1);
    707             SkScalar radius, scale;
    708             SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
    709                                                devLightPos.fY - center.fY, devLightPos.fZ,
    710                                                lightRadius, &radius, &scale, &factory.fOffset);
    711             SkRect devBounds;
    712             viewMatrix.mapRect(&devBounds, path.getBounds());
    713             if (transparent ||
    714                 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
    715                 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
    716                 // if the translation of the shadow is big enough we're going to end up
    717                 // filling the entire umbra, so we can treat these as all the same
    718                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
    719             } else if (factory.fOffset.length()*scale + scale < radius) {
    720                 // if we don't translate more than the blur distance, can assume umbra is covered
    721                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
    722             } else if (path.isConvex()) {
    723                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
    724             } else {
    725                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
    726             }
    727             // need to add this after we classify the shadow
    728             factory.fOffset.fX += viewMatrix.getTranslateX();
    729             factory.fOffset.fY += viewMatrix.getTranslateY();
    730 
    731             SkColor color = rec.fSpotColor;
    732 #ifdef DEBUG_SHADOW_CHECKS
    733             switch (factory.fOccluderType) {
    734                 case SpotVerticesFactory::OccluderType::kTransparent:
    735                     color = 0xFFD2B48C;  // tan for transparent
    736                     break;
    737                 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
    738                     color = 0xFFFFA500;   // orange for opaque
    739                     break;
    740                 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
    741                     color = 0xFFE5E500;  // corn yellow for covered
    742                     break;
    743             }
    744 #endif
    745             if (!draw_shadow(factory, drawVertsProc, shadowedPath, color)) {
    746                 // draw with blur
    747                 SkMatrix shadowMatrix;
    748                 if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
    749                                                                  viewMatrix, zPlaneParams,
    750                                                                  path.getBounds(),
    751                                                                  &shadowMatrix, &radius)) {
    752                     return;
    753                 }
    754                 SkAutoDeviceCTMRestore adr(this, shadowMatrix);
    755 
    756                 SkPaint paint;
    757                 paint.setColor(rec.fSpotColor);
    758                 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
    759                 bool respectCTM = false;
    760                 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
    761                 this->drawPath(path, paint);
    762             }
    763         }
    764     }
    765 }
    766