blob: ebd27e02b9859668d8800b14f6a108cc03d88810 [file] [log] [blame]
Jim Van Verth43475ad2017-01-13 14:37:37 -05001/*
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"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050010#include "SkColorFilter.h"
Florin Malitaab244f02017-05-03 19:16:58 +000011#include "SkColorPriv.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050012#include "SkPath.h"
Mike Reedb9641bd2017-05-04 10:57:40 -040013#include "SkPM4f.h"
Brian Salomond1ac9822017-02-03 14:25:02 -050014#include "SkRandom.h"
Brian Salomon5e689522017-02-01 12:07:17 -050015#include "SkResourceCache.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050016#include "SkShadowTessellator.h"
Ben Wagner4d1955c2017-03-10 13:08:15 -050017#include "SkString.h"
Brian Salomon5e689522017-02-01 12:07:17 -050018#include "SkTLazy.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050019#include "SkVertices.h"
Brian Salomon5e689522017-02-01 12:07:17 -050020#if SK_SUPPORT_GPU
21#include "GrShape.h"
22#include "effects/GrBlurredEdgeFragmentProcessor.h"
23#endif
Jim Van Verthcf40e302017-03-02 11:28:43 -050024#include "../../src/effects/shadows/SkAmbientShadowMaskFilter.h"
25#include "../../src/effects/shadows/SkSpotShadowMaskFilter.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050026
27/**
28* Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
29* then blends with the color's G value.
30* Final result is black with alpha of Gaussian(B)*G.
31* The assumption is that the original color's alpha is 1.
32*/
33class SK_API SkGaussianColorFilter : public SkColorFilter {
34public:
35 static sk_sp<SkColorFilter> Make() {
36 return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
37 }
38
39 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
Mike Reedb9641bd2017-05-04 10:57:40 -040040 void filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const override;
Jim Van Verthefe3ded2017-01-30 13:11:45 -050041
42#if SK_SUPPORT_GPU
43 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override;
44#endif
45
46 SK_TO_STRING_OVERRIDE()
47 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianColorFilter)
48
49protected:
50 void flatten(SkWriteBuffer&) const override {}
51
52private:
53 SkGaussianColorFilter() : INHERITED() {}
54
55 typedef SkColorFilter INHERITED;
56};
57
Mike Reedb9641bd2017-05-04 10:57:40 -040058static inline float eval_gaussian(float x) {
59 float factor = 1 - x;
60 return sk_float_exp(-factor * factor * 4) - 0.018f;
61}
62
Mike Reed37f16552017-05-02 15:13:43 -040063static void build_table() {
Jim Van Verth060d9822017-05-04 09:58:17 -040064 SkDebugf("const uint8_t gByteExpU8Table[256] = {");
Mike Reed37f16552017-05-02 15:13:43 -040065 for (int i = 0; i <= 255; ++i) {
66 if (!(i % 8)) {
67 SkDebugf("\n");
68 }
Mike Reedb9641bd2017-05-04 10:57:40 -040069 int v = (int)(eval_gaussian(i / 255.f) * 256);
Jim Van Verth060d9822017-05-04 09:58:17 -040070 SkDebugf(" 0x%02X,", v);
Mike Reed37f16552017-05-02 15:13:43 -040071 }
72 SkDebugf("\n};\n");
73}
74
Jim Van Verth060d9822017-05-04 09:58:17 -040075const uint8_t gByteExpU8Table[256] = {
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
77 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
78 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
Mike Reedb9641bd2017-05-04 10:57:40 -040079 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
Jim Van Verth060d9822017-05-04 09:58:17 -040080 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
81 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D,
82 0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11,
83 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15,
84 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B,
85 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21,
Mike Reedb9641bd2017-05-04 10:57:40 -040086 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28,
Jim Van Verth060d9822017-05-04 09:58:17 -040087 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
88 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39,
89 0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42,
90 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D,
91 0x4E, 0x50, 0x51, 0x53, 0x54, 0x55, 0x57, 0x58,
92 0x5A, 0x5B, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64,
93 0x66, 0x68, 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71,
Mike Reedb9641bd2017-05-04 10:57:40 -040094 0x73, 0x75, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
Jim Van Verth060d9822017-05-04 09:58:17 -040095 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8A, 0x8C,
Mike Reedb9641bd2017-05-04 10:57:40 -040096 0x8E, 0x90, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
Jim Van Verth060d9822017-05-04 09:58:17 -040097 0x9C, 0x9D, 0x9F, 0xA1, 0xA2, 0xA4, 0xA6, 0xA8,
98 0xA9, 0xAB, 0xAD, 0xAE, 0xB0, 0xB2, 0xB3, 0xB5,
Mike Reedb9641bd2017-05-04 10:57:40 -040099 0xB7, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC0, 0xC2,
100 0xC3, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE,
Jim Van Verth060d9822017-05-04 09:58:17 -0400101 0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9,
Mike Reedb9641bd2017-05-04 10:57:40 -0400102 0xDA, 0xDC, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3,
Jim Van Verth060d9822017-05-04 09:58:17 -0400103 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
104 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF0, 0xF1, 0xF2,
105 0xF3, 0xF3, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF7,
106 0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA,
107 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
Mike Reed37f16552017-05-02 15:13:43 -0400108};
109
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500110void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
Mike Reed37f16552017-05-02 15:13:43 -0400111 // to re-build the table, call build_table() which will dump it out using SkDebugf.
112 if (false) {
113 build_table();
114 }
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500115 for (int i = 0; i < count; ++i) {
116 SkPMColor c = src[i];
Jim Van Verth060d9822017-05-04 09:58:17 -0400117 uint8_t a = gByteExpU8Table[SkGetPackedA32(c)];
Brian Salomon0bd699e2017-02-01 12:23:25 -0500118 dst[i] = SkPackARGB32(a, a, a, a);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500119 }
120}
121
Mike Reedb9641bd2017-05-04 10:57:40 -0400122void SkGaussianColorFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const {
123 for (int i = 0; i < count; ++i) {
124 float v = eval_gaussian(src[i].a());
125 dst[i] = SkPM4f::FromPremulRGBA(v, v, v, v);
126 }
127}
128
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500129sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
130 return Make();
131}
132
133#ifndef SK_IGNORE_TO_STRING
134void SkGaussianColorFilter::toString(SkString* str) const {
135 str->append("SkGaussianColorFilter ");
136}
137#endif
138
139#if SK_SUPPORT_GPU
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500140
141sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
142 SkColorSpace*) const {
143 return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
144}
145#endif
146
147///////////////////////////////////////////////////////////////////////////////////////////////////
Brian Salomon5e689522017-02-01 12:07:17 -0500148
149namespace {
150
Brian Salomonbc9956d2017-02-22 13:49:09 -0500151uint64_t resource_cache_shared_id() {
152 return 0x2020776f64616873llu; // 'shadow '
153}
154
Brian Salomond1ac9822017-02-03 14:25:02 -0500155/** Factory for an ambient shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500156struct AmbientVerticesFactory {
Jim Van Verthb4366552017-03-27 14:25:29 -0400157 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
Brian Salomon5e689522017-02-01 12:07:17 -0500158 bool fTransparent;
159
Brian Salomond1ac9822017-02-03 14:25:02 -0500160 bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
Jim Van Verth060d9822017-05-04 09:58:17 -0400161 if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500162 return false;
163 }
164 translate->set(0, 0);
165 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500166 }
Brian Salomon5e689522017-02-01 12:07:17 -0500167
Brian Salomonaff27a22017-02-06 15:47:44 -0500168 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
Jim Van Verthb4366552017-03-27 14:25:29 -0400169 SkScalar z = fOccluderHeight;
170 return SkShadowTessellator::MakeAmbient(path, ctm,
171 [z](SkScalar, SkScalar) { return z; },
Jim Van Verth060d9822017-05-04 09:58:17 -0400172 fTransparent);
Brian Salomon5e689522017-02-01 12:07:17 -0500173 }
174};
175
Brian Salomond1ac9822017-02-03 14:25:02 -0500176/** Factory for an spot shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500177struct SpotVerticesFactory {
Brian Salomond1ac9822017-02-03 14:25:02 -0500178 enum class OccluderType {
179 // The umbra cannot be dropped out because the occluder is not opaque.
180 kTransparent,
181 // The umbra can be dropped where it is occluded.
182 kOpaque,
183 // It is known that the entire umbra is occluded.
184 kOpaqueCoversUmbra
185 };
186
Brian Salomon5e689522017-02-01 12:07:17 -0500187 SkVector fOffset;
Jim Van Verthb4366552017-03-27 14:25:29 -0400188 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
189 SkPoint3 fDevLightPos;
190 SkScalar fLightRadius;
Brian Salomond1ac9822017-02-03 14:25:02 -0500191 OccluderType fOccluderType;
Brian Salomon5e689522017-02-01 12:07:17 -0500192
Brian Salomond1ac9822017-02-03 14:25:02 -0500193 bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
Jim Van Verthb4366552017-03-27 14:25:29 -0400194 if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
Jim Van Verth060d9822017-05-04 09:58:17 -0400195 fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500196 return false;
197 }
198 switch (fOccluderType) {
199 case OccluderType::kTransparent:
200 case OccluderType::kOpaqueCoversUmbra:
201 // 'this' and 'that' will either both have no umbra removed or both have all the
202 // umbra removed.
203 *translate = that.fOffset - fOffset;
204 return true;
205 case OccluderType::kOpaque:
206 // In this case we partially remove the umbra differently for 'this' and 'that'
207 // if the offsets don't match.
208 if (fOffset == that.fOffset) {
209 translate->set(0, 0);
210 return true;
211 }
212 return false;
213 }
214 SkFAIL("Uninitialized occluder type?");
215 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500216 }
Brian Salomon5e689522017-02-01 12:07:17 -0500217
Brian Salomonaff27a22017-02-06 15:47:44 -0500218 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500219 bool transparent = OccluderType::kTransparent == fOccluderType;
Jim Van Verthb4366552017-03-27 14:25:29 -0400220 SkScalar z = fOccluderHeight;
221 return SkShadowTessellator::MakeSpot(path, ctm,
222 [z](SkScalar, SkScalar) -> SkScalar { return z; },
Jim Van Verth060d9822017-05-04 09:58:17 -0400223 fDevLightPos, fLightRadius, transparent);
Brian Salomon5e689522017-02-01 12:07:17 -0500224 }
225};
226
227/**
Brian Salomond1ac9822017-02-03 14:25:02 -0500228 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
229 * records are immutable this is not itself a Rec. When we need to update it we return this on
230 * the FindVisitor and let the cache destory the Rec. We'll update the tessellations and then add
231 * a new Rec with an adjusted size for any deletions/additions.
Brian Salomon5e689522017-02-01 12:07:17 -0500232 */
Brian Salomond1ac9822017-02-03 14:25:02 -0500233class CachedTessellations : public SkRefCnt {
Brian Salomon5e689522017-02-01 12:07:17 -0500234public:
Brian Salomond1ac9822017-02-03 14:25:02 -0500235 size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
236
Brian Salomonaff27a22017-02-06 15:47:44 -0500237 sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
238 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500239 return fAmbientSet.find(ambient, matrix, translate);
240 }
241
Brian Salomonaff27a22017-02-06 15:47:44 -0500242 sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
243 const SkMatrix& matrix) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500244 return fAmbientSet.add(devPath, ambient, matrix);
245 }
246
Brian Salomonaff27a22017-02-06 15:47:44 -0500247 sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
248 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500249 return fSpotSet.find(spot, matrix, translate);
250 }
251
Brian Salomonaff27a22017-02-06 15:47:44 -0500252 sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
253 const SkMatrix& matrix) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500254 return fSpotSet.add(devPath, spot, matrix);
255 }
256
257private:
258 template <typename FACTORY, int MAX_ENTRIES>
259 class Set {
260 public:
261 size_t size() const { return fSize; }
262
Brian Salomonaff27a22017-02-06 15:47:44 -0500263 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
264 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500265 for (int i = 0; i < MAX_ENTRIES; ++i) {
266 if (fEntries[i].fFactory.isCompatible(factory, translate)) {
267 const SkMatrix& m = fEntries[i].fMatrix;
268 if (matrix.hasPerspective() || m.hasPerspective()) {
269 if (matrix != fEntries[i].fMatrix) {
270 continue;
271 }
272 } else if (matrix.getScaleX() != m.getScaleX() ||
273 matrix.getSkewX() != m.getSkewX() ||
274 matrix.getScaleY() != m.getScaleY() ||
275 matrix.getSkewY() != m.getSkewY()) {
276 continue;
277 }
278 *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(),
279 matrix.getTranslateY() - m.getTranslateY()};
280 return fEntries[i].fVertices;
281 }
282 }
283 return nullptr;
284 }
285
Brian Salomonaff27a22017-02-06 15:47:44 -0500286 sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) {
287 sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix);
Brian Salomond1ac9822017-02-03 14:25:02 -0500288 if (!vertices) {
289 return nullptr;
290 }
291 int i;
292 if (fCount < MAX_ENTRIES) {
293 i = fCount++;
294 } else {
295 i = gRandom.nextULessThan(MAX_ENTRIES);
Mike Reedaa9e3322017-03-16 14:38:48 -0400296 fSize -= fEntries[i].fVertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500297 }
298 fEntries[i].fFactory = factory;
299 fEntries[i].fVertices = vertices;
300 fEntries[i].fMatrix = matrix;
Mike Reedaa9e3322017-03-16 14:38:48 -0400301 fSize += vertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500302 return vertices;
303 }
304
305 private:
306 struct Entry {
307 FACTORY fFactory;
Brian Salomonaff27a22017-02-06 15:47:44 -0500308 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500309 SkMatrix fMatrix;
310 };
311 Entry fEntries[MAX_ENTRIES];
312 int fCount = 0;
313 size_t fSize = 0;
314 };
315
316 Set<AmbientVerticesFactory, 4> fAmbientSet;
317 Set<SpotVerticesFactory, 4> fSpotSet;
318
319 static SkRandom gRandom;
320};
321
322SkRandom CachedTessellations::gRandom;
323
324/**
325 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
326 * path. The key represents the path's geometry and not any shadow params.
327 */
328class CachedTessellationsRec : public SkResourceCache::Rec {
329public:
330 CachedTessellationsRec(const SkResourceCache::Key& key,
331 sk_sp<CachedTessellations> tessellations)
332 : fTessellations(std::move(tessellations)) {
Brian Salomon5e689522017-02-01 12:07:17 -0500333 fKey.reset(new uint8_t[key.size()]);
334 memcpy(fKey.get(), &key, key.size());
335 }
336
337 const Key& getKey() const override {
338 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
339 }
Brian Salomon5e689522017-02-01 12:07:17 -0500340
Brian Salomond1ac9822017-02-03 14:25:02 -0500341 size_t bytesUsed() const override { return fTessellations->size(); }
Brian Salomon5e689522017-02-01 12:07:17 -0500342
Brian Salomond1ac9822017-02-03 14:25:02 -0500343 const char* getCategory() const override { return "tessellated shadow masks"; }
Brian Salomon5e689522017-02-01 12:07:17 -0500344
Brian Salomond1ac9822017-02-03 14:25:02 -0500345 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
Brian Salomon5e689522017-02-01 12:07:17 -0500346
Brian Salomond1ac9822017-02-03 14:25:02 -0500347 template <typename FACTORY>
Brian Salomonaff27a22017-02-06 15:47:44 -0500348 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
349 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500350 return fTessellations->find(factory, matrix, translate);
351 }
Brian Salomon5e689522017-02-01 12:07:17 -0500352
353private:
354 std::unique_ptr<uint8_t[]> fKey;
Brian Salomond1ac9822017-02-03 14:25:02 -0500355 sk_sp<CachedTessellations> fTessellations;
Brian Salomon5e689522017-02-01 12:07:17 -0500356};
357
358/**
359 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
Brian Salomond1ac9822017-02-03 14:25:02 -0500360 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
361 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
362 * to the caller. The caller will update it and reinsert it back into the cache.
Brian Salomon5e689522017-02-01 12:07:17 -0500363 */
364template <typename FACTORY>
365struct FindContext {
366 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
367 : fViewMatrix(viewMatrix), fFactory(factory) {}
Brian Salomond1ac9822017-02-03 14:25:02 -0500368 const SkMatrix* const fViewMatrix;
369 // If this is valid after Find is called then we found the vertices and they should be drawn
370 // with fTranslate applied.
Brian Salomonaff27a22017-02-06 15:47:44 -0500371 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500372 SkVector fTranslate = {0, 0};
373
374 // If this is valid after Find then the caller should add the vertices to the tessellation set
375 // and create a new CachedTessellationsRec and insert it into SkResourceCache.
376 sk_sp<CachedTessellations> fTessellationsOnFailure;
377
Brian Salomon5e689522017-02-01 12:07:17 -0500378 const FACTORY* fFactory;
379};
380
381/**
382 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
383 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
384 * necessary translation vector are set on the FindContext.
385 */
386template <typename FACTORY>
387bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
388 FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
Brian Salomond1ac9822017-02-03 14:25:02 -0500389 const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
390 findContext->fVertices =
391 rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
392 if (findContext->fVertices) {
393 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500394 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500395 // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
396 // manipulated we will add a new Rec.
397 findContext->fTessellationsOnFailure = rec.refTessellations();
398 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500399}
400
401class ShadowedPath {
402public:
403 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
Jim Van Vertha84898d2017-02-06 13:38:23 -0500404 : fPath(path)
Brian Salomon5e689522017-02-01 12:07:17 -0500405 , fViewMatrix(viewMatrix)
406#if SK_SUPPORT_GPU
407 , fShapeForKey(*path, GrStyle::SimpleFill())
408#endif
409 {}
410
Jim Van Vertha84898d2017-02-06 13:38:23 -0500411 const SkPath& path() const { return *fPath; }
Brian Salomon5e689522017-02-01 12:07:17 -0500412 const SkMatrix& viewMatrix() const { return *fViewMatrix; }
413#if SK_SUPPORT_GPU
414 /** Negative means the vertices should not be cached for this path. */
415 int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
416 void writeKey(void* key) const {
417 fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
418 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500419 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
Brian Salomon5e689522017-02-01 12:07:17 -0500420#else
421 int keyBytes() const { return -1; }
422 void writeKey(void* key) const { SkFAIL("Should never be called"); }
Brian Salomond1ac9822017-02-03 14:25:02 -0500423 bool isRRect(SkRRect* rrect) { return false; }
Brian Salomon5e689522017-02-01 12:07:17 -0500424#endif
425
426private:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500427 const SkPath* fPath;
Brian Salomon5e689522017-02-01 12:07:17 -0500428 const SkMatrix* fViewMatrix;
429#if SK_SUPPORT_GPU
430 GrShape fShapeForKey;
431#endif
Brian Salomon5e689522017-02-01 12:07:17 -0500432};
433
Brian Salomond1ac9822017-02-03 14:25:02 -0500434// This creates a domain of keys in SkResourceCache used by this file.
435static void* kNamespace;
436
Brian Salomon5e689522017-02-01 12:07:17 -0500437/**
438 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
439 * they are first found in SkResourceCache.
440 */
441template <typename FACTORY>
Brian Salomon804e0912017-02-23 09:34:03 -0500442void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color,
443 SkResourceCache* cache) {
Brian Salomon5e689522017-02-01 12:07:17 -0500444 FindContext<FACTORY> context(&path.viewMatrix(), &factory);
Brian Salomon5e689522017-02-01 12:07:17 -0500445
446 SkResourceCache::Key* key = nullptr;
447 SkAutoSTArray<32 * 4, uint8_t> keyStorage;
448 int keyDataBytes = path.keyBytes();
449 if (keyDataBytes >= 0) {
450 keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
451 key = new (keyStorage.begin()) SkResourceCache::Key();
452 path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
Brian Salomonbc9956d2017-02-22 13:49:09 -0500453 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
Brian Salomon804e0912017-02-23 09:34:03 -0500454 if (cache) {
455 cache->find(*key, FindVisitor<FACTORY>, &context);
456 } else {
457 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
458 }
Brian Salomon5e689522017-02-01 12:07:17 -0500459 }
460
Brian Salomonaff27a22017-02-06 15:47:44 -0500461 sk_sp<SkVertices> vertices;
Brian Salomon5e689522017-02-01 12:07:17 -0500462 const SkVector* translate;
463 static constexpr SkVector kZeroTranslate = {0, 0};
464 bool foundInCache = SkToBool(context.fVertices);
465 if (foundInCache) {
466 vertices = std::move(context.fVertices);
467 translate = &context.fTranslate;
468 } else {
469 // TODO: handle transforming the path as part of the tessellator
Brian Salomond1ac9822017-02-03 14:25:02 -0500470 if (key) {
471 // Update or initialize a tessellation set and add it to the cache.
472 sk_sp<CachedTessellations> tessellations;
473 if (context.fTessellationsOnFailure) {
474 tessellations = std::move(context.fTessellationsOnFailure);
475 } else {
476 tessellations.reset(new CachedTessellations());
477 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500478 vertices = tessellations->add(path.path(), factory, path.viewMatrix());
Brian Salomond1ac9822017-02-03 14:25:02 -0500479 if (!vertices) {
480 return;
481 }
Brian Salomon804e0912017-02-23 09:34:03 -0500482 auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
483 if (cache) {
484 cache->add(rec);
485 } else {
486 SkResourceCache::Add(rec);
487 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500488 } else {
Jim Van Vertha84898d2017-02-06 13:38:23 -0500489 vertices = factory.makeVertices(path.path(), path.viewMatrix());
Brian Salomond1ac9822017-02-03 14:25:02 -0500490 if (!vertices) {
491 return;
492 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500493 }
Brian Salomon5e689522017-02-01 12:07:17 -0500494 translate = &kZeroTranslate;
495 }
496
497 SkPaint paint;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500498 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
499 // that against our 'color' param.
500 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
501 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
502 SkGaussianColorFilter::Make()));
Brian Salomon5e689522017-02-01 12:07:17 -0500503 if (translate->fX || translate->fY) {
504 canvas->save();
505 canvas->translate(translate->fX, translate->fY);
506 }
Brian Salomonaff27a22017-02-06 15:47:44 -0500507 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
Brian Salomon5e689522017-02-01 12:07:17 -0500508 if (translate->fX || translate->fY) {
509 canvas->restore();
510 }
Brian Salomon5e689522017-02-01 12:07:17 -0500511}
512}
513
Jim Van Verthb6069df2017-04-28 11:00:35 -0400514static bool draw_analytic_shadows(SkCanvas* canvas, const SkPath& path, SkScalar occluderZ,
515 const SkPoint3& devLightPos, SkScalar lightRadius,
516 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
517 uint32_t flags) {
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400518 // only supported in GPU code
519 if (!canvas->getGrContext()) {
520 return false;
521 }
522
Jim Van Verthb6069df2017-04-28 11:00:35 -0400523 SkRect rect;
524 SkRRect rrect;
525 if (canvas->getTotalMatrix().isSimilarity()) {
526 if (path.isRect(&rect)) {
527 SkPaint newPaint;
528 newPaint.setColor(color);
529 if (ambientAlpha > 0) {
530 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
531 ambientAlpha, flags));
532 canvas->drawRect(rect, newPaint);
533 }
534 if (spotAlpha > 0) {
535 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
536 lightRadius, spotAlpha,
537 flags));
538 canvas->drawRect(rect, newPaint);
539 }
540 return true;
541 } else if (path.isRRect(&rrect) && rrect.isSimpleCircular() &&
542 rrect.radii(SkRRect::kUpperLeft_Corner).fX > SK_ScalarNearlyZero) {
543 SkPaint newPaint;
544 newPaint.setColor(color);
545 if (ambientAlpha > 0) {
546 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
547 ambientAlpha, flags));
548 canvas->drawRRect(rrect, newPaint);
549 }
550 if (spotAlpha > 0) {
551 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
552 lightRadius, spotAlpha,
553 flags));
554 canvas->drawRRect(rrect, newPaint);
555 }
556 return true;
557 } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) &&
558 rect.width() > SK_ScalarNearlyZero) {
559 SkPaint newPaint;
560 newPaint.setColor(color);
561 if (ambientAlpha > 0) {
562 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ,
563 ambientAlpha, flags));
564 canvas->drawOval(rect, newPaint);
565 }
566 if (spotAlpha > 0) {
567 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos,
568 lightRadius, spotAlpha,
569 flags));
570 canvas->drawOval(rect, newPaint);
571 }
572 return true;
573 }
574 }
575
576 return false;
577}
578
Jim Van Verth060d9822017-05-04 09:58:17 -0400579static SkColor compute_render_color(SkColor color, float alpha) {
580 return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
581 SkColorGetG(color), SkColorGetB(color));
582}
583
Jim Van Verth43475ad2017-01-13 14:37:37 -0500584// Draw an offset spot shadow and outlining ambient shadow for the given path.
585void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
Brian Salomon0bd699e2017-02-01 12:23:25 -0500586 const SkPoint3& devLightPos, SkScalar lightRadius,
Jim Van Verth43475ad2017-01-13 14:37:37 -0500587 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
Brian Salomon804e0912017-02-23 09:34:03 -0500588 uint32_t flags, SkResourceCache* cache) {
Jim Van Verthb6069df2017-04-28 11:00:35 -0400589 // try fast paths
Jim Van Verth060d9822017-05-04 09:58:17 -0400590 bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag);
591 if (!skipAnalytic && draw_analytic_shadows(canvas, path, occluderHeight, devLightPos,
592 lightRadius, ambientAlpha, spotAlpha, color,
593 flags)) {
Jim Van Verthcf40e302017-03-02 11:28:43 -0500594 return;
595 }
596
Jim Van Verthb6069df2017-04-28 11:00:35 -0400597 SkAutoCanvasRestore acr(canvas, true);
598 SkMatrix viewMatrix = canvas->getTotalMatrix();
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500599 canvas->resetMatrix();
600
Brian Salomon5e689522017-02-01 12:07:17 -0500601 ShadowedPath shadowedPath(&path, &viewMatrix);
602
Brian Salomon958fbc42017-01-30 17:01:28 -0500603 bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
604
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500605 if (ambientAlpha > 0) {
Brian Salomon0bd699e2017-02-01 12:23:25 -0500606 ambientAlpha = SkTMin(ambientAlpha, 1.f);
Brian Salomon5e689522017-02-01 12:07:17 -0500607 AmbientVerticesFactory factory;
Jim Van Verthb4366552017-03-27 14:25:29 -0400608 factory.fOccluderHeight = occluderHeight;
Brian Salomon5e689522017-02-01 12:07:17 -0500609 factory.fTransparent = transparent;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500610
Jim Van Verth060d9822017-05-04 09:58:17 -0400611 SkColor renderColor = compute_render_color(color, ambientAlpha);
612 draw_shadow(factory, canvas, shadowedPath, renderColor, cache);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500613 }
614
615 if (spotAlpha > 0) {
Brian Salomon0bd699e2017-02-01 12:23:25 -0500616 spotAlpha = SkTMin(spotAlpha, 1.f);
Brian Salomon5e689522017-02-01 12:07:17 -0500617 SpotVerticesFactory factory;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500618 float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
Jim Van Verthb4366552017-03-27 14:25:29 -0400619 SkScalar radius = lightRadius * zRatio;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500620
621 // Compute the scale and translation for the spot shadow.
Jim Van Verthb4366552017-03-27 14:25:29 -0400622 SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500623
624 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Brian Salomon0bd699e2017-02-01 12:23:25 -0500625 viewMatrix.mapPoints(&center, 1);
626 factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
627 zRatio * (center.fY - devLightPos.fY));
Jim Van Verthb4366552017-03-27 14:25:29 -0400628 factory.fOccluderHeight = occluderHeight;
629 factory.fDevLightPos = devLightPos;
630 factory.fLightRadius = lightRadius;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500631
Brian Salomond1ac9822017-02-03 14:25:02 -0500632 SkRRect rrect;
633 if (transparent) {
634 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
635 } else {
636 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque;
637 if (shadowedPath.isRRect(&rrect)) {
638 SkRRect devRRect;
639 if (rrect.transform(viewMatrix, &devRRect)) {
Jim Van Verthb4366552017-03-27 14:25:29 -0400640 SkScalar s = 1.f - scale;
Brian Salomond1ac9822017-02-03 14:25:02 -0500641 SkScalar w = devRRect.width();
642 SkScalar h = devRRect.height();
643 SkScalar hw = w / 2.f;
644 SkScalar hh = h / 2.f;
Jim Van Verthb4366552017-03-27 14:25:29 -0400645 SkScalar umbraInsetX = s * hw + radius;
646 SkScalar umbraInsetY = s * hh + radius;
Brian Salomon67386d42017-02-06 14:23:53 -0500647 // The umbra is inset by radius along the diagonal, so adjust for that.
648 SkScalar d = 1.f / SkScalarSqrt(hw * hw + hh * hh);
649 umbraInsetX *= hw * d;
650 umbraInsetY *= hh * d;
Brian Salomond1ac9822017-02-03 14:25:02 -0500651 if (umbraInsetX > hw || umbraInsetY > hh) {
652 // There is no umbra to occlude.
653 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
654 } else if (fabsf(factory.fOffset.fX) < umbraInsetX &&
655 fabsf(factory.fOffset.fY) < umbraInsetY) {
656 factory.fOccluderType =
657 SpotVerticesFactory::OccluderType::kOpaqueCoversUmbra;
658 } else if (factory.fOffset.fX > w - umbraInsetX ||
659 factory.fOffset.fY > h - umbraInsetY) {
660 // There umbra is fully exposed, there is nothing to omit.
661 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
662 }
663 }
664 }
665 }
Jim Van Vertha84898d2017-02-06 13:38:23 -0500666 if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) {
667 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
668 }
Jim Van Verth060d9822017-05-04 09:58:17 -0400669
670 SkColor renderColor = compute_render_color(color, spotAlpha);
671 draw_shadow(factory, canvas, shadowedPath, renderColor, cache);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500672 }
Jim Van Verth43475ad2017-01-13 14:37:37 -0500673}
Jim Van Verthb4366552017-03-27 14:25:29 -0400674
675// Draw an offset spot shadow and outlining ambient shadow for the given path,
676// without caching and using a function based on local position to compute the height.
677void SkShadowUtils::DrawUncachedShadow(SkCanvas* canvas, const SkPath& path,
678 std::function<SkScalar(SkScalar, SkScalar)> heightFunc,
679 const SkPoint3& lightPos, SkScalar lightRadius,
680 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
681 uint32_t flags) {
Jim Van Verthb6069df2017-04-28 11:00:35 -0400682 // try fast paths
Jim Van Verth060d9822017-05-04 09:58:17 -0400683 bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag);
684 if (!skipAnalytic && draw_analytic_shadows(canvas, path, heightFunc(0, 0), lightPos,
685 lightRadius, ambientAlpha, spotAlpha, color,
686 flags)) {
Jim Van Verth8f7dc9f2017-04-20 15:48:37 -0400687 return;
688 }
689
Jim Van Verthb6069df2017-04-28 11:00:35 -0400690 SkAutoCanvasRestore acr(canvas, true);
691 SkMatrix viewMatrix = canvas->getTotalMatrix();
Jim Van Verthb4366552017-03-27 14:25:29 -0400692 canvas->resetMatrix();
693
694 bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
695
696 if (ambientAlpha > 0) {
697 ambientAlpha = SkTMin(ambientAlpha, 1.f);
698 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
Jim Van Verth060d9822017-05-04 09:58:17 -0400699 heightFunc, transparent);
700 SkColor renderColor = compute_render_color(color, ambientAlpha);
Jim Van Verthb4366552017-03-27 14:25:29 -0400701 SkPaint paint;
702 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
703 // result of that against our 'color' param.
704 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
Jim Van Verth060d9822017-05-04 09:58:17 -0400705 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
Jim Van Verthb4366552017-03-27 14:25:29 -0400706 SkGaussianColorFilter::Make()));
707 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
708 }
709
710 if (spotAlpha > 0) {
711 spotAlpha = SkTMin(spotAlpha, 1.f);
712 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, heightFunc,
713 lightPos, lightRadius,
Jim Van Verth060d9822017-05-04 09:58:17 -0400714 transparent);
715 SkColor renderColor = compute_render_color(color, spotAlpha);
Jim Van Verthb4366552017-03-27 14:25:29 -0400716 SkPaint paint;
717 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
718 // result of that against our 'color' param.
719 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
Jim Van Verth060d9822017-05-04 09:58:17 -0400720 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
Jim Van Verthb4366552017-03-27 14:25:29 -0400721 SkGaussianColorFilter::Make()));
722 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
723 }
724}