blob: d2efbe42b27c4077578dd97311879723f54c598b [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"
Mike Reed4204da22017-05-17 08:53:36 -040012#include "SkDevice.h"
13#include "SkDrawShadowRec.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050014#include "SkPath.h"
Mike Reedb9641bd2017-05-04 10:57:40 -040015#include "SkPM4f.h"
Brian Salomond1ac9822017-02-03 14:25:02 -050016#include "SkRandom.h"
Brian Salomon5e689522017-02-01 12:07:17 -050017#include "SkResourceCache.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050018#include "SkShadowTessellator.h"
Ben Wagner4d1955c2017-03-10 13:08:15 -050019#include "SkString.h"
Brian Salomon5e689522017-02-01 12:07:17 -050020#include "SkTLazy.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050021#include "SkVertices.h"
Brian Salomon5e689522017-02-01 12:07:17 -050022#if SK_SUPPORT_GPU
23#include "GrShape.h"
24#include "effects/GrBlurredEdgeFragmentProcessor.h"
Mike Reed4204da22017-05-17 08:53:36 -040025#endif
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) {
Mike Kleinbe256002017-05-08 09:46:40 -040059 // x = 1 - x;
Mike Reedb1485f22017-05-05 14:34:56 -040060 // return sk_float_exp(-x * x * 4) - 0.018f;
61
62 return 0.00030726194381713867f +
63 x*(0.15489584207534790039f +
64 x*(0.21345567703247070312f +
65 (2.89795351028442382812f - 2.26661229133605957031f*x)*x));
Mike Reedb9641bd2017-05-04 10:57:40 -040066}
67
Mike Reed37f16552017-05-02 15:13:43 -040068static void build_table() {
Jim Van Verth060d9822017-05-04 09:58:17 -040069 SkDebugf("const uint8_t gByteExpU8Table[256] = {");
Mike Reed37f16552017-05-02 15:13:43 -040070 for (int i = 0; i <= 255; ++i) {
71 if (!(i % 8)) {
72 SkDebugf("\n");
73 }
Mike Reedb9641bd2017-05-04 10:57:40 -040074 int v = (int)(eval_gaussian(i / 255.f) * 256);
Jim Van Verth060d9822017-05-04 09:58:17 -040075 SkDebugf(" 0x%02X,", v);
Mike Reed37f16552017-05-02 15:13:43 -040076 }
77 SkDebugf("\n};\n");
78}
79
Jim Van Verth060d9822017-05-04 09:58:17 -040080const uint8_t gByteExpU8Table[256] = {
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
82 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
83 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
Mike Reedb9641bd2017-05-04 10:57:40 -040084 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
Jim Van Verth060d9822017-05-04 09:58:17 -040085 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
86 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D,
87 0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11,
88 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15,
89 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B,
90 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21,
Mike Reedb9641bd2017-05-04 10:57:40 -040091 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28,
Jim Van Verth060d9822017-05-04 09:58:17 -040092 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,
Mike Reedb9641bd2017-05-04 10:57:40 -040099 0x73, 0x75, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
Jim Van Verth060d9822017-05-04 09:58:17 -0400100 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8A, 0x8C,
Mike Reedb9641bd2017-05-04 10:57:40 -0400101 0x8E, 0x90, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
Jim Van Verth060d9822017-05-04 09:58:17 -0400102 0x9C, 0x9D, 0x9F, 0xA1, 0xA2, 0xA4, 0xA6, 0xA8,
103 0xA9, 0xAB, 0xAD, 0xAE, 0xB0, 0xB2, 0xB3, 0xB5,
Mike Reedb9641bd2017-05-04 10:57:40 -0400104 0xB7, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC0, 0xC2,
105 0xC3, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE,
Jim Van Verth060d9822017-05-04 09:58:17 -0400106 0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9,
Mike Reedb9641bd2017-05-04 10:57:40 -0400107 0xDA, 0xDC, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3,
Jim Van Verth060d9822017-05-04 09:58:17 -0400108 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,
Mike Reed37f16552017-05-02 15:13:43 -0400113};
114
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500115void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
Mike Reed37f16552017-05-02 15:13:43 -0400116 // to re-build the table, call build_table() which will dump it out using SkDebugf.
117 if (false) {
118 build_table();
119 }
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500120 for (int i = 0; i < count; ++i) {
121 SkPMColor c = src[i];
Jim Van Verth060d9822017-05-04 09:58:17 -0400122 uint8_t a = gByteExpU8Table[SkGetPackedA32(c)];
Brian Salomon0bd699e2017-02-01 12:23:25 -0500123 dst[i] = SkPackARGB32(a, a, a, a);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500124 }
125}
126
Mike Reedb9641bd2017-05-04 10:57:40 -0400127void 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
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500134sk_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
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500145
146sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
147 SkColorSpace*) const {
148 return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
149}
150#endif
151
152///////////////////////////////////////////////////////////////////////////////////////////////////
Brian Salomon5e689522017-02-01 12:07:17 -0500153
154namespace {
155
Brian Salomonbc9956d2017-02-22 13:49:09 -0500156uint64_t resource_cache_shared_id() {
157 return 0x2020776f64616873llu; // 'shadow '
158}
159
Brian Salomond1ac9822017-02-03 14:25:02 -0500160/** Factory for an ambient shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500161struct AmbientVerticesFactory {
Jim Van Verthb4366552017-03-27 14:25:29 -0400162 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
Brian Salomon5e689522017-02-01 12:07:17 -0500163 bool fTransparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400164 SkVector fOffset;
Brian Salomon5e689522017-02-01 12:07:17 -0500165
Brian Salomond1ac9822017-02-03 14:25:02 -0500166 bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
Jim Van Verth060d9822017-05-04 09:58:17 -0400167 if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500168 return false;
169 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400170 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -0500171 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500172 }
Brian Salomon5e689522017-02-01 12:07:17 -0500173
Jim Van Verth8793e382017-05-22 15:52:21 -0400174 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
175 SkVector* translate) const {
Jim Van Verthe308a122017-05-08 14:19:30 -0400176 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400177 // 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);
Brian Salomon5e689522017-02-01 12:07:17 -0500185 }
186};
187
Brian Salomond1ac9822017-02-03 14:25:02 -0500188/** Factory for an spot shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500189struct SpotVerticesFactory {
Brian Salomond1ac9822017-02-03 14:25:02 -0500190 enum class OccluderType {
Jim Van Verth8793e382017-05-22 15:52:21 -0400191 // The umbra cannot be dropped out because either the occluder is not opaque,
192 // or the center of the umbra is visible.
Brian Salomond1ac9822017-02-03 14:25:02 -0500193 kTransparent,
194 // The umbra can be dropped where it is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400195 kOpaquePartialUmbra,
Brian Salomond1ac9822017-02-03 14:25:02 -0500196 // It is known that the entire umbra is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400197 kOpaqueNoUmbra
Brian Salomond1ac9822017-02-03 14:25:02 -0500198 };
199
Brian Salomon5e689522017-02-01 12:07:17 -0500200 SkVector fOffset;
Jim Van Verth8793e382017-05-22 15:52:21 -0400201 SkPoint fLocalCenter;
Jim Van Verthb4366552017-03-27 14:25:29 -0400202 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
203 SkPoint3 fDevLightPos;
204 SkScalar fLightRadius;
Brian Salomond1ac9822017-02-03 14:25:02 -0500205 OccluderType fOccluderType;
Brian Salomon5e689522017-02-01 12:07:17 -0500206
Brian Salomond1ac9822017-02-03 14:25:02 -0500207 bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
Jim Van Verthb4366552017-03-27 14:25:29 -0400208 if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
Jim Van Verth060d9822017-05-04 09:58:17 -0400209 fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500210 return false;
211 }
212 switch (fOccluderType) {
213 case OccluderType::kTransparent:
Jim Van Verth78c8f302017-05-15 10:44:22 -0400214 case OccluderType::kOpaqueNoUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500215 // 'this' and 'that' will either both have no umbra removed or both have all the
216 // umbra removed.
Jim Van Verth8793e382017-05-22 15:52:21 -0400217 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -0500218 return true;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400219 case OccluderType::kOpaquePartialUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500220 // 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;
Brian Salomon5e689522017-02-01 12:07:17 -0500230 }
Brian Salomon5e689522017-02-01 12:07:17 -0500231
Jim Van Verth8793e382017-05-22 15:52:21 -0400232 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
233 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500234 bool transparent = OccluderType::kTransparent == fOccluderType;
Jim Van Verthe308a122017-05-08 14:19:30 -0400235 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400236 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 }
Brian Salomon5e689522017-02-01 12:07:17 -0500252 }
253};
254
255/**
Brian Salomond1ac9822017-02-03 14:25:02 -0500256 * 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
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400258 * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
Brian Salomond1ac9822017-02-03 14:25:02 -0500259 * a new Rec with an adjusted size for any deletions/additions.
Brian Salomon5e689522017-02-01 12:07:17 -0500260 */
Brian Salomond1ac9822017-02-03 14:25:02 -0500261class CachedTessellations : public SkRefCnt {
Brian Salomon5e689522017-02-01 12:07:17 -0500262public:
Brian Salomond1ac9822017-02-03 14:25:02 -0500263 size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
264
Brian Salomonaff27a22017-02-06 15:47:44 -0500265 sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
266 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500267 return fAmbientSet.find(ambient, matrix, translate);
268 }
269
Brian Salomonaff27a22017-02-06 15:47:44 -0500270 sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
Jim Van Verth8793e382017-05-22 15:52:21 -0400271 const SkMatrix& matrix, SkVector* translate) {
272 return fAmbientSet.add(devPath, ambient, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500273 }
274
Brian Salomonaff27a22017-02-06 15:47:44 -0500275 sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
276 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500277 return fSpotSet.find(spot, matrix, translate);
278 }
279
Brian Salomonaff27a22017-02-06 15:47:44 -0500280 sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
Jim Van Verth8793e382017-05-22 15:52:21 -0400281 const SkMatrix& matrix, SkVector* translate) {
282 return fSpotSet.add(devPath, spot, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500283 }
284
285private:
286 template <typename FACTORY, int MAX_ENTRIES>
287 class Set {
288 public:
289 size_t size() const { return fSize; }
290
Brian Salomonaff27a22017-02-06 15:47:44 -0500291 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
292 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500293 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 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500306 return fEntries[i].fVertices;
307 }
308 }
309 return nullptr;
310 }
311
Jim Van Verth8793e382017-05-22 15:52:21 -0400312 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);
Brian Salomond1ac9822017-02-03 14:25:02 -0500315 if (!vertices) {
316 return nullptr;
317 }
318 int i;
319 if (fCount < MAX_ENTRIES) {
320 i = fCount++;
321 } else {
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400322 i = fRandom.nextULessThan(MAX_ENTRIES);
Mike Reedaa9e3322017-03-16 14:38:48 -0400323 fSize -= fEntries[i].fVertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500324 }
325 fEntries[i].fFactory = factory;
326 fEntries[i].fVertices = vertices;
327 fEntries[i].fMatrix = matrix;
Mike Reedaa9e3322017-03-16 14:38:48 -0400328 fSize += vertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500329 return vertices;
330 }
331
332 private:
333 struct Entry {
334 FACTORY fFactory;
Brian Salomonaff27a22017-02-06 15:47:44 -0500335 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500336 SkMatrix fMatrix;
337 };
338 Entry fEntries[MAX_ENTRIES];
339 int fCount = 0;
340 size_t fSize = 0;
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400341 SkRandom fRandom;
Brian Salomond1ac9822017-02-03 14:25:02 -0500342 };
343
344 Set<AmbientVerticesFactory, 4> fAmbientSet;
345 Set<SpotVerticesFactory, 4> fSpotSet;
Brian Salomond1ac9822017-02-03 14:25:02 -0500346};
347
Brian Salomond1ac9822017-02-03 14:25:02 -0500348/**
349 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
350 * path. The key represents the path's geometry and not any shadow params.
351 */
352class CachedTessellationsRec : public SkResourceCache::Rec {
353public:
354 CachedTessellationsRec(const SkResourceCache::Key& key,
355 sk_sp<CachedTessellations> tessellations)
356 : fTessellations(std::move(tessellations)) {
Brian Salomon5e689522017-02-01 12:07:17 -0500357 fKey.reset(new uint8_t[key.size()]);
358 memcpy(fKey.get(), &key, key.size());
359 }
360
361 const Key& getKey() const override {
362 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
363 }
Brian Salomon5e689522017-02-01 12:07:17 -0500364
Brian Salomond1ac9822017-02-03 14:25:02 -0500365 size_t bytesUsed() const override { return fTessellations->size(); }
Brian Salomon5e689522017-02-01 12:07:17 -0500366
Brian Salomond1ac9822017-02-03 14:25:02 -0500367 const char* getCategory() const override { return "tessellated shadow masks"; }
Brian Salomon5e689522017-02-01 12:07:17 -0500368
Brian Salomond1ac9822017-02-03 14:25:02 -0500369 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
Brian Salomon5e689522017-02-01 12:07:17 -0500370
Brian Salomond1ac9822017-02-03 14:25:02 -0500371 template <typename FACTORY>
Brian Salomonaff27a22017-02-06 15:47:44 -0500372 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
373 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500374 return fTessellations->find(factory, matrix, translate);
375 }
Brian Salomon5e689522017-02-01 12:07:17 -0500376
377private:
378 std::unique_ptr<uint8_t[]> fKey;
Brian Salomond1ac9822017-02-03 14:25:02 -0500379 sk_sp<CachedTessellations> fTessellations;
Brian Salomon5e689522017-02-01 12:07:17 -0500380};
381
382/**
383 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
Brian Salomond1ac9822017-02-03 14:25:02 -0500384 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
385 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
386 * to the caller. The caller will update it and reinsert it back into the cache.
Brian Salomon5e689522017-02-01 12:07:17 -0500387 */
388template <typename FACTORY>
389struct FindContext {
390 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
391 : fViewMatrix(viewMatrix), fFactory(factory) {}
Brian Salomond1ac9822017-02-03 14:25:02 -0500392 const SkMatrix* const fViewMatrix;
393 // If this is valid after Find is called then we found the vertices and they should be drawn
394 // with fTranslate applied.
Brian Salomonaff27a22017-02-06 15:47:44 -0500395 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500396 SkVector fTranslate = {0, 0};
397
398 // If this is valid after Find then the caller should add the vertices to the tessellation set
399 // and create a new CachedTessellationsRec and insert it into SkResourceCache.
400 sk_sp<CachedTessellations> fTessellationsOnFailure;
401
Brian Salomon5e689522017-02-01 12:07:17 -0500402 const FACTORY* fFactory;
403};
404
405/**
406 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
407 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
408 * necessary translation vector are set on the FindContext.
409 */
410template <typename FACTORY>
411bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
412 FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
Brian Salomond1ac9822017-02-03 14:25:02 -0500413 const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
414 findContext->fVertices =
415 rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
416 if (findContext->fVertices) {
417 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500418 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500419 // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
420 // manipulated we will add a new Rec.
421 findContext->fTessellationsOnFailure = rec.refTessellations();
422 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500423}
424
425class ShadowedPath {
426public:
427 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
Jim Van Vertha84898d2017-02-06 13:38:23 -0500428 : fPath(path)
Brian Salomon5e689522017-02-01 12:07:17 -0500429 , fViewMatrix(viewMatrix)
430#if SK_SUPPORT_GPU
431 , fShapeForKey(*path, GrStyle::SimpleFill())
432#endif
433 {}
434
Jim Van Vertha84898d2017-02-06 13:38:23 -0500435 const SkPath& path() const { return *fPath; }
Brian Salomon5e689522017-02-01 12:07:17 -0500436 const SkMatrix& viewMatrix() const { return *fViewMatrix; }
437#if SK_SUPPORT_GPU
438 /** Negative means the vertices should not be cached for this path. */
439 int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
440 void writeKey(void* key) const {
441 fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
442 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500443 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
Brian Salomon5e689522017-02-01 12:07:17 -0500444#else
445 int keyBytes() const { return -1; }
446 void writeKey(void* key) const { SkFAIL("Should never be called"); }
Brian Salomond1ac9822017-02-03 14:25:02 -0500447 bool isRRect(SkRRect* rrect) { return false; }
Brian Salomon5e689522017-02-01 12:07:17 -0500448#endif
449
450private:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500451 const SkPath* fPath;
Brian Salomon5e689522017-02-01 12:07:17 -0500452 const SkMatrix* fViewMatrix;
453#if SK_SUPPORT_GPU
454 GrShape fShapeForKey;
455#endif
Brian Salomon5e689522017-02-01 12:07:17 -0500456};
457
Brian Salomond1ac9822017-02-03 14:25:02 -0500458// This creates a domain of keys in SkResourceCache used by this file.
459static void* kNamespace;
460
Brian Salomon5e689522017-02-01 12:07:17 -0500461/**
462 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
463 * they are first found in SkResourceCache.
464 */
465template <typename FACTORY>
Mike Reed4204da22017-05-17 08:53:36 -0400466 void draw_shadow(const FACTORY& factory,
467 std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
468 SkScalar tx, SkScalar ty)> drawProc, ShadowedPath& path, SkColor color) {
Brian Salomon5e689522017-02-01 12:07:17 -0500469 FindContext<FACTORY> context(&path.viewMatrix(), &factory);
Brian Salomon5e689522017-02-01 12:07:17 -0500470
471 SkResourceCache::Key* key = nullptr;
472 SkAutoSTArray<32 * 4, uint8_t> keyStorage;
473 int keyDataBytes = path.keyBytes();
474 if (keyDataBytes >= 0) {
475 keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
476 key = new (keyStorage.begin()) SkResourceCache::Key();
477 path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
Brian Salomonbc9956d2017-02-22 13:49:09 -0500478 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400479 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
Brian Salomon5e689522017-02-01 12:07:17 -0500480 }
481
Brian Salomonaff27a22017-02-06 15:47:44 -0500482 sk_sp<SkVertices> vertices;
Brian Salomon5e689522017-02-01 12:07:17 -0500483 bool foundInCache = SkToBool(context.fVertices);
484 if (foundInCache) {
485 vertices = std::move(context.fVertices);
Brian Salomon5e689522017-02-01 12:07:17 -0500486 } else {
487 // TODO: handle transforming the path as part of the tessellator
Brian Salomond1ac9822017-02-03 14:25:02 -0500488 if (key) {
489 // Update or initialize a tessellation set and add it to the cache.
490 sk_sp<CachedTessellations> tessellations;
491 if (context.fTessellationsOnFailure) {
492 tessellations = std::move(context.fTessellationsOnFailure);
493 } else {
494 tessellations.reset(new CachedTessellations());
495 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400496 vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
497 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500498 if (!vertices) {
499 return;
500 }
Brian Salomon804e0912017-02-23 09:34:03 -0500501 auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
Jim Van Verth37c5a962017-05-10 14:13:24 -0400502 SkResourceCache::Add(rec);
Brian Salomond1ac9822017-02-03 14:25:02 -0500503 } else {
Jim Van Verth8793e382017-05-22 15:52:21 -0400504 vertices = factory.makeVertices(path.path(), path.viewMatrix(),
505 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500506 if (!vertices) {
507 return;
508 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500509 }
Brian Salomon5e689522017-02-01 12:07:17 -0500510 }
511
512 SkPaint paint;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500513 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
514 // that against our 'color' param.
515 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
516 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
517 SkGaussianColorFilter::Make()));
Mike Reed4204da22017-05-17 08:53:36 -0400518
Jim Van Verth8793e382017-05-22 15:52:21 -0400519 drawProc(vertices.get(), SkBlendMode::kModulate, paint,
520 context.fTranslate.fX, context.fTranslate.fY);
Brian Salomon5e689522017-02-01 12:07:17 -0500521}
522}
523
Mike Reed4204da22017-05-17 08:53:36 -0400524static bool tilted(const SkPoint3& zPlaneParams) {
525 return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
526}
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400527
Mike Reed4204da22017-05-17 08:53:36 -0400528static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
529 SkPoint3 result;
530 m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
531 result.fZ = pt.fZ;
532 return result;
533}
534
Jim Van Verth060d9822017-05-04 09:58:17 -0400535static SkColor compute_render_color(SkColor color, float alpha) {
536 return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
537 SkColorGetG(color), SkColorGetB(color));
538}
539
Jim Van Verth43475ad2017-01-13 14:37:37 -0500540// Draw an offset spot shadow and outlining ambient shadow for the given path.
Jim Van Verth37c5a962017-05-10 14:13:24 -0400541void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
Brian Salomon0bd699e2017-02-01 12:23:25 -0500542 const SkPoint3& devLightPos, SkScalar lightRadius,
Jim Van Verth43475ad2017-01-13 14:37:37 -0500543 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
Jim Van Verth37c5a962017-05-10 14:13:24 -0400544 uint32_t flags) {
Mike Reed4204da22017-05-17 08:53:36 -0400545 SkMatrix inverse;
546 if (!canvas->getTotalMatrix().invert(&inverse)) {
Jim Van Verthcf40e302017-03-02 11:28:43 -0500547 return;
548 }
Mike Reed4204da22017-05-17 08:53:36 -0400549 SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
Jim Van Verthcf40e302017-03-02 11:28:43 -0500550
Mike Reed4204da22017-05-17 08:53:36 -0400551 SkDrawShadowRec rec;
552 rec.fZPlaneParams = zPlaneParams;
553 rec.fLightPos = { pt.fX, pt.fY, devLightPos.fZ };
554 rec.fLightRadius = lightRadius;
555 rec.fAmbientAlpha = SkScalarToFloat(ambientAlpha);
556 rec.fSpotAlpha = SkScalarToFloat(spotAlpha);
557 rec.fColor = color;
558 rec.fFlags = flags;
559
560 canvas->private_draw_shadow_rec(path, rec);
561}
562
563void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
564 auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
565 SkScalar tx, SkScalar ty) {
566 SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
567 SkMatrix::MakeTrans(tx, ty)));
568 this->drawVertices(vertices, mode, paint);
569 };
570
571 SkMatrix viewMatrix = this->ctm();
572 SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500573
Brian Salomon5e689522017-02-01 12:07:17 -0500574 ShadowedPath shadowedPath(&path, &viewMatrix);
575
Mike Reed4204da22017-05-17 08:53:36 -0400576 bool tiltZPlane = tilted(rec.fZPlaneParams);
577 bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400578 bool uncached = tiltZPlane || path.isVolatile();
Brian Salomon958fbc42017-01-30 17:01:28 -0500579
Mike Reed4204da22017-05-17 08:53:36 -0400580 SkColor color = rec.fColor;
581 SkPoint3 zPlaneParams = rec.fZPlaneParams;
582 SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
583 float lightRadius = rec.fLightRadius;
584
585 float ambientAlpha = rec.fAmbientAlpha;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500586 if (ambientAlpha > 0) {
Brian Salomon0bd699e2017-02-01 12:23:25 -0500587 ambientAlpha = SkTMin(ambientAlpha, 1.f);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400588 if (uncached) {
589 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
590 zPlaneParams,
591 transparent);
592 SkColor renderColor = compute_render_color(color, ambientAlpha);
593 SkPaint paint;
594 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
595 // result of that against our 'color' param.
596 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
Mike Reed4204da22017-05-17 08:53:36 -0400597 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
598 SkGaussianColorFilter::Make()));
599 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Brian Salomond1ac9822017-02-03 14:25:02 -0500600 } else {
Jim Van Verth37c5a962017-05-10 14:13:24 -0400601 AmbientVerticesFactory factory;
602 factory.fOccluderHeight = zPlaneParams.fZ;
603 factory.fTransparent = transparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400604 if (viewMatrix.hasPerspective()) {
605 factory.fOffset.set(0, 0);
606 } else {
607 factory.fOffset.fX = viewMatrix.getTranslateX();
608 factory.fOffset.fY = viewMatrix.getTranslateY();
609 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400610
611 SkColor renderColor = compute_render_color(color, ambientAlpha);
Mike Reed4204da22017-05-17 08:53:36 -0400612 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Brian Salomond1ac9822017-02-03 14:25:02 -0500613 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400614 }
615
Mike Reed4204da22017-05-17 08:53:36 -0400616 float spotAlpha = rec.fSpotAlpha;
Jim Van Verthb4366552017-03-27 14:25:29 -0400617 if (spotAlpha > 0) {
618 spotAlpha = SkTMin(spotAlpha, 1.f);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400619 if (uncached) {
620 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
621 zPlaneParams,
622 devLightPos, lightRadius,
623 transparent);
624 SkColor renderColor = compute_render_color(color, spotAlpha);
625 SkPaint paint;
626 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
627 // result of that against our 'color' param.
628 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
629 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
630 SkGaussianColorFilter::Make()));
Mike Reed4204da22017-05-17 08:53:36 -0400631 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400632 } else {
633 SpotVerticesFactory factory;
634 SkScalar occluderHeight = zPlaneParams.fZ;
635 float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
Jim Van Vertha783c362017-05-11 17:05:28 -0400636 SkScalar radius = lightRadius * zRatio;
637
Jim Van Verth78c8f302017-05-15 10:44:22 -0400638 // Compute the scale and translation for the spot shadow.
639 SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400640 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verth8793e382017-05-22 15:52:21 -0400641 factory.fLocalCenter = center;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400642 viewMatrix.mapPoints(&center, 1);
643 factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
644 zRatio * (center.fY - devLightPos.fY));
645 factory.fOccluderHeight = occluderHeight;
646 factory.fDevLightPos = devLightPos;
647 factory.fLightRadius = lightRadius;
Jim Van Vertha783c362017-05-11 17:05:28 -0400648 SkRect devBounds;
649 viewMatrix.mapRect(&devBounds, path.getBounds());
Jim Van Verth8793e382017-05-22 15:52:21 -0400650 if (transparent ||
651 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
652 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400653 // if the translation of the shadow is big enough we're going to end up
654 // filling the entire umbra, so we can treat these as all the same
Jim Van Verth8793e382017-05-22 15:52:21 -0400655 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400656 } else if (factory.fOffset.length()*scale + scale < radius) {
Jim Van Vertha783c362017-05-11 17:05:28 -0400657 // if we don't translate more than the blur distance, can assume umbra is covered
Jim Van Verth78c8f302017-05-15 10:44:22 -0400658 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400659 } else {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400660 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400661 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400662 // need to add this after we classify the shadow
663 factory.fOffset.fX += viewMatrix.getTranslateX();
664 factory.fOffset.fY += viewMatrix.getTranslateY();
Jim Van Vertha783c362017-05-11 17:05:28 -0400665#ifdef DEBUG_SHADOW_CHECKS
666 switch (factory.fOccluderType) {
667 case SpotVerticesFactory::OccluderType::kTransparent:
668 color = 0xFFD2B48C; // tan for transparent
669 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400670 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
Jim Van Vertha783c362017-05-11 17:05:28 -0400671 color = 0xFFFFA500; // orange for opaque
672 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400673 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
674 color = 0xFFE5E500; // corn yellow for covered
Jim Van Vertha783c362017-05-11 17:05:28 -0400675 break;
676 }
677#endif
Jim Van Verth37c5a962017-05-10 14:13:24 -0400678 SkColor renderColor = compute_render_color(color, spotAlpha);
Mike Reed4204da22017-05-17 08:53:36 -0400679 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400680 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400681 }
682}