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