Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / src / utils / SkShadowUtils.cpp
1 /*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/utils/SkShadowUtils.h"
9
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkBlender.h"
12 #include "include/core/SkBlurTypes.h"
13 #include "include/core/SkCanvas.h"
14 #include "include/core/SkColorFilter.h"
15 #include "include/core/SkFlattenable.h"
16 #include "include/core/SkMaskFilter.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkPaint.h"
19 #include "include/core/SkPath.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkPoint3.h"
22 #include "include/core/SkRect.h"
23 #include "include/core/SkRefCnt.h"
24 #include "include/core/SkVertices.h"
25 #include "include/private/SkIDChangeListener.h"
26 #include "include/private/SkTPin.h"
27 #include "include/private/SkTemplates.h"
28 #include "include/utils/SkRandom.h"
29 #include "src/core/SkBlurMask.h"
30 #include "src/core/SkColorFilterBase.h"
31 #include "src/core/SkColorFilterPriv.h"
32 #include "src/core/SkDevice.h"
33 #include "src/core/SkDrawShadowInfo.h"
34 #include "src/core/SkEffectPriv.h"
35 #include "src/core/SkPathPriv.h"
36 #include "src/core/SkRasterPipeline.h"
37 #include "src/core/SkResourceCache.h"
38 #include "src/core/SkVM.h"
39 #include "src/core/SkVerticesPriv.h"
40 #include "src/utils/SkShadowTessellator.h"
41
42 #if SK_SUPPORT_GPU
43 #include "include/effects/SkRuntimeEffect.h"
44 #include "src/core/SkRuntimeEffectPriv.h"
45 #include "src/gpu/ganesh/GrFragmentProcessor.h"
46 #include "src/gpu/ganesh/GrStyle.h"
47 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
48 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
49
50 class GrColorInfo;
51 class GrRecordingContext;
52 #endif
53
54 #include <string.h>
55 #include <algorithm>
56 #include <cstdint>
57 #include <functional>
58 #include <memory>
59 #include <new>
60 #include <utility>
61
62 class SkArenaAlloc;
63 class SkColorInfo;
64 class SkRRect;
65 class SkReadBuffer;
66 class SkWriteBuffer;
67
68 /**
69 *  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
70 *                           then blends with the color's G value.
71 *                           Final result is black with alpha of Gaussian(B)*G.
72 *                           The assumption is that the original color's alpha is 1.
73 */
74 class SkGaussianColorFilter : public SkColorFilterBase {
75 public:
76     SkGaussianColorFilter() : INHERITED() {}
77
78 #if SK_SUPPORT_GPU
79     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
80                                    GrRecordingContext*, const GrColorInfo&) const override;
81 #endif
82
83 protected:
84     void flatten(SkWriteBuffer&) const override {}
85     bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
86         rec.fPipeline->append(SkRasterPipeline::gauss_a_to_rgba);
87         return true;
88     }
89
90     skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& dst, skvm::Uniforms*,
91                           SkArenaAlloc*) const override {
92         // x = 1 - x;
93         // exp(-x * x * 4) - 0.018f;
94         // ... now approximate with quartic
95         //
96         skvm::F32 x = p->splat(-2.26661229133605957031f);
97                   x = c.a * x + 2.89795351028442382812f;
98                   x = c.a * x + 0.21345567703247070312f;
99                   x = c.a * x + 0.15489584207534790039f;
100                   x = c.a * x + 0.00030726194381713867f;
101         return {x, x, x, x};
102     }
103
104 private:
105     SK_FLATTENABLE_HOOKS(SkGaussianColorFilter)
106
107     using INHERITED = SkColorFilterBase;
108 };
109
110 sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
111     return SkColorFilterPriv::MakeGaussian();
112 }
113
114 #if SK_SUPPORT_GPU
115
116 GrFPResult SkGaussianColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
117                                                       GrRecordingContext*,
118                                                       const GrColorInfo&) const {
119     static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, R"(
120         half4 main(half4 inColor) {
121             half factor = 1 - inColor.a;
122             factor = exp(-factor * factor * 4) - 0.018;
123             return half4(factor);
124         }
125     )");
126     SkASSERT(SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect));
127     return GrFPSuccess(
128             GrSkSLFP::Make(effect, "gaussian_fp", std::move(inputFP), GrSkSLFP::OptFlags::kNone));
129 }
130 #endif
131
132 sk_sp<SkColorFilter> SkColorFilterPriv::MakeGaussian() {
133     return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
134 }
135
136 ///////////////////////////////////////////////////////////////////////////////////////////////////
137
138 namespace {
139
140 uint64_t resource_cache_shared_id() {
141     return 0x2020776f64616873llu;  // 'shadow  '
142 }
143
144 /** Factory for an ambient shadow mesh with particular shadow properties. */
145 struct AmbientVerticesFactory {
146     SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
147     bool fTransparent;
148     SkVector fOffset;
149
150     bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
151         if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
152             return false;
153         }
154         *translate = that.fOffset;
155         return true;
156     }
157
158     sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
159                                    SkVector* translate) const {
160         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
161         // pick a canonical place to generate shadow
162         SkMatrix noTrans(ctm);
163         if (!ctm.hasPerspective()) {
164             noTrans[SkMatrix::kMTransX] = 0;
165             noTrans[SkMatrix::kMTransY] = 0;
166         }
167         *translate = fOffset;
168         return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
169     }
170 };
171
172 /** Factory for an spot shadow mesh with particular shadow properties. */
173 struct SpotVerticesFactory {
174     enum class OccluderType {
175         // The umbra cannot be dropped out because either the occluder is not opaque,
176         // or the center of the umbra is visible. Uses point light.
177         kPointTransparent,
178         // The umbra can be dropped where it is occluded. Uses point light.
179         kPointOpaquePartialUmbra,
180         // It is known that the entire umbra is occluded. Uses point light.
181         kPointOpaqueNoUmbra,
182         // Uses directional light.
183         kDirectional,
184         // The umbra can't be dropped out. Uses directional light.
185         kDirectionalTransparent,
186     };
187
188     SkVector fOffset;
189     SkPoint  fLocalCenter;
190     SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
191     SkPoint3 fDevLightPos;
192     SkScalar fLightRadius;
193     OccluderType fOccluderType;
194
195     bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
196         if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
197             fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
198             return false;
199         }
200         switch (fOccluderType) {
201             case OccluderType::kPointTransparent:
202             case OccluderType::kPointOpaqueNoUmbra:
203                 // 'this' and 'that' will either both have no umbra removed or both have all the
204                 // umbra removed.
205                 *translate = that.fOffset;
206                 return true;
207             case OccluderType::kPointOpaquePartialUmbra:
208                 // In this case we partially remove the umbra differently for 'this' and 'that'
209                 // if the offsets don't match.
210                 if (fOffset == that.fOffset) {
211                     translate->set(0, 0);
212                     return true;
213                 }
214                 return false;
215             case OccluderType::kDirectional:
216             case OccluderType::kDirectionalTransparent:
217                 *translate = that.fOffset - fOffset;
218                 return true;
219         }
220         SK_ABORT("Uninitialized occluder type?");
221     }
222
223     sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
224                                    SkVector* translate) const {
225         bool transparent = fOccluderType == OccluderType::kPointTransparent ||
226                            fOccluderType == OccluderType::kDirectionalTransparent;
227         bool directional = fOccluderType == OccluderType::kDirectional ||
228                            fOccluderType == OccluderType::kDirectionalTransparent;
229         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
230         if (directional) {
231             translate->set(0, 0);
232             return SkShadowTessellator::MakeSpot(path, ctm, zParams, fDevLightPos, fLightRadius,
233                                                  transparent, true);
234         } else if (ctm.hasPerspective() || OccluderType::kPointOpaquePartialUmbra == fOccluderType) {
235             translate->set(0, 0);
236             return SkShadowTessellator::MakeSpot(path, ctm, zParams, fDevLightPos, fLightRadius,
237                                                  transparent, false);
238         } else {
239             // pick a canonical place to generate shadow, with light centered over path
240             SkMatrix noTrans(ctm);
241             noTrans[SkMatrix::kMTransX] = 0;
242             noTrans[SkMatrix::kMTransY] = 0;
243             SkPoint devCenter(fLocalCenter);
244             noTrans.mapPoints(&devCenter, 1);
245             SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
246             *translate = fOffset;
247             return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
248                                                  centerLightPos, fLightRadius, transparent, false);
249         }
250     }
251 };
252
253 /**
254  * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
255  * records are immutable this is not itself a Rec. When we need to update it we return this on
256  * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
257  * a new Rec with an adjusted size for any deletions/additions.
258  */
259 class CachedTessellations : public SkRefCnt {
260 public:
261     size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
262
263     sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
264                            SkVector* translate) const {
265         return fAmbientSet.find(ambient, matrix, translate);
266     }
267
268     sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
269                           const SkMatrix& matrix, SkVector* translate) {
270         return fAmbientSet.add(devPath, ambient, matrix, translate);
271     }
272
273     sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
274                            SkVector* translate) const {
275         return fSpotSet.find(spot, matrix, translate);
276     }
277
278     sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
279                           const SkMatrix& matrix, SkVector* translate) {
280         return fSpotSet.add(devPath, spot, matrix, translate);
281     }
282
283 private:
284     template <typename FACTORY, int MAX_ENTRIES>
285     class Set {
286     public:
287         size_t size() const { return fSize; }
288
289         sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
290                                SkVector* translate) const {
291             for (int i = 0; i < MAX_ENTRIES; ++i) {
292                 if (fEntries[i].fFactory.isCompatible(factory, translate)) {
293                     const SkMatrix& m = fEntries[i].fMatrix;
294                     if (matrix.hasPerspective() || m.hasPerspective()) {
295                         if (matrix != fEntries[i].fMatrix) {
296                             continue;
297                         }
298                     } else if (matrix.getScaleX() != m.getScaleX() ||
299                                matrix.getSkewX() != m.getSkewX() ||
300                                matrix.getScaleY() != m.getScaleY() ||
301                                matrix.getSkewY() != m.getSkewY()) {
302                         continue;
303                     }
304                     return fEntries[i].fVertices;
305                 }
306             }
307             return nullptr;
308         }
309
310         sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
311                               SkVector* translate) {
312             sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
313             if (!vertices) {
314                 return nullptr;
315             }
316             int i;
317             if (fCount < MAX_ENTRIES) {
318                 i = fCount++;
319             } else {
320                 i = fRandom.nextULessThan(MAX_ENTRIES);
321                 fSize -= fEntries[i].fVertices->approximateSize();
322             }
323             fEntries[i].fFactory = factory;
324             fEntries[i].fVertices = vertices;
325             fEntries[i].fMatrix = matrix;
326             fSize += vertices->approximateSize();
327             return vertices;
328         }
329
330     private:
331         struct Entry {
332             FACTORY fFactory;
333             sk_sp<SkVertices> fVertices;
334             SkMatrix fMatrix;
335         };
336         Entry fEntries[MAX_ENTRIES];
337         int fCount = 0;
338         size_t fSize = 0;
339         SkRandom fRandom;
340     };
341
342     Set<AmbientVerticesFactory, 4> fAmbientSet;
343     Set<SpotVerticesFactory, 4> fSpotSet;
344 };
345
346 /**
347  * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
348  * path. The key represents the path's geometry and not any shadow params.
349  */
350 class CachedTessellationsRec : public SkResourceCache::Rec {
351 public:
352     CachedTessellationsRec(const SkResourceCache::Key& key,
353                            sk_sp<CachedTessellations> tessellations)
354             : fTessellations(std::move(tessellations)) {
355         fKey.reset(new uint8_t[key.size()]);
356         memcpy(fKey.get(), &key, key.size());
357     }
358
359     const Key& getKey() const override {
360         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
361     }
362
363     size_t bytesUsed() const override { return fTessellations->size(); }
364
365     const char* getCategory() const override { return "tessellated shadow masks"; }
366
367     sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
368
369     template <typename FACTORY>
370     sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
371                            SkVector* translate) const {
372         return fTessellations->find(factory, matrix, translate);
373     }
374
375 private:
376     std::unique_ptr<uint8_t[]> fKey;
377     sk_sp<CachedTessellations> fTessellations;
378 };
379
380 /**
381  * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
382  * vertices and a translation vector. If the CachedTessellations does not contain a suitable
383  * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
384  * to the caller. The caller will update it and reinsert it back into the cache.
385  */
386 template <typename FACTORY>
387 struct FindContext {
388     FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
389             : fViewMatrix(viewMatrix), fFactory(factory) {}
390     const SkMatrix* const fViewMatrix;
391     // If this is valid after Find is called then we found the vertices and they should be drawn
392     // with fTranslate applied.
393     sk_sp<SkVertices> fVertices;
394     SkVector fTranslate = {0, 0};
395
396     // If this is valid after Find then the caller should add the vertices to the tessellation set
397     // and create a new CachedTessellationsRec and insert it into SkResourceCache.
398     sk_sp<CachedTessellations> fTessellationsOnFailure;
399
400     const FACTORY* fFactory;
401 };
402
403 /**
404  * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
405  * the FindContext are used to determine if the vertices are reusable. If so the vertices and
406  * necessary translation vector are set on the FindContext.
407  */
408 template <typename FACTORY>
409 bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
410     FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
411     const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
412     findContext->fVertices =
413             rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
414     if (findContext->fVertices) {
415         return true;
416     }
417     // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
418     // manipulated we will add a new Rec.
419     findContext->fTessellationsOnFailure = rec.refTessellations();
420     return false;
421 }
422
423 class ShadowedPath {
424 public:
425     ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
426             : fPath(path)
427             , fViewMatrix(viewMatrix)
428 #if SK_SUPPORT_GPU
429             , fShapeForKey(*path, GrStyle::SimpleFill())
430 #endif
431     {}
432
433     const SkPath& path() const { return *fPath; }
434     const SkMatrix& viewMatrix() const { return *fViewMatrix; }
435 #if SK_SUPPORT_GPU
436     /** Negative means the vertices should not be cached for this path. */
437     int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
438     void writeKey(void* key) const {
439         fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
440     }
441     bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
442 #else
443     int keyBytes() const { return -1; }
444     void writeKey(void* key) const { SK_ABORT("Should never be called"); }
445     bool isRRect(SkRRect* rrect) { return false; }
446 #endif
447
448 private:
449     const SkPath* fPath;
450     const SkMatrix* fViewMatrix;
451 #if SK_SUPPORT_GPU
452     GrStyledShape fShapeForKey;
453 #endif
454 };
455
456 // This creates a domain of keys in SkResourceCache used by this file.
457 static void* kNamespace;
458
459 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
460 class ShadowInvalidator : public SkIDChangeListener {
461 public:
462     ShadowInvalidator(const SkResourceCache::Key& key) {
463         fKey.reset(new uint8_t[key.size()]);
464         memcpy(fKey.get(), &key, key.size());
465     }
466
467 private:
468     const SkResourceCache::Key& getKey() const {
469         return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
470     }
471
472     // always purge
473     static bool FindVisitor(const SkResourceCache::Rec&, void*) {
474         return false;
475     }
476
477     void changed() override {
478         SkResourceCache::Find(this->getKey(), ShadowInvalidator::FindVisitor, nullptr);
479     }
480
481     std::unique_ptr<uint8_t[]> fKey;
482 };
483
484 /**
485  * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
486  * they are first found in SkResourceCache.
487  */
488 template <typename FACTORY>
489 bool draw_shadow(const FACTORY& factory,
490                  std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
491                  SkScalar tx, SkScalar ty, bool)> drawProc, ShadowedPath& path, SkColor color) {
492     FindContext<FACTORY> context(&path.viewMatrix(), &factory);
493
494     SkResourceCache::Key* key = nullptr;
495     SkAutoSTArray<32 * 4, uint8_t> keyStorage;
496     int keyDataBytes = path.keyBytes();
497     if (keyDataBytes >= 0) {
498         keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
499         key = new (keyStorage.begin()) SkResourceCache::Key();
500         path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
501         key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
502         SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
503     }
504
505     sk_sp<SkVertices> vertices;
506     bool foundInCache = SkToBool(context.fVertices);
507     if (foundInCache) {
508         vertices = std::move(context.fVertices);
509     } else {
510         // TODO: handle transforming the path as part of the tessellator
511         if (key) {
512             // Update or initialize a tessellation set and add it to the cache.
513             sk_sp<CachedTessellations> tessellations;
514             if (context.fTessellationsOnFailure) {
515                 tessellations = std::move(context.fTessellationsOnFailure);
516             } else {
517                 tessellations.reset(new CachedTessellations());
518             }
519             vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
520                                           &context.fTranslate);
521             if (!vertices) {
522                 return false;
523             }
524             auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
525             SkPathPriv::AddGenIDChangeListener(path.path(), sk_make_sp<ShadowInvalidator>(*key));
526             SkResourceCache::Add(rec);
527         } else {
528             vertices = factory.makeVertices(path.path(), path.viewMatrix(),
529                                             &context.fTranslate);
530             if (!vertices) {
531                 return false;
532             }
533         }
534     }
535
536     SkPaint paint;
537     // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
538     // that against our 'color' param.
539     paint.setColorFilter(
540          SkColorFilters::Blend(color, SkBlendMode::kModulate)->makeComposed(
541                                                                 SkColorFilterPriv::MakeGaussian()));
542
543     drawProc(vertices.get(), SkBlendMode::kModulate, paint,
544              context.fTranslate.fX, context.fTranslate.fY, path.viewMatrix().hasPerspective());
545
546     return true;
547 }
548 }  // namespace
549
550 static bool tilted(const SkPoint3& zPlaneParams) {
551     return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
552 }
553
554 void SkShadowUtils::ComputeTonalColors(SkColor inAmbientColor, SkColor inSpotColor,
555                                        SkColor* outAmbientColor, SkColor* outSpotColor) {
556     // For tonal color we only compute color values for the spot shadow.
557     // The ambient shadow is greyscale only.
558
559     // Ambient
560     *outAmbientColor = SkColorSetARGB(SkColorGetA(inAmbientColor), 0, 0, 0);
561
562     // Spot
563     int spotR = SkColorGetR(inSpotColor);
564     int spotG = SkColorGetG(inSpotColor);
565     int spotB = SkColorGetB(inSpotColor);
566     int max = std::max(std::max(spotR, spotG), spotB);
567     int min = std::min(std::min(spotR, spotG), spotB);
568     SkScalar luminance = 0.5f*(max + min)/255.f;
569     SkScalar origA = SkColorGetA(inSpotColor)/255.f;
570
571     // We compute a color alpha value based on the luminance of the color, scaled by an
572     // adjusted alpha value. We want the following properties to match the UX examples
573     // (assuming a = 0.25) and to ensure that we have reasonable results when the color
574     // is black and/or the alpha is 0:
575     //     f(0, a) = 0
576     //     f(luminance, 0) = 0
577     //     f(1, 0.25) = .5
578     //     f(0.5, 0.25) = .4
579     //     f(1, 1) = 1
580     // The following functions match this as closely as possible.
581     SkScalar alphaAdjust = (2.6f + (-2.66667f + 1.06667f*origA)*origA)*origA;
582     SkScalar colorAlpha = (3.544762f + (-4.891428f + 2.3466f*luminance)*luminance)*luminance;
583     colorAlpha = SkTPin(alphaAdjust*colorAlpha, 0.0f, 1.0f);
584
585     // Similarly, we set the greyscale alpha based on luminance and alpha so that
586     //     f(0, a) = a
587     //     f(luminance, 0) = 0
588     //     f(1, 0.25) = 0.15
589     SkScalar greyscaleAlpha = SkTPin(origA*(1 - 0.4f*luminance), 0.0f, 1.0f);
590
591     // The final color we want to emulate is generated by rendering a color shadow (C_rgb) using an
592     // alpha computed from the color's luminance (C_a), and then a black shadow with alpha (S_a)
593     // which is an adjusted value of 'a'.  Assuming SrcOver, a background color of B_rgb, and
594     // ignoring edge falloff, this becomes
595     //
596     //      (C_a - S_a*C_a)*C_rgb + (1 - (S_a + C_a - S_a*C_a))*B_rgb
597     //
598     // Assuming premultiplied alpha, this means we scale the color by (C_a - S_a*C_a) and
599     // set the alpha to (S_a + C_a - S_a*C_a).
600     SkScalar colorScale = colorAlpha*(SK_Scalar1 - greyscaleAlpha);
601     SkScalar tonalAlpha = colorScale + greyscaleAlpha;
602     SkScalar unPremulScale = colorScale / tonalAlpha;
603     *outSpotColor = SkColorSetARGB(tonalAlpha*255.999f,
604                                    unPremulScale*spotR,
605                                    unPremulScale*spotG,
606                                    unPremulScale*spotB);
607 }
608
609 static bool fill_shadow_rec(const SkPath& path, const SkPoint3& zPlaneParams,
610                             const SkPoint3& lightPos, SkScalar lightRadius,
611                             SkColor ambientColor, SkColor spotColor,
612                             uint32_t flags, const SkMatrix& ctm, SkDrawShadowRec* rec) {
613     SkPoint pt = { lightPos.fX, lightPos.fY };
614     if (!SkToBool(flags & kDirectionalLight_ShadowFlag)) {
615         // If light position is in device space, need to transform to local space
616         // before applying to SkCanvas.
617         SkMatrix inverse;
618         if (!ctm.invert(&inverse)) {
619             return false;
620         }
621         inverse.mapPoints(&pt, 1);
622     }
623
624     rec->fZPlaneParams   = zPlaneParams;
625     rec->fLightPos       = { pt.fX, pt.fY, lightPos.fZ };
626     rec->fLightRadius    = lightRadius;
627     rec->fAmbientColor   = ambientColor;
628     rec->fSpotColor      = spotColor;
629     rec->fFlags          = flags;
630
631     return true;
632 }
633
634 // Draw an offset spot shadow and outlining ambient shadow for the given path.
635 void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
636                                const SkPoint3& lightPos, SkScalar lightRadius,
637                                SkColor ambientColor, SkColor spotColor,
638                                uint32_t flags) {
639     SkDrawShadowRec rec;
640     if (!fill_shadow_rec(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor,
641                          flags, canvas->getTotalMatrix(), &rec)) {
642         return;
643     }
644
645     canvas->private_draw_shadow_rec(path, rec);
646 }
647
648 bool SkShadowUtils::GetLocalBounds(const SkMatrix& ctm, const SkPath& path,
649                                    const SkPoint3& zPlaneParams, const SkPoint3& lightPos,
650                                    SkScalar lightRadius, uint32_t flags, SkRect* bounds) {
651     SkDrawShadowRec rec;
652     if (!fill_shadow_rec(path, zPlaneParams, lightPos, lightRadius, SK_ColorBLACK, SK_ColorBLACK,
653                          flags, ctm, &rec)) {
654         return false;
655     }
656
657     SkDrawShadowMetrics::GetLocalBounds(path, rec, ctm, bounds);
658
659     return true;
660 }
661
662 //////////////////////////////////////////////////////////////////////////////////////////////
663
664 static bool validate_rec(const SkDrawShadowRec& rec) {
665     return rec.fLightPos.isFinite() && rec.fZPlaneParams.isFinite() &&
666            SkScalarIsFinite(rec.fLightRadius);
667 }
668
669 void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
670     auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
671                                 SkScalar tx, SkScalar ty, bool hasPerspective) {
672         if (vertices->priv().vertexCount()) {
673             // For perspective shadows we've already computed the shadow in world space,
674             // and we can't translate it without changing it. Otherwise we concat the
675             // change in translation from the cached version.
676             SkAutoDeviceTransformRestore adr(
677                     this,
678                     hasPerspective ? SkMatrix::I()
679                                    : this->localToDevice() * SkMatrix::Translate(tx, ty));
680             // The vertex colors for a tesselated shadow polygon are always either opaque black
681             // or transparent and their real contribution to the final blended color is via
682             // their alpha. We can skip expensive per-vertex color conversion for this.
683             this->drawVertices(vertices, SkBlender::Mode(mode), paint, /*skipColorXform=*/true);
684         }
685     };
686
687     if (!validate_rec(rec)) {
688         return;
689     }
690
691     SkMatrix viewMatrix = this->localToDevice();
692     SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
693
694     ShadowedPath shadowedPath(&path, &viewMatrix);
695
696     bool tiltZPlane = tilted(rec.fZPlaneParams);
697     bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
698     bool directional = SkToBool(rec.fFlags & SkShadowFlags::kDirectionalLight_ShadowFlag);
699     bool useBlur = SkToBool(rec.fFlags & SkShadowFlags::kConcaveBlurOnly_ShadowFlag) &&
700                    !path.isConvex();
701     bool uncached = tiltZPlane || path.isVolatile();
702
703     SkPoint3 zPlaneParams = rec.fZPlaneParams;
704     SkPoint3 devLightPos = rec.fLightPos;
705     if (!directional) {
706         viewMatrix.mapPoints((SkPoint*)&devLightPos.fX, 1);
707     }
708     float lightRadius = rec.fLightRadius;
709
710     if (SkColorGetA(rec.fAmbientColor) > 0) {
711         bool success = false;
712         if (uncached && !useBlur) {
713             sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
714                                                                           zPlaneParams,
715                                                                           transparent);
716             if (vertices) {
717                 SkPaint paint;
718                 // Run the vertex color through a GaussianColorFilter and then modulate the
719                 // grayscale result of that against our 'color' param.
720                 paint.setColorFilter(
721                     SkColorFilters::Blend(rec.fAmbientColor,
722                                                   SkBlendMode::kModulate)->makeComposed(
723                                                                SkColorFilterPriv::MakeGaussian()));
724                 // The vertex colors for a tesselated shadow polygon are always either opaque black
725                 // or transparent and their real contribution to the final blended color is via
726                 // their alpha. We can skip expensive per-vertex color conversion for this.
727                 this->drawVertices(vertices.get(),
728                                    SkBlender::Mode(SkBlendMode::kModulate),
729                                    paint,
730                                    /*skipColorXform=*/true);
731                 success = true;
732             }
733         }
734
735         if (!success && !useBlur) {
736             AmbientVerticesFactory factory;
737             factory.fOccluderHeight = zPlaneParams.fZ;
738             factory.fTransparent = transparent;
739             if (viewMatrix.hasPerspective()) {
740                 factory.fOffset.set(0, 0);
741             } else {
742                 factory.fOffset.fX = viewMatrix.getTranslateX();
743                 factory.fOffset.fY = viewMatrix.getTranslateY();
744             }
745
746             success = draw_shadow(factory, drawVertsProc, shadowedPath, rec.fAmbientColor);
747         }
748
749         // All else has failed, draw with blur
750         if (!success) {
751             // Pretransform the path to avoid transforming the stroke, below.
752             SkPath devSpacePath;
753             path.transform(viewMatrix, &devSpacePath);
754             devSpacePath.setIsVolatile(true);
755
756             // The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
757             // the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
758             //
759             // We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
760             // can be calculated by interpolating:
761             //
762             //            original edge        outer edge
763             //         |       |<---------- r ------>|
764             //         |<------|--- f -------------->|
765             //         |       |                     |
766             //    alpha = 1  alpha = a          alpha = 0
767             //
768             // Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
769             //
770             // We now need to outset the path to place the new edge in the center of the
771             // blur region:
772             //
773             //             original   new
774             //         |       |<------|--- r ------>|
775             //         |<------|--- f -|------------>|
776             //         |       |<- o ->|<--- f/2 --->|
777             //
778             //     r = o + f/2, so o = r - f/2
779             //
780             // We outset by using the stroker, so the strokeWidth is o/2.
781             //
782             SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
783             SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
784             SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
785             SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
786
787             // Now draw with blur
788             SkPaint paint;
789             paint.setColor(rec.fAmbientColor);
790             paint.setStrokeWidth(strokeWidth);
791             paint.setStyle(SkPaint::kStrokeAndFill_Style);
792             SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
793             bool respectCTM = false;
794             paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
795             this->drawPath(devSpacePath, paint);
796         }
797     }
798
799     if (SkColorGetA(rec.fSpotColor) > 0) {
800         bool success = false;
801         if (uncached && !useBlur) {
802             sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
803                                                                        zPlaneParams,
804                                                                        devLightPos, lightRadius,
805                                                                        transparent,
806                                                                        directional);
807             if (vertices) {
808                 SkPaint paint;
809                 // Run the vertex color through a GaussianColorFilter and then modulate the
810                 // grayscale result of that against our 'color' param.
811                 paint.setColorFilter(
812                     SkColorFilters::Blend(rec.fSpotColor,
813                                                   SkBlendMode::kModulate)->makeComposed(
814                                                       SkColorFilterPriv::MakeGaussian()));
815                 // The vertex colors for a tesselated shadow polygon are always either opaque black
816                 // or transparent and their real contribution to the final blended color is via
817                 // their alpha. We can skip expensive per-vertex color conversion for this.
818                 this->drawVertices(vertices.get(),
819                                    SkBlender::Mode(SkBlendMode::kModulate),
820                                    paint,
821                                    /*skipColorXform=*/true);
822                 success = true;
823             }
824         }
825
826         if (!success && !useBlur) {
827             SpotVerticesFactory factory;
828             factory.fOccluderHeight = zPlaneParams.fZ;
829             factory.fDevLightPos = devLightPos;
830             factory.fLightRadius = lightRadius;
831
832             SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
833             factory.fLocalCenter = center;
834             viewMatrix.mapPoints(&center, 1);
835             SkScalar radius, scale;
836             if (SkToBool(rec.fFlags & kDirectionalLight_ShadowFlag)) {
837                 SkDrawShadowMetrics::GetDirectionalParams(zPlaneParams.fZ, devLightPos.fX,
838                                                           devLightPos.fY, devLightPos.fZ,
839                                                           lightRadius, &radius, &scale,
840                                                           &factory.fOffset);
841             } else {
842                 SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
843                                                    devLightPos.fY - center.fY, devLightPos.fZ,
844                                                    lightRadius, &radius, &scale, &factory.fOffset);
845             }
846
847             SkRect devBounds;
848             viewMatrix.mapRect(&devBounds, path.getBounds());
849             if (transparent ||
850                 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
851                 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
852                 // if the translation of the shadow is big enough we're going to end up
853                 // filling the entire umbra, we can treat these as all the same
854                 if (directional) {
855                     factory.fOccluderType =
856                             SpotVerticesFactory::OccluderType::kDirectionalTransparent;
857                 } else {
858                     factory.fOccluderType = SpotVerticesFactory::OccluderType::kPointTransparent;
859                 }
860             } else if (directional) {
861                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kDirectional;
862             } else if (factory.fOffset.length()*scale + scale < radius) {
863                 // if we don't translate more than the blur distance, can assume umbra is covered
864                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kPointOpaqueNoUmbra;
865             } else if (path.isConvex()) {
866                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kPointOpaquePartialUmbra;
867             } else {
868                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kPointTransparent;
869             }
870             // need to add this after we classify the shadow
871             factory.fOffset.fX += viewMatrix.getTranslateX();
872             factory.fOffset.fY += viewMatrix.getTranslateY();
873
874             SkColor color = rec.fSpotColor;
875 #ifdef DEBUG_SHADOW_CHECKS
876             switch (factory.fOccluderType) {
877                 case SpotVerticesFactory::OccluderType::kPointTransparent:
878                     color = 0xFFD2B48C;  // tan for transparent
879                     break;
880                 case SpotVerticesFactory::OccluderType::kPointOpaquePartialUmbra:
881                     color = 0xFFFFA500;   // orange for opaque
882                     break;
883                 case SpotVerticesFactory::OccluderType::kPointOpaqueNoUmbra:
884                     color = 0xFFE5E500;  // corn yellow for covered
885                     break;
886                 case SpotVerticesFactory::OccluderType::kDirectional:
887                 case SpotVerticesFactory::OccluderType::kDirectionalTransparent:
888                     color = 0xFF550000;  // dark red for directional
889                     break;
890             }
891 #endif
892             success = draw_shadow(factory, drawVertsProc, shadowedPath, color);
893         }
894
895         // All else has failed, draw with blur
896         if (!success) {
897             SkMatrix shadowMatrix;
898             SkScalar radius;
899             if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
900                                                              viewMatrix, zPlaneParams,
901                                                              path.getBounds(), directional,
902                                                              &shadowMatrix, &radius)) {
903                 return;
904             }
905             SkAutoDeviceTransformRestore adr2(this, shadowMatrix);
906
907             SkPaint paint;
908             paint.setColor(rec.fSpotColor);
909             SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
910             bool respectCTM = false;
911             paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
912             this->drawPath(path, paint);
913         }
914     }
915 }