SkShadowUtils.cpp revision 8793e3889833a3de18254cd8a147e213ec98b7fc
1f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar/*
2f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar* Copyright 2017 Google Inc.
3f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar*
4f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar* Use of this source code is governed by a BSD-style license that can be
5f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar* found in the LICENSE file.
6f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar*/
7f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
8f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkShadowUtils.h"
9f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkCanvas.h"
10f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkColorFilter.h"
11f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkColorPriv.h"
12f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkDevice.h"
13f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkDrawShadowRec.h"
14f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkPath.h"
15f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkPM4f.h"
16de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar#include "SkRandom.h"
17f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkResourceCache.h"
18f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkShadowTessellator.h"
19f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkString.h"
20f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkTLazy.h"
21f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "SkVertices.h"
22f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#if SK_SUPPORT_GPU
23f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "GrShape.h"
24f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#include "effects/GrBlurredEdgeFragmentProcessor.h"
25de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar#endif
26f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
27f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar/**
28f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar*  Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
29f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar*                           then blends with the color's G value.
30f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar*                           Final result is black with alpha of Gaussian(B)*G.
31de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar*                           The assumption is that the original color's alpha is 1.
32de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar*/
33de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarclass SK_API SkGaussianColorFilter : public SkColorFilter {
34f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainarpublic:
35f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    static sk_sp<SkColorFilter> Make() {
36de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
37f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    }
38de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
39f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
40f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    void filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const override;
41f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
42f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#if SK_SUPPORT_GPU
43f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override;
44f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar#endif
45f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
46de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    SK_TO_STRING_OVERRIDE()
47de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianColorFilter)
48de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
49f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainarprotected:
50f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    void flatten(SkWriteBuffer&) const override {}
51f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
52de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarprivate:
53de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    SkGaussianColorFilter() : INHERITED() {}
54f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
55f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    typedef SkColorFilter INHERITED;
56f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar};
57f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar
58f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainarstatic inline float eval_gaussian(float x) {
59de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // x = 1 - x;
60de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // return sk_float_exp(-x * x * 4) - 0.018f;
61de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
62de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    return 0.00030726194381713867f +
63de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar             x*(0.15489584207534790039f +
64de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar               x*(0.21345567703247070312f +
65de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar                 (2.89795351028442382812f - 2.26661229133605957031f*x)*x));
66de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar}
67de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
68de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarstatic void build_table() {
69de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    SkDebugf("const uint8_t gByteExpU8Table[256] = {");
70de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    for (int i = 0; i <= 255; ++i) {
71de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        if (!(i % 8)) {
72de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar            SkDebugf("\n");
73de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        }
74de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        int v = (int)(eval_gaussian(i / 255.f) * 256);
75de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        SkDebugf(" 0x%02X,", v);
76de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
77de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    SkDebugf("\n};\n");
78de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar}
79de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
80de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarconst uint8_t gByteExpU8Table[256] = {
81de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
82de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
83de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
84de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
85de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
86de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D,
87de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11,
88de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15,
89f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B,
90f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21,
91f3ef5332fa3f4d5ec72c178a2b19dac363a19383Pirama Arumuga Nainar    0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28,
92    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
93    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39,
94    0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42,
95    0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D,
96    0x4E, 0x50, 0x51, 0x53, 0x54, 0x55, 0x57, 0x58,
97    0x5A, 0x5B, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64,
98    0x66, 0x68, 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71,
99    0x73, 0x75, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
100    0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8A, 0x8C,
101    0x8E, 0x90, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
102    0x9C, 0x9D, 0x9F, 0xA1, 0xA2, 0xA4, 0xA6, 0xA8,
103    0xA9, 0xAB, 0xAD, 0xAE, 0xB0, 0xB2, 0xB3, 0xB5,
104    0xB7, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC0, 0xC2,
105    0xC3, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE,
106    0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9,
107    0xDA, 0xDC, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3,
108    0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
109    0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF0, 0xF1, 0xF2,
110    0xF3, 0xF3, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF7,
111    0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA,
112    0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
113};
114
115void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
116    // to re-build the table, call build_table() which will dump it out using SkDebugf.
117    if (false) {
118        build_table();
119    }
120    for (int i = 0; i < count; ++i) {
121        SkPMColor c = src[i];
122        uint8_t a = gByteExpU8Table[SkGetPackedA32(c)];
123        dst[i] = SkPackARGB32(a, a, a, a);
124    }
125}
126
127void SkGaussianColorFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
128    for (int i = 0; i < count; ++i) {
129        float v = eval_gaussian(src[i].a());
130        dst[i] = SkPM4f::FromPremulRGBA(v, v, v, v);
131    }
132}
133
134sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
135    return Make();
136}
137
138#ifndef SK_IGNORE_TO_STRING
139void SkGaussianColorFilter::toString(SkString* str) const {
140    str->append("SkGaussianColorFilter ");
141}
142#endif
143
144#if SK_SUPPORT_GPU
145
146sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
147                                                                      SkColorSpace*) const {
148    return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
149}
150#endif
151
152///////////////////////////////////////////////////////////////////////////////////////////////////
153
154namespace {
155
156uint64_t resource_cache_shared_id() {
157    return 0x2020776f64616873llu;  // 'shadow  '
158}
159
160/** Factory for an ambient shadow mesh with particular shadow properties. */
161struct AmbientVerticesFactory {
162    SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
163    bool fTransparent;
164    SkVector fOffset;
165
166    bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
167        if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
168            return false;
169        }
170        *translate = that.fOffset;
171        return true;
172    }
173
174    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
175                                   SkVector* translate) const {
176        SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
177        // pick a canonical place to generate shadow
178        SkMatrix noTrans(ctm);
179        if (!ctm.hasPerspective()) {
180            noTrans[SkMatrix::kMTransX] = 0;
181            noTrans[SkMatrix::kMTransY] = 0;
182        }
183        *translate = fOffset;
184        return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
185    }
186};
187
188/** Factory for an spot shadow mesh with particular shadow properties. */
189struct SpotVerticesFactory {
190    enum class OccluderType {
191        // The umbra cannot be dropped out because either the occluder is not opaque,
192        // or the center of the umbra is visible.
193        kTransparent,
194        // The umbra can be dropped where it is occluded.
195        kOpaquePartialUmbra,
196        // It is known that the entire umbra is occluded.
197        kOpaqueNoUmbra
198    };
199
200    SkVector fOffset;
201    SkPoint  fLocalCenter;
202    SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
203    SkPoint3 fDevLightPos;
204    SkScalar fLightRadius;
205    OccluderType fOccluderType;
206
207    bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
208        if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
209            fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
210            return false;
211        }
212        switch (fOccluderType) {
213            case OccluderType::kTransparent:
214            case OccluderType::kOpaqueNoUmbra:
215                // 'this' and 'that' will either both have no umbra removed or both have all the
216                // umbra removed.
217                *translate = that.fOffset;
218                return true;
219            case OccluderType::kOpaquePartialUmbra:
220                // In this case we partially remove the umbra differently for 'this' and 'that'
221                // if the offsets don't match.
222                if (fOffset == that.fOffset) {
223                    translate->set(0, 0);
224                    return true;
225                }
226                return false;
227        }
228        SkFAIL("Uninitialized occluder type?");
229        return false;
230    }
231
232    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
233                                   SkVector* translate) const {
234        bool transparent = OccluderType::kTransparent == fOccluderType;
235        SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
236        if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
237            translate->set(0, 0);
238            return SkShadowTessellator::MakeSpot(path, ctm, zParams,
239                                                 fDevLightPos, fLightRadius, transparent);
240        } else {
241            // pick a canonical place to generate shadow, with light centered over path
242            SkMatrix noTrans(ctm);
243            noTrans[SkMatrix::kMTransX] = 0;
244            noTrans[SkMatrix::kMTransY] = 0;
245            SkPoint devCenter(fLocalCenter);
246            noTrans.mapPoints(&devCenter, 1);
247            SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
248            *translate = fOffset;
249            return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
250                                                 centerLightPos, fLightRadius, transparent);
251        }
252    }
253};
254
255/**
256 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
257 * records are immutable this is not itself a Rec. When we need to update it we return this on
258 * the FindVisitor and let the cache destory the Rec. We'll update the tessellations and then add
259 * a new Rec with an adjusted size for any deletions/additions.
260 */
261class CachedTessellations : public SkRefCnt {
262public:
263    size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
264
265    sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
266                           SkVector* translate) const {
267        return fAmbientSet.find(ambient, matrix, translate);
268    }
269
270    sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
271                          const SkMatrix& matrix, SkVector* translate) {
272        return fAmbientSet.add(devPath, ambient, matrix, translate);
273    }
274
275    sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
276                           SkVector* translate) const {
277        return fSpotSet.find(spot, matrix, translate);
278    }
279
280    sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
281                          const SkMatrix& matrix, SkVector* translate) {
282        return fSpotSet.add(devPath, spot, matrix, translate);
283    }
284
285private:
286    template <typename FACTORY, int MAX_ENTRIES>
287    class Set {
288    public:
289        size_t size() const { return fSize; }
290
291        sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
292                               SkVector* translate) const {
293            for (int i = 0; i < MAX_ENTRIES; ++i) {
294                if (fEntries[i].fFactory.isCompatible(factory, translate)) {
295                    const SkMatrix& m = fEntries[i].fMatrix;
296                    if (matrix.hasPerspective() || m.hasPerspective()) {
297                        if (matrix != fEntries[i].fMatrix) {
298                            continue;
299                        }
300                    } else if (matrix.getScaleX() != m.getScaleX() ||
301                               matrix.getSkewX() != m.getSkewX() ||
302                               matrix.getScaleY() != m.getScaleY() ||
303                               matrix.getSkewY() != m.getSkewY()) {
304                        continue;
305                    }
306                    return fEntries[i].fVertices;
307                }
308            }
309            return nullptr;
310        }
311
312        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
313                              SkVector* translate) {
314            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
315            if (!vertices) {
316                return nullptr;
317            }
318            int i;
319            if (fCount < MAX_ENTRIES) {
320                i = fCount++;
321            } else {
322                i = gRandom.nextULessThan(MAX_ENTRIES);
323                fSize -= fEntries[i].fVertices->approximateSize();
324            }
325            fEntries[i].fFactory = factory;
326            fEntries[i].fVertices = vertices;
327            fEntries[i].fMatrix = matrix;
328            fSize += vertices->approximateSize();
329            return vertices;
330        }
331
332    private:
333        struct Entry {
334            FACTORY fFactory;
335            sk_sp<SkVertices> fVertices;
336            SkMatrix fMatrix;
337        };
338        Entry fEntries[MAX_ENTRIES];
339        int fCount = 0;
340        size_t fSize = 0;
341    };
342
343    Set<AmbientVerticesFactory, 4> fAmbientSet;
344    Set<SpotVerticesFactory, 4> fSpotSet;
345
346    static SkRandom gRandom;
347};
348
349SkRandom CachedTessellations::gRandom;
350
351/**
352 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
353 * path. The key represents the path's geometry and not any shadow params.
354 */
355class CachedTessellationsRec : public SkResourceCache::Rec {
356public:
357    CachedTessellationsRec(const SkResourceCache::Key& key,
358                           sk_sp<CachedTessellations> tessellations)
359            : fTessellations(std::move(tessellations)) {
360        fKey.reset(new uint8_t[key.size()]);
361        memcpy(fKey.get(), &key, key.size());
362    }
363
364    const Key& getKey() const override {
365        return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
366    }
367
368    size_t bytesUsed() const override { return fTessellations->size(); }
369
370    const char* getCategory() const override { return "tessellated shadow masks"; }
371
372    sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
373
374    template <typename FACTORY>
375    sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
376                           SkVector* translate) const {
377        return fTessellations->find(factory, matrix, translate);
378    }
379
380private:
381    std::unique_ptr<uint8_t[]> fKey;
382    sk_sp<CachedTessellations> fTessellations;
383};
384
385/**
386 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
387 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
388 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
389 * to the caller. The caller will update it and reinsert it back into the cache.
390 */
391template <typename FACTORY>
392struct FindContext {
393    FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
394            : fViewMatrix(viewMatrix), fFactory(factory) {}
395    const SkMatrix* const fViewMatrix;
396    // If this is valid after Find is called then we found the vertices and they should be drawn
397    // with fTranslate applied.
398    sk_sp<SkVertices> fVertices;
399    SkVector fTranslate = {0, 0};
400
401    // If this is valid after Find then the caller should add the vertices to the tessellation set
402    // and create a new CachedTessellationsRec and insert it into SkResourceCache.
403    sk_sp<CachedTessellations> fTessellationsOnFailure;
404
405    const FACTORY* fFactory;
406};
407
408/**
409 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
410 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
411 * necessary translation vector are set on the FindContext.
412 */
413template <typename FACTORY>
414bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
415    FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
416    const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
417    findContext->fVertices =
418            rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
419    if (findContext->fVertices) {
420        return true;
421    }
422    // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
423    // manipulated we will add a new Rec.
424    findContext->fTessellationsOnFailure = rec.refTessellations();
425    return false;
426}
427
428class ShadowedPath {
429public:
430    ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
431            : fPath(path)
432            , fViewMatrix(viewMatrix)
433#if SK_SUPPORT_GPU
434            , fShapeForKey(*path, GrStyle::SimpleFill())
435#endif
436    {}
437
438    const SkPath& path() const { return *fPath; }
439    const SkMatrix& viewMatrix() const { return *fViewMatrix; }
440#if SK_SUPPORT_GPU
441    /** Negative means the vertices should not be cached for this path. */
442    int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
443    void writeKey(void* key) const {
444        fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
445    }
446    bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
447#else
448    int keyBytes() const { return -1; }
449    void writeKey(void* key) const { SkFAIL("Should never be called"); }
450    bool isRRect(SkRRect* rrect) { return false; }
451#endif
452
453private:
454    const SkPath* fPath;
455    const SkMatrix* fViewMatrix;
456#if SK_SUPPORT_GPU
457    GrShape fShapeForKey;
458#endif
459};
460
461// This creates a domain of keys in SkResourceCache used by this file.
462static void* kNamespace;
463
464/**
465 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
466 * they are first found in SkResourceCache.
467 */
468template <typename FACTORY>
469    void draw_shadow(const FACTORY& factory,
470                     std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
471                     SkScalar tx, SkScalar ty)> drawProc, ShadowedPath& path, SkColor color) {
472    FindContext<FACTORY> context(&path.viewMatrix(), &factory);
473
474    SkResourceCache::Key* key = nullptr;
475    SkAutoSTArray<32 * 4, uint8_t> keyStorage;
476    int keyDataBytes = path.keyBytes();
477    if (keyDataBytes >= 0) {
478        keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
479        key = new (keyStorage.begin()) SkResourceCache::Key();
480        path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
481        key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
482        SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
483    }
484
485    sk_sp<SkVertices> vertices;
486    bool foundInCache = SkToBool(context.fVertices);
487    if (foundInCache) {
488        vertices = std::move(context.fVertices);
489    } else {
490        // TODO: handle transforming the path as part of the tessellator
491        if (key) {
492            // Update or initialize a tessellation set and add it to the cache.
493            sk_sp<CachedTessellations> tessellations;
494            if (context.fTessellationsOnFailure) {
495                tessellations = std::move(context.fTessellationsOnFailure);
496            } else {
497                tessellations.reset(new CachedTessellations());
498            }
499            vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
500                                          &context.fTranslate);
501            if (!vertices) {
502                return;
503            }
504            auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
505            SkResourceCache::Add(rec);
506        } else {
507            vertices = factory.makeVertices(path.path(), path.viewMatrix(),
508                                            &context.fTranslate);
509            if (!vertices) {
510                return;
511            }
512        }
513    }
514
515    SkPaint paint;
516    // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
517    // that against our 'color' param.
518    paint.setColorFilter(SkColorFilter::MakeComposeFilter(
519            SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
520            SkGaussianColorFilter::Make()));
521
522    drawProc(vertices.get(), SkBlendMode::kModulate, paint,
523             context.fTranslate.fX, context.fTranslate.fY);
524}
525}
526
527static bool tilted(const SkPoint3& zPlaneParams) {
528    return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
529}
530
531static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
532    SkPoint3 result;
533    m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
534    result.fZ = pt.fZ;
535    return result;
536}
537
538static SkColor compute_render_color(SkColor color, float alpha) {
539    return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
540                          SkColorGetG(color), SkColorGetB(color));
541}
542
543// Draw an offset spot shadow and outlining ambient shadow for the given path.
544void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
545                               const SkPoint3& devLightPos, SkScalar lightRadius,
546                               SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
547                               uint32_t flags) {
548    SkMatrix inverse;
549    if (!canvas->getTotalMatrix().invert(&inverse)) {
550        return;
551    }
552    SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
553
554    SkDrawShadowRec rec;
555    rec.fZPlaneParams   = zPlaneParams;
556    rec.fLightPos       = { pt.fX, pt.fY, devLightPos.fZ };
557    rec.fLightRadius    = lightRadius;
558    rec.fAmbientAlpha   = SkScalarToFloat(ambientAlpha);
559    rec.fSpotAlpha      = SkScalarToFloat(spotAlpha);
560    rec.fColor          = color;
561    rec.fFlags          = flags;
562
563    canvas->private_draw_shadow_rec(path, rec);
564}
565
566void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
567    auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
568                                SkScalar tx, SkScalar ty) {
569        SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
570                                                          SkMatrix::MakeTrans(tx, ty)));
571        this->drawVertices(vertices, mode, paint);
572    };
573
574    SkMatrix viewMatrix = this->ctm();
575    SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
576
577    ShadowedPath shadowedPath(&path, &viewMatrix);
578
579    bool tiltZPlane = tilted(rec.fZPlaneParams);
580    bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
581    bool uncached = tiltZPlane || path.isVolatile();
582
583    SkColor color = rec.fColor;
584    SkPoint3 zPlaneParams = rec.fZPlaneParams;
585    SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
586    float lightRadius = rec.fLightRadius;
587
588    float ambientAlpha = rec.fAmbientAlpha;
589    if (ambientAlpha > 0) {
590        ambientAlpha = SkTMin(ambientAlpha, 1.f);
591        if (uncached) {
592            sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
593                                                                          zPlaneParams,
594                                                                          transparent);
595            SkColor renderColor = compute_render_color(color, ambientAlpha);
596            SkPaint paint;
597            // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
598            // result of that against our 'color' param.
599            paint.setColorFilter(SkColorFilter::MakeComposeFilter(
600                                                                  SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
601                                                                  SkGaussianColorFilter::Make()));
602            this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
603        } else {
604            AmbientVerticesFactory factory;
605            factory.fOccluderHeight = zPlaneParams.fZ;
606            factory.fTransparent = transparent;
607            if (viewMatrix.hasPerspective()) {
608                factory.fOffset.set(0, 0);
609            } else {
610                factory.fOffset.fX = viewMatrix.getTranslateX();
611                factory.fOffset.fY = viewMatrix.getTranslateY();
612            }
613
614            SkColor renderColor = compute_render_color(color, ambientAlpha);
615            draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
616        }
617    }
618
619    float spotAlpha = rec.fSpotAlpha;
620    if (spotAlpha > 0) {
621        spotAlpha = SkTMin(spotAlpha, 1.f);
622        if (uncached) {
623            sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
624                                                                       zPlaneParams,
625                                                                       devLightPos, lightRadius,
626                                                                       transparent);
627            SkColor renderColor = compute_render_color(color, spotAlpha);
628            SkPaint paint;
629            // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
630            // result of that against our 'color' param.
631            paint.setColorFilter(SkColorFilter::MakeComposeFilter(
632                SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
633                SkGaussianColorFilter::Make()));
634            this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
635        } else {
636            SpotVerticesFactory factory;
637            SkScalar occluderHeight = zPlaneParams.fZ;
638            float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
639            SkScalar radius = lightRadius * zRatio;
640
641            // Compute the scale and translation for the spot shadow.
642            SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
643            SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
644            factory.fLocalCenter = center;
645            viewMatrix.mapPoints(&center, 1);
646            factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
647                                             zRatio * (center.fY - devLightPos.fY));
648            factory.fOccluderHeight = occluderHeight;
649            factory.fDevLightPos = devLightPos;
650            factory.fLightRadius = lightRadius;
651            SkRect devBounds;
652            viewMatrix.mapRect(&devBounds, path.getBounds());
653            if (transparent ||
654                SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
655                SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
656                // if the translation of the shadow is big enough we're going to end up
657                // filling the entire umbra, so we can treat these as all the same
658                factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
659            } else if (factory.fOffset.length()*scale + scale < radius) {
660                // if we don't translate more than the blur distance, can assume umbra is covered
661                factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
662            } else {
663                factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
664            }
665            // need to add this after we classify the shadow
666            factory.fOffset.fX += viewMatrix.getTranslateX();
667            factory.fOffset.fY += viewMatrix.getTranslateY();
668#ifdef DEBUG_SHADOW_CHECKS
669            switch (factory.fOccluderType) {
670                case SpotVerticesFactory::OccluderType::kTransparent:
671                    color = 0xFFD2B48C;  // tan for transparent
672                    break;
673                case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
674                    color = 0xFFFFA500;   // orange for opaque
675                    break;
676                case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
677                    color = 0xFFE5E500;  // corn yellow for covered
678                    break;
679            }
680#endif
681            SkColor renderColor = compute_render_color(color, spotAlpha);
682            draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
683        }
684    }
685}
686