blob: 2cbf14a5ca948b55cfe11f630da6324598a69818 [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
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.
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 {
322 i = gRandom.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;
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)) {
Brian Salomon5e689522017-02-01 12:07:17 -0500360 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 }
Brian Salomon5e689522017-02-01 12:07:17 -0500367
Brian Salomond1ac9822017-02-03 14:25:02 -0500368 size_t bytesUsed() const override { return fTessellations->size(); }
Brian Salomon5e689522017-02-01 12:07:17 -0500369
Brian Salomond1ac9822017-02-03 14:25:02 -0500370 const char* getCategory() const override { return "tessellated shadow masks"; }
Brian Salomon5e689522017-02-01 12:07:17 -0500371
Brian Salomond1ac9822017-02-03 14:25:02 -0500372 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
Brian Salomon5e689522017-02-01 12:07:17 -0500373
Brian Salomond1ac9822017-02-03 14:25:02 -0500374 template <typename FACTORY>
Brian Salomonaff27a22017-02-06 15:47:44 -0500375 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
376 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500377 return fTessellations->find(factory, matrix, translate);
378 }
Brian Salomon5e689522017-02-01 12:07:17 -0500379
380private:
381 std::unique_ptr<uint8_t[]> fKey;
Brian Salomond1ac9822017-02-03 14:25:02 -0500382 sk_sp<CachedTessellations> fTessellations;
Brian Salomon5e689522017-02-01 12:07:17 -0500383};
384
385/**
386 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
Brian Salomond1ac9822017-02-03 14:25:02 -0500387 * 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.
Brian Salomon5e689522017-02-01 12:07:17 -0500390 */
391template <typename FACTORY>
392struct FindContext {
393 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
394 : fViewMatrix(viewMatrix), fFactory(factory) {}
Brian Salomond1ac9822017-02-03 14:25:02 -0500395 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.
Brian Salomonaff27a22017-02-06 15:47:44 -0500398 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500399 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
Brian Salomon5e689522017-02-01 12:07:17 -0500405 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;
Brian Salomond1ac9822017-02-03 14:25:02 -0500416 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;
Brian Salomon5e689522017-02-01 12:07:17 -0500421 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500422 // 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;
Brian Salomon5e689522017-02-01 12:07:17 -0500426}
427
428class ShadowedPath {
429public:
430 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
Jim Van Vertha84898d2017-02-06 13:38:23 -0500431 : fPath(path)
Brian Salomon5e689522017-02-01 12:07:17 -0500432 , fViewMatrix(viewMatrix)
433#if SK_SUPPORT_GPU
434 , fShapeForKey(*path, GrStyle::SimpleFill())
435#endif
436 {}
437
Jim Van Vertha84898d2017-02-06 13:38:23 -0500438 const SkPath& path() const { return *fPath; }
Brian Salomon5e689522017-02-01 12:07:17 -0500439 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 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500446 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
Brian Salomon5e689522017-02-01 12:07:17 -0500447#else
448 int keyBytes() const { return -1; }
449 void writeKey(void* key) const { SkFAIL("Should never be called"); }
Brian Salomond1ac9822017-02-03 14:25:02 -0500450 bool isRRect(SkRRect* rrect) { return false; }
Brian Salomon5e689522017-02-01 12:07:17 -0500451#endif
452
453private:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500454 const SkPath* fPath;
Brian Salomon5e689522017-02-01 12:07:17 -0500455 const SkMatrix* fViewMatrix;
456#if SK_SUPPORT_GPU
457 GrShape fShapeForKey;
458#endif
Brian Salomon5e689522017-02-01 12:07:17 -0500459};
460
Brian Salomond1ac9822017-02-03 14:25:02 -0500461// This creates a domain of keys in SkResourceCache used by this file.
462static void* kNamespace;
463
Brian Salomon5e689522017-02-01 12:07:17 -0500464/**
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>
Mike Reed4204da22017-05-17 08:53:36 -0400469 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) {
Brian Salomon5e689522017-02-01 12:07:17 -0500472 FindContext<FACTORY> context(&path.viewMatrix(), &factory);
Brian Salomon5e689522017-02-01 12:07:17 -0500473
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)));
Brian Salomonbc9956d2017-02-22 13:49:09 -0500481 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400482 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
Brian Salomon5e689522017-02-01 12:07:17 -0500483 }
484
Brian Salomonaff27a22017-02-06 15:47:44 -0500485 sk_sp<SkVertices> vertices;
Brian Salomon5e689522017-02-01 12:07:17 -0500486 bool foundInCache = SkToBool(context.fVertices);
487 if (foundInCache) {
488 vertices = std::move(context.fVertices);
Brian Salomon5e689522017-02-01 12:07:17 -0500489 } else {
490 // TODO: handle transforming the path as part of the tessellator
Brian Salomond1ac9822017-02-03 14:25:02 -0500491 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 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400499 vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
500 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500501 if (!vertices) {
502 return;
503 }
Brian Salomon804e0912017-02-23 09:34:03 -0500504 auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
Jim Van Verth37c5a962017-05-10 14:13:24 -0400505 SkResourceCache::Add(rec);
Brian Salomond1ac9822017-02-03 14:25:02 -0500506 } else {
Jim Van Verth8793e382017-05-22 15:52:21 -0400507 vertices = factory.makeVertices(path.path(), path.viewMatrix(),
508 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500509 if (!vertices) {
510 return;
511 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500512 }
Brian Salomon5e689522017-02-01 12:07:17 -0500513 }
514
515 SkPaint paint;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500516 // 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()));
Mike Reed4204da22017-05-17 08:53:36 -0400521
Jim Van Verth8793e382017-05-22 15:52:21 -0400522 drawProc(vertices.get(), SkBlendMode::kModulate, paint,
523 context.fTranslate.fX, context.fTranslate.fY);
Brian Salomon5e689522017-02-01 12:07:17 -0500524}
525}
526
Mike Reed4204da22017-05-17 08:53:36 -0400527static bool tilted(const SkPoint3& zPlaneParams) {
528 return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
529}
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400530
Mike Reed4204da22017-05-17 08:53:36 -0400531static 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
Jim Van Verth060d9822017-05-04 09:58:17 -0400538static SkColor compute_render_color(SkColor color, float alpha) {
539 return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
540 SkColorGetG(color), SkColorGetB(color));
541}
542
Jim Van Verth43475ad2017-01-13 14:37:37 -0500543// Draw an offset spot shadow and outlining ambient shadow for the given path.
Jim Van Verth37c5a962017-05-10 14:13:24 -0400544void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
Brian Salomon0bd699e2017-02-01 12:23:25 -0500545 const SkPoint3& devLightPos, SkScalar lightRadius,
Jim Van Verth43475ad2017-01-13 14:37:37 -0500546 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
Jim Van Verth37c5a962017-05-10 14:13:24 -0400547 uint32_t flags) {
Mike Reed4204da22017-05-17 08:53:36 -0400548 SkMatrix inverse;
549 if (!canvas->getTotalMatrix().invert(&inverse)) {
Jim Van Verthcf40e302017-03-02 11:28:43 -0500550 return;
551 }
Mike Reed4204da22017-05-17 08:53:36 -0400552 SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
Jim Van Verthcf40e302017-03-02 11:28:43 -0500553
Mike Reed4204da22017-05-17 08:53:36 -0400554 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());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500576
Brian Salomon5e689522017-02-01 12:07:17 -0500577 ShadowedPath shadowedPath(&path, &viewMatrix);
578
Mike Reed4204da22017-05-17 08:53:36 -0400579 bool tiltZPlane = tilted(rec.fZPlaneParams);
580 bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400581 bool uncached = tiltZPlane || path.isVolatile();
Brian Salomon958fbc42017-01-30 17:01:28 -0500582
Mike Reed4204da22017-05-17 08:53:36 -0400583 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;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500589 if (ambientAlpha > 0) {
Brian Salomon0bd699e2017-02-01 12:23:25 -0500590 ambientAlpha = SkTMin(ambientAlpha, 1.f);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400591 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(
Mike Reed4204da22017-05-17 08:53:36 -0400600 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
601 SkGaussianColorFilter::Make()));
602 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Brian Salomond1ac9822017-02-03 14:25:02 -0500603 } else {
Jim Van Verth37c5a962017-05-10 14:13:24 -0400604 AmbientVerticesFactory factory;
605 factory.fOccluderHeight = zPlaneParams.fZ;
606 factory.fTransparent = transparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400607 if (viewMatrix.hasPerspective()) {
608 factory.fOffset.set(0, 0);
609 } else {
610 factory.fOffset.fX = viewMatrix.getTranslateX();
611 factory.fOffset.fY = viewMatrix.getTranslateY();
612 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400613
614 SkColor renderColor = compute_render_color(color, ambientAlpha);
Mike Reed4204da22017-05-17 08:53:36 -0400615 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Brian Salomond1ac9822017-02-03 14:25:02 -0500616 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400617 }
618
Mike Reed4204da22017-05-17 08:53:36 -0400619 float spotAlpha = rec.fSpotAlpha;
Jim Van Verthb4366552017-03-27 14:25:29 -0400620 if (spotAlpha > 0) {
621 spotAlpha = SkTMin(spotAlpha, 1.f);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400622 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()));
Mike Reed4204da22017-05-17 08:53:36 -0400634 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400635 } else {
636 SpotVerticesFactory factory;
637 SkScalar occluderHeight = zPlaneParams.fZ;
638 float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
Jim Van Vertha783c362017-05-11 17:05:28 -0400639 SkScalar radius = lightRadius * zRatio;
640
Jim Van Verth78c8f302017-05-15 10:44:22 -0400641 // Compute the scale and translation for the spot shadow.
642 SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400643 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verth8793e382017-05-22 15:52:21 -0400644 factory.fLocalCenter = center;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400645 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;
Jim Van Vertha783c362017-05-11 17:05:28 -0400651 SkRect devBounds;
652 viewMatrix.mapRect(&devBounds, path.getBounds());
Jim Van Verth8793e382017-05-22 15:52:21 -0400653 if (transparent ||
654 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
655 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400656 // 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
Jim Van Verth8793e382017-05-22 15:52:21 -0400658 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400659 } else if (factory.fOffset.length()*scale + scale < radius) {
Jim Van Vertha783c362017-05-11 17:05:28 -0400660 // if we don't translate more than the blur distance, can assume umbra is covered
Jim Van Verth78c8f302017-05-15 10:44:22 -0400661 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400662 } else {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400663 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400664 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400665 // need to add this after we classify the shadow
666 factory.fOffset.fX += viewMatrix.getTranslateX();
667 factory.fOffset.fY += viewMatrix.getTranslateY();
Jim Van Vertha783c362017-05-11 17:05:28 -0400668#ifdef DEBUG_SHADOW_CHECKS
669 switch (factory.fOccluderType) {
670 case SpotVerticesFactory::OccluderType::kTransparent:
671 color = 0xFFD2B48C; // tan for transparent
672 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400673 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
Jim Van Vertha783c362017-05-11 17:05:28 -0400674 color = 0xFFFFA500; // orange for opaque
675 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400676 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
677 color = 0xFFE5E500; // corn yellow for covered
Jim Van Vertha783c362017-05-11 17:05:28 -0400678 break;
679 }
680#endif
Jim Van Verth37c5a962017-05-10 14:13:24 -0400681 SkColor renderColor = compute_render_color(color, spotAlpha);
Mike Reed4204da22017-05-17 08:53:36 -0400682 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400683 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400684 }
685}