blob: c5b04f311bb2db5ca543b951541b9d382878ffed [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"
Mike Reed65331592017-05-24 16:45:34 -040017#include "SkRasterPipeline.h"
Brian Salomon5e689522017-02-01 12:07:17 -050018#include "SkResourceCache.h"
Jim Van Verthefe3ded2017-01-30 13:11:45 -050019#include "SkShadowTessellator.h"
Ben Wagner4d1955c2017-03-10 13:08:15 -050020#include "SkString.h"
Brian Salomon5e689522017-02-01 12:07:17 -050021#include "SkTLazy.h"
Brian Salomonaff27a22017-02-06 15:47:44 -050022#include "SkVertices.h"
Brian Salomon5e689522017-02-01 12:07:17 -050023#if SK_SUPPORT_GPU
24#include "GrShape.h"
25#include "effects/GrBlurredEdgeFragmentProcessor.h"
Mike Reed4204da22017-05-17 08:53:36 -040026#endif
Jim Van Verthefe3ded2017-01-30 13:11:45 -050027
28/**
29* Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
30* then blends with the color's G value.
31* Final result is black with alpha of Gaussian(B)*G.
32* The assumption is that the original color's alpha is 1.
33*/
34class SK_API SkGaussianColorFilter : public SkColorFilter {
35public:
36 static sk_sp<SkColorFilter> Make() {
37 return sk_sp<SkColorFilter>(new SkGaussianColorFilter);
38 }
39
40 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
41
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 {}
Mike Reed65331592017-05-24 16:45:34 -040051 void onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc,
52 bool shaderIsOpaque) const override {
53 pipeline->append(SkRasterPipeline::gauss_a_to_rgba);
54 }
Jim Van Verthefe3ded2017-01-30 13:11:45 -050055private:
56 SkGaussianColorFilter() : INHERITED() {}
57
58 typedef SkColorFilter INHERITED;
59};
60
Mike Reedb9641bd2017-05-04 10:57:40 -040061static inline float eval_gaussian(float x) {
Mike Kleinbe256002017-05-08 09:46:40 -040062 // x = 1 - x;
Mike Reedb1485f22017-05-05 14:34:56 -040063 // return sk_float_exp(-x * x * 4) - 0.018f;
64
65 return 0.00030726194381713867f +
66 x*(0.15489584207534790039f +
67 x*(0.21345567703247070312f +
68 (2.89795351028442382812f - 2.26661229133605957031f*x)*x));
Mike Reedb9641bd2017-05-04 10:57:40 -040069}
70
Mike Reed37f16552017-05-02 15:13:43 -040071static void build_table() {
Jim Van Verth060d9822017-05-04 09:58:17 -040072 SkDebugf("const uint8_t gByteExpU8Table[256] = {");
Mike Reed37f16552017-05-02 15:13:43 -040073 for (int i = 0; i <= 255; ++i) {
74 if (!(i % 8)) {
75 SkDebugf("\n");
76 }
Mike Reedb9641bd2017-05-04 10:57:40 -040077 int v = (int)(eval_gaussian(i / 255.f) * 256);
Jim Van Verth060d9822017-05-04 09:58:17 -040078 SkDebugf(" 0x%02X,", v);
Mike Reed37f16552017-05-02 15:13:43 -040079 }
80 SkDebugf("\n};\n");
81}
82
Jim Van Verth060d9822017-05-04 09:58:17 -040083const uint8_t gByteExpU8Table[256] = {
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
85 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
86 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
Mike Reedb9641bd2017-05-04 10:57:40 -040087 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
Jim Van Verth060d9822017-05-04 09:58:17 -040088 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
89 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D,
90 0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11,
91 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15,
92 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B,
93 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21,
Mike Reedb9641bd2017-05-04 10:57:40 -040094 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28,
Jim Van Verth060d9822017-05-04 09:58:17 -040095 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
96 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39,
97 0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42,
98 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D,
99 0x4E, 0x50, 0x51, 0x53, 0x54, 0x55, 0x57, 0x58,
100 0x5A, 0x5B, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64,
101 0x66, 0x68, 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71,
Mike Reedb9641bd2017-05-04 10:57:40 -0400102 0x73, 0x75, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F,
Jim Van Verth060d9822017-05-04 09:58:17 -0400103 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8A, 0x8C,
Mike Reedb9641bd2017-05-04 10:57:40 -0400104 0x8E, 0x90, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
Jim Van Verth060d9822017-05-04 09:58:17 -0400105 0x9C, 0x9D, 0x9F, 0xA1, 0xA2, 0xA4, 0xA6, 0xA8,
106 0xA9, 0xAB, 0xAD, 0xAE, 0xB0, 0xB2, 0xB3, 0xB5,
Mike Reedb9641bd2017-05-04 10:57:40 -0400107 0xB7, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC0, 0xC2,
108 0xC3, 0xC5, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE,
Jim Van Verth060d9822017-05-04 09:58:17 -0400109 0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9,
Mike Reedb9641bd2017-05-04 10:57:40 -0400110 0xDA, 0xDC, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3,
Jim Van Verth060d9822017-05-04 09:58:17 -0400111 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
112 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF0, 0xF1, 0xF2,
113 0xF3, 0xF3, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF7,
114 0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA,
115 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
Mike Reed37f16552017-05-02 15:13:43 -0400116};
117
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500118void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
Mike Reed37f16552017-05-02 15:13:43 -0400119 // to re-build the table, call build_table() which will dump it out using SkDebugf.
120 if (false) {
121 build_table();
122 }
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500123 for (int i = 0; i < count; ++i) {
124 SkPMColor c = src[i];
Jim Van Verth060d9822017-05-04 09:58:17 -0400125 uint8_t a = gByteExpU8Table[SkGetPackedA32(c)];
Brian Salomon0bd699e2017-02-01 12:23:25 -0500126 dst[i] = SkPackARGB32(a, a, a, a);
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500127 }
128}
129
130sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) {
131 return Make();
132}
133
134#ifndef SK_IGNORE_TO_STRING
135void SkGaussianColorFilter::toString(SkString* str) const {
136 str->append("SkGaussianColorFilter ");
137}
138#endif
139
140#if SK_SUPPORT_GPU
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500141
142sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
143 SkColorSpace*) const {
144 return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
145}
146#endif
147
148///////////////////////////////////////////////////////////////////////////////////////////////////
Brian Salomon5e689522017-02-01 12:07:17 -0500149
150namespace {
151
Brian Salomonbc9956d2017-02-22 13:49:09 -0500152uint64_t resource_cache_shared_id() {
153 return 0x2020776f64616873llu; // 'shadow '
154}
155
Brian Salomond1ac9822017-02-03 14:25:02 -0500156/** Factory for an ambient shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500157struct AmbientVerticesFactory {
Jim Van Verthb4366552017-03-27 14:25:29 -0400158 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
Brian Salomon5e689522017-02-01 12:07:17 -0500159 bool fTransparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400160 SkVector fOffset;
Brian Salomon5e689522017-02-01 12:07:17 -0500161
Brian Salomond1ac9822017-02-03 14:25:02 -0500162 bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
Jim Van Verth060d9822017-05-04 09:58:17 -0400163 if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500164 return false;
165 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400166 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -0500167 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500168 }
Brian Salomon5e689522017-02-01 12:07:17 -0500169
Jim Van Verth8793e382017-05-22 15:52:21 -0400170 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
171 SkVector* translate) const {
Jim Van Verthe308a122017-05-08 14:19:30 -0400172 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400173 // pick a canonical place to generate shadow
174 SkMatrix noTrans(ctm);
175 if (!ctm.hasPerspective()) {
176 noTrans[SkMatrix::kMTransX] = 0;
177 noTrans[SkMatrix::kMTransY] = 0;
178 }
179 *translate = fOffset;
180 return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
Brian Salomon5e689522017-02-01 12:07:17 -0500181 }
182};
183
Brian Salomond1ac9822017-02-03 14:25:02 -0500184/** Factory for an spot shadow mesh with particular shadow properties. */
Brian Salomon5e689522017-02-01 12:07:17 -0500185struct SpotVerticesFactory {
Brian Salomond1ac9822017-02-03 14:25:02 -0500186 enum class OccluderType {
Jim Van Verth8793e382017-05-22 15:52:21 -0400187 // The umbra cannot be dropped out because either the occluder is not opaque,
188 // or the center of the umbra is visible.
Brian Salomond1ac9822017-02-03 14:25:02 -0500189 kTransparent,
190 // The umbra can be dropped where it is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400191 kOpaquePartialUmbra,
Brian Salomond1ac9822017-02-03 14:25:02 -0500192 // It is known that the entire umbra is occluded.
Jim Van Verth78c8f302017-05-15 10:44:22 -0400193 kOpaqueNoUmbra
Brian Salomond1ac9822017-02-03 14:25:02 -0500194 };
195
Brian Salomon5e689522017-02-01 12:07:17 -0500196 SkVector fOffset;
Jim Van Verth8793e382017-05-22 15:52:21 -0400197 SkPoint fLocalCenter;
Jim Van Verthb4366552017-03-27 14:25:29 -0400198 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
199 SkPoint3 fDevLightPos;
200 SkScalar fLightRadius;
Brian Salomond1ac9822017-02-03 14:25:02 -0500201 OccluderType fOccluderType;
Brian Salomon5e689522017-02-01 12:07:17 -0500202
Brian Salomond1ac9822017-02-03 14:25:02 -0500203 bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const {
Jim Van Verthb4366552017-03-27 14:25:29 -0400204 if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ ||
Jim Van Verth060d9822017-05-04 09:58:17 -0400205 fLightRadius != that.fLightRadius || fOccluderType != that.fOccluderType) {
Brian Salomond1ac9822017-02-03 14:25:02 -0500206 return false;
207 }
208 switch (fOccluderType) {
209 case OccluderType::kTransparent:
Jim Van Verth78c8f302017-05-15 10:44:22 -0400210 case OccluderType::kOpaqueNoUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500211 // 'this' and 'that' will either both have no umbra removed or both have all the
212 // umbra removed.
Jim Van Verth8793e382017-05-22 15:52:21 -0400213 *translate = that.fOffset;
Brian Salomond1ac9822017-02-03 14:25:02 -0500214 return true;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400215 case OccluderType::kOpaquePartialUmbra:
Brian Salomond1ac9822017-02-03 14:25:02 -0500216 // In this case we partially remove the umbra differently for 'this' and 'that'
217 // if the offsets don't match.
218 if (fOffset == that.fOffset) {
219 translate->set(0, 0);
220 return true;
221 }
222 return false;
223 }
224 SkFAIL("Uninitialized occluder type?");
225 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500226 }
Brian Salomon5e689522017-02-01 12:07:17 -0500227
Jim Van Verth8793e382017-05-22 15:52:21 -0400228 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
229 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500230 bool transparent = OccluderType::kTransparent == fOccluderType;
Jim Van Verthe308a122017-05-08 14:19:30 -0400231 SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
Jim Van Verth8793e382017-05-22 15:52:21 -0400232 if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
233 translate->set(0, 0);
234 return SkShadowTessellator::MakeSpot(path, ctm, zParams,
235 fDevLightPos, fLightRadius, transparent);
236 } else {
237 // pick a canonical place to generate shadow, with light centered over path
238 SkMatrix noTrans(ctm);
239 noTrans[SkMatrix::kMTransX] = 0;
240 noTrans[SkMatrix::kMTransY] = 0;
241 SkPoint devCenter(fLocalCenter);
242 noTrans.mapPoints(&devCenter, 1);
243 SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
244 *translate = fOffset;
245 return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
246 centerLightPos, fLightRadius, transparent);
247 }
Brian Salomon5e689522017-02-01 12:07:17 -0500248 }
249};
250
251/**
Brian Salomond1ac9822017-02-03 14:25:02 -0500252 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache
253 * 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 -0400254 * the FindVisitor and let the cache destroy the Rec. We'll update the tessellations and then add
Brian Salomond1ac9822017-02-03 14:25:02 -0500255 * a new Rec with an adjusted size for any deletions/additions.
Brian Salomon5e689522017-02-01 12:07:17 -0500256 */
Brian Salomond1ac9822017-02-03 14:25:02 -0500257class CachedTessellations : public SkRefCnt {
Brian Salomon5e689522017-02-01 12:07:17 -0500258public:
Brian Salomond1ac9822017-02-03 14:25:02 -0500259 size_t size() const { return fAmbientSet.size() + fSpotSet.size(); }
260
Brian Salomonaff27a22017-02-06 15:47:44 -0500261 sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix,
262 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500263 return fAmbientSet.find(ambient, matrix, translate);
264 }
265
Brian Salomonaff27a22017-02-06 15:47:44 -0500266 sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
Jim Van Verth8793e382017-05-22 15:52:21 -0400267 const SkMatrix& matrix, SkVector* translate) {
268 return fAmbientSet.add(devPath, ambient, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500269 }
270
Brian Salomonaff27a22017-02-06 15:47:44 -0500271 sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
272 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500273 return fSpotSet.find(spot, matrix, translate);
274 }
275
Brian Salomonaff27a22017-02-06 15:47:44 -0500276 sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
Jim Van Verth8793e382017-05-22 15:52:21 -0400277 const SkMatrix& matrix, SkVector* translate) {
278 return fSpotSet.add(devPath, spot, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500279 }
280
281private:
282 template <typename FACTORY, int MAX_ENTRIES>
283 class Set {
284 public:
285 size_t size() const { return fSize; }
286
Brian Salomonaff27a22017-02-06 15:47:44 -0500287 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
288 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500289 for (int i = 0; i < MAX_ENTRIES; ++i) {
290 if (fEntries[i].fFactory.isCompatible(factory, translate)) {
291 const SkMatrix& m = fEntries[i].fMatrix;
292 if (matrix.hasPerspective() || m.hasPerspective()) {
293 if (matrix != fEntries[i].fMatrix) {
294 continue;
295 }
296 } else if (matrix.getScaleX() != m.getScaleX() ||
297 matrix.getSkewX() != m.getSkewX() ||
298 matrix.getScaleY() != m.getScaleY() ||
299 matrix.getSkewY() != m.getSkewY()) {
300 continue;
301 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500302 return fEntries[i].fVertices;
303 }
304 }
305 return nullptr;
306 }
307
Jim Van Verth8793e382017-05-22 15:52:21 -0400308 sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
309 SkVector* translate) {
310 sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500311 if (!vertices) {
312 return nullptr;
313 }
314 int i;
315 if (fCount < MAX_ENTRIES) {
316 i = fCount++;
317 } else {
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400318 i = fRandom.nextULessThan(MAX_ENTRIES);
Mike Reedaa9e3322017-03-16 14:38:48 -0400319 fSize -= fEntries[i].fVertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500320 }
321 fEntries[i].fFactory = factory;
322 fEntries[i].fVertices = vertices;
323 fEntries[i].fMatrix = matrix;
Mike Reedaa9e3322017-03-16 14:38:48 -0400324 fSize += vertices->approximateSize();
Brian Salomond1ac9822017-02-03 14:25:02 -0500325 return vertices;
326 }
327
328 private:
329 struct Entry {
330 FACTORY fFactory;
Brian Salomonaff27a22017-02-06 15:47:44 -0500331 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500332 SkMatrix fMatrix;
333 };
334 Entry fEntries[MAX_ENTRIES];
335 int fCount = 0;
336 size_t fSize = 0;
Jim Van Vertheb63eb72017-05-23 09:40:02 -0400337 SkRandom fRandom;
Brian Salomond1ac9822017-02-03 14:25:02 -0500338 };
339
340 Set<AmbientVerticesFactory, 4> fAmbientSet;
341 Set<SpotVerticesFactory, 4> fSpotSet;
Brian Salomond1ac9822017-02-03 14:25:02 -0500342};
343
Brian Salomond1ac9822017-02-03 14:25:02 -0500344/**
345 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular
346 * path. The key represents the path's geometry and not any shadow params.
347 */
348class CachedTessellationsRec : public SkResourceCache::Rec {
349public:
350 CachedTessellationsRec(const SkResourceCache::Key& key,
351 sk_sp<CachedTessellations> tessellations)
352 : fTessellations(std::move(tessellations)) {
Brian Salomon5e689522017-02-01 12:07:17 -0500353 fKey.reset(new uint8_t[key.size()]);
354 memcpy(fKey.get(), &key, key.size());
355 }
356
357 const Key& getKey() const override {
358 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
359 }
Brian Salomon5e689522017-02-01 12:07:17 -0500360
Brian Salomond1ac9822017-02-03 14:25:02 -0500361 size_t bytesUsed() const override { return fTessellations->size(); }
Brian Salomon5e689522017-02-01 12:07:17 -0500362
Brian Salomond1ac9822017-02-03 14:25:02 -0500363 const char* getCategory() const override { return "tessellated shadow masks"; }
Brian Salomon5e689522017-02-01 12:07:17 -0500364
Brian Salomond1ac9822017-02-03 14:25:02 -0500365 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; }
Brian Salomon5e689522017-02-01 12:07:17 -0500366
Brian Salomond1ac9822017-02-03 14:25:02 -0500367 template <typename FACTORY>
Brian Salomonaff27a22017-02-06 15:47:44 -0500368 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix,
369 SkVector* translate) const {
Brian Salomond1ac9822017-02-03 14:25:02 -0500370 return fTessellations->find(factory, matrix, translate);
371 }
Brian Salomon5e689522017-02-01 12:07:17 -0500372
373private:
374 std::unique_ptr<uint8_t[]> fKey;
Brian Salomond1ac9822017-02-03 14:25:02 -0500375 sk_sp<CachedTessellations> fTessellations;
Brian Salomon5e689522017-02-01 12:07:17 -0500376};
377
378/**
379 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
Brian Salomond1ac9822017-02-03 14:25:02 -0500380 * vertices and a translation vector. If the CachedTessellations does not contain a suitable
381 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations
382 * to the caller. The caller will update it and reinsert it back into the cache.
Brian Salomon5e689522017-02-01 12:07:17 -0500383 */
384template <typename FACTORY>
385struct FindContext {
386 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
387 : fViewMatrix(viewMatrix), fFactory(factory) {}
Brian Salomond1ac9822017-02-03 14:25:02 -0500388 const SkMatrix* const fViewMatrix;
389 // If this is valid after Find is called then we found the vertices and they should be drawn
390 // with fTranslate applied.
Brian Salomonaff27a22017-02-06 15:47:44 -0500391 sk_sp<SkVertices> fVertices;
Brian Salomond1ac9822017-02-03 14:25:02 -0500392 SkVector fTranslate = {0, 0};
393
394 // If this is valid after Find then the caller should add the vertices to the tessellation set
395 // and create a new CachedTessellationsRec and insert it into SkResourceCache.
396 sk_sp<CachedTessellations> fTessellationsOnFailure;
397
Brian Salomon5e689522017-02-01 12:07:17 -0500398 const FACTORY* fFactory;
399};
400
401/**
402 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
403 * the FindContext are used to determine if the vertices are reusable. If so the vertices and
404 * necessary translation vector are set on the FindContext.
405 */
406template <typename FACTORY>
407bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
408 FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
Brian Salomond1ac9822017-02-03 14:25:02 -0500409 const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec);
410 findContext->fVertices =
411 rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate);
412 if (findContext->fVertices) {
413 return true;
Brian Salomon5e689522017-02-01 12:07:17 -0500414 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500415 // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been
416 // manipulated we will add a new Rec.
417 findContext->fTessellationsOnFailure = rec.refTessellations();
418 return false;
Brian Salomon5e689522017-02-01 12:07:17 -0500419}
420
421class ShadowedPath {
422public:
423 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
Jim Van Vertha84898d2017-02-06 13:38:23 -0500424 : fPath(path)
Brian Salomon5e689522017-02-01 12:07:17 -0500425 , fViewMatrix(viewMatrix)
426#if SK_SUPPORT_GPU
427 , fShapeForKey(*path, GrStyle::SimpleFill())
428#endif
429 {}
430
Jim Van Vertha84898d2017-02-06 13:38:23 -0500431 const SkPath& path() const { return *fPath; }
Brian Salomon5e689522017-02-01 12:07:17 -0500432 const SkMatrix& viewMatrix() const { return *fViewMatrix; }
433#if SK_SUPPORT_GPU
434 /** Negative means the vertices should not be cached for this path. */
435 int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
436 void writeKey(void* key) const {
437 fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
438 }
Brian Salomond1ac9822017-02-03 14:25:02 -0500439 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); }
Brian Salomon5e689522017-02-01 12:07:17 -0500440#else
441 int keyBytes() const { return -1; }
442 void writeKey(void* key) const { SkFAIL("Should never be called"); }
Brian Salomond1ac9822017-02-03 14:25:02 -0500443 bool isRRect(SkRRect* rrect) { return false; }
Brian Salomon5e689522017-02-01 12:07:17 -0500444#endif
445
446private:
Jim Van Vertha84898d2017-02-06 13:38:23 -0500447 const SkPath* fPath;
Brian Salomon5e689522017-02-01 12:07:17 -0500448 const SkMatrix* fViewMatrix;
449#if SK_SUPPORT_GPU
450 GrShape fShapeForKey;
451#endif
Brian Salomon5e689522017-02-01 12:07:17 -0500452};
453
Brian Salomond1ac9822017-02-03 14:25:02 -0500454// This creates a domain of keys in SkResourceCache used by this file.
455static void* kNamespace;
456
Brian Salomon5e689522017-02-01 12:07:17 -0500457/**
458 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
459 * they are first found in SkResourceCache.
460 */
461template <typename FACTORY>
Mike Reed4204da22017-05-17 08:53:36 -0400462 void draw_shadow(const FACTORY& factory,
463 std::function<void(const SkVertices*, SkBlendMode, const SkPaint&,
464 SkScalar tx, SkScalar ty)> drawProc, ShadowedPath& path, SkColor color) {
Brian Salomon5e689522017-02-01 12:07:17 -0500465 FindContext<FACTORY> context(&path.viewMatrix(), &factory);
Brian Salomon5e689522017-02-01 12:07:17 -0500466
467 SkResourceCache::Key* key = nullptr;
468 SkAutoSTArray<32 * 4, uint8_t> keyStorage;
469 int keyDataBytes = path.keyBytes();
470 if (keyDataBytes >= 0) {
471 keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
472 key = new (keyStorage.begin()) SkResourceCache::Key();
473 path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
Brian Salomonbc9956d2017-02-22 13:49:09 -0500474 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400475 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
Brian Salomon5e689522017-02-01 12:07:17 -0500476 }
477
Brian Salomonaff27a22017-02-06 15:47:44 -0500478 sk_sp<SkVertices> vertices;
Brian Salomon5e689522017-02-01 12:07:17 -0500479 bool foundInCache = SkToBool(context.fVertices);
480 if (foundInCache) {
481 vertices = std::move(context.fVertices);
Brian Salomon5e689522017-02-01 12:07:17 -0500482 } else {
483 // TODO: handle transforming the path as part of the tessellator
Brian Salomond1ac9822017-02-03 14:25:02 -0500484 if (key) {
485 // Update or initialize a tessellation set and add it to the cache.
486 sk_sp<CachedTessellations> tessellations;
487 if (context.fTessellationsOnFailure) {
488 tessellations = std::move(context.fTessellationsOnFailure);
489 } else {
490 tessellations.reset(new CachedTessellations());
491 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400492 vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
493 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500494 if (!vertices) {
495 return;
496 }
Brian Salomon804e0912017-02-23 09:34:03 -0500497 auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
Jim Van Verth37c5a962017-05-10 14:13:24 -0400498 SkResourceCache::Add(rec);
Brian Salomond1ac9822017-02-03 14:25:02 -0500499 } else {
Jim Van Verth8793e382017-05-22 15:52:21 -0400500 vertices = factory.makeVertices(path.path(), path.viewMatrix(),
501 &context.fTranslate);
Brian Salomond1ac9822017-02-03 14:25:02 -0500502 if (!vertices) {
503 return;
504 }
Brian Salomon0dda9cb2017-02-03 10:33:25 -0500505 }
Brian Salomon5e689522017-02-01 12:07:17 -0500506 }
507
508 SkPaint paint;
Brian Salomon0bd699e2017-02-01 12:23:25 -0500509 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
510 // that against our 'color' param.
511 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
512 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
513 SkGaussianColorFilter::Make()));
Mike Reed4204da22017-05-17 08:53:36 -0400514
Jim Van Verth8793e382017-05-22 15:52:21 -0400515 drawProc(vertices.get(), SkBlendMode::kModulate, paint,
516 context.fTranslate.fX, context.fTranslate.fY);
Brian Salomon5e689522017-02-01 12:07:17 -0500517}
518}
519
Mike Reed4204da22017-05-17 08:53:36 -0400520static bool tilted(const SkPoint3& zPlaneParams) {
521 return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
522}
Jim Van Verthe7e1d9d2017-05-01 16:06:48 -0400523
Mike Reed4204da22017-05-17 08:53:36 -0400524static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
525 SkPoint3 result;
526 m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
527 result.fZ = pt.fZ;
528 return result;
529}
530
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400531static SkColor compute_render_color(SkColor color, float alpha, bool useTonalColor) {
532 if (useTonalColor) {
533 SkScalar colorScale;
534 SkScalar tonalAlpha;
535 SkColor4f color4f = SkColor4f::FromColor(color);
536 SkShadowUtils::ComputeTonalColorParams(color4f.fR,
537 color4f.fG,
538 color4f.fB,
539 alpha,
540 &colorScale, &tonalAlpha);
541 // After pre-multiplying, we want the alpha to be scaled by tonalAlpha, and
542 // the color scaled by colorScale. This scale factor gives that.
543 SkScalar unPremulScale = colorScale / tonalAlpha;
544
545 return SkColorSetARGB(tonalAlpha*255.999f, unPremulScale*SkColorGetR(color),
546 unPremulScale*SkColorGetG(color), unPremulScale*SkColorGetB(color));
547 }
548
Jim Van Verth060d9822017-05-04 09:58:17 -0400549 return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
550 SkColorGetG(color), SkColorGetB(color));
551}
552
Jim Van Verth43475ad2017-01-13 14:37:37 -0500553// Draw an offset spot shadow and outlining ambient shadow for the given path.
Jim Van Verth37c5a962017-05-10 14:13:24 -0400554void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
Brian Salomon0bd699e2017-02-01 12:23:25 -0500555 const SkPoint3& devLightPos, SkScalar lightRadius,
Jim Van Verth43475ad2017-01-13 14:37:37 -0500556 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
Jim Van Verth37c5a962017-05-10 14:13:24 -0400557 uint32_t flags) {
Mike Reed4204da22017-05-17 08:53:36 -0400558 SkMatrix inverse;
559 if (!canvas->getTotalMatrix().invert(&inverse)) {
Jim Van Verthcf40e302017-03-02 11:28:43 -0500560 return;
561 }
Mike Reed4204da22017-05-17 08:53:36 -0400562 SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
Jim Van Verthcf40e302017-03-02 11:28:43 -0500563
Mike Reed4204da22017-05-17 08:53:36 -0400564 SkDrawShadowRec rec;
565 rec.fZPlaneParams = zPlaneParams;
566 rec.fLightPos = { pt.fX, pt.fY, devLightPos.fZ };
567 rec.fLightRadius = lightRadius;
568 rec.fAmbientAlpha = SkScalarToFloat(ambientAlpha);
569 rec.fSpotAlpha = SkScalarToFloat(spotAlpha);
570 rec.fColor = color;
571 rec.fFlags = flags;
572
573 canvas->private_draw_shadow_rec(path, rec);
574}
575
576void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
577 auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
578 SkScalar tx, SkScalar ty) {
579 SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
580 SkMatrix::MakeTrans(tx, ty)));
581 this->drawVertices(vertices, mode, paint);
582 };
583
584 SkMatrix viewMatrix = this->ctm();
585 SkAutoDeviceCTMRestore adr(this, SkMatrix::I());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500586
Brian Salomon5e689522017-02-01 12:07:17 -0500587 ShadowedPath shadowedPath(&path, &viewMatrix);
588
Mike Reed4204da22017-05-17 08:53:36 -0400589 bool tiltZPlane = tilted(rec.fZPlaneParams);
590 bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
Jim Van Verth4c9b8932017-05-15 13:49:21 -0400591 bool uncached = tiltZPlane || path.isVolatile();
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400592 bool useTonalColor = SkToBool(rec.fFlags & kTonalColor_ShadowFlag);
Brian Salomon958fbc42017-01-30 17:01:28 -0500593
Mike Reed4204da22017-05-17 08:53:36 -0400594 SkColor color = rec.fColor;
595 SkPoint3 zPlaneParams = rec.fZPlaneParams;
596 SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
597 float lightRadius = rec.fLightRadius;
598
599 float ambientAlpha = rec.fAmbientAlpha;
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500600 if (ambientAlpha > 0) {
Brian Salomon0bd699e2017-02-01 12:23:25 -0500601 ambientAlpha = SkTMin(ambientAlpha, 1.f);
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400602 SkColor renderColor;
603 if (useTonalColor) {
604 renderColor = compute_render_color(SK_ColorBLACK, ambientAlpha, false);
605 } else {
606 renderColor = compute_render_color(color, ambientAlpha, false);
607 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400608 if (uncached) {
609 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
610 zPlaneParams,
611 transparent);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400612 SkPaint paint;
613 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
614 // result of that against our 'color' param.
615 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
Mike Reed4204da22017-05-17 08:53:36 -0400616 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
617 SkGaussianColorFilter::Make()));
618 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Brian Salomond1ac9822017-02-03 14:25:02 -0500619 } else {
Jim Van Verth37c5a962017-05-10 14:13:24 -0400620 AmbientVerticesFactory factory;
621 factory.fOccluderHeight = zPlaneParams.fZ;
622 factory.fTransparent = transparent;
Jim Van Verth8793e382017-05-22 15:52:21 -0400623 if (viewMatrix.hasPerspective()) {
624 factory.fOffset.set(0, 0);
625 } else {
626 factory.fOffset.fX = viewMatrix.getTranslateX();
627 factory.fOffset.fY = viewMatrix.getTranslateY();
628 }
Jim Van Verth37c5a962017-05-10 14:13:24 -0400629
Mike Reed4204da22017-05-17 08:53:36 -0400630 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Brian Salomond1ac9822017-02-03 14:25:02 -0500631 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400632 }
633
Mike Reed4204da22017-05-17 08:53:36 -0400634 float spotAlpha = rec.fSpotAlpha;
Jim Van Verthb4366552017-03-27 14:25:29 -0400635 if (spotAlpha > 0) {
636 spotAlpha = SkTMin(spotAlpha, 1.f);
Jim Van Verth34d6e4b2017-06-09 11:09:03 -0400637 SkColor renderColor = compute_render_color(color, spotAlpha, useTonalColor);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400638 if (uncached) {
639 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
640 zPlaneParams,
641 devLightPos, lightRadius,
642 transparent);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400643 SkPaint paint;
644 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
645 // result of that against our 'color' param.
646 paint.setColorFilter(SkColorFilter::MakeComposeFilter(
647 SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
648 SkGaussianColorFilter::Make()));
Mike Reed4204da22017-05-17 08:53:36 -0400649 this->drawVertices(vertices.get(), SkBlendMode::kModulate, paint);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400650 } else {
651 SpotVerticesFactory factory;
652 SkScalar occluderHeight = zPlaneParams.fZ;
653 float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
Jim Van Vertha783c362017-05-11 17:05:28 -0400654 SkScalar radius = lightRadius * zRatio;
655
Jim Van Verth78c8f302017-05-15 10:44:22 -0400656 // Compute the scale and translation for the spot shadow.
657 SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400658 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
Jim Van Verth8793e382017-05-22 15:52:21 -0400659 factory.fLocalCenter = center;
Jim Van Verth37c5a962017-05-10 14:13:24 -0400660 viewMatrix.mapPoints(&center, 1);
661 factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
662 zRatio * (center.fY - devLightPos.fY));
663 factory.fOccluderHeight = occluderHeight;
664 factory.fDevLightPos = devLightPos;
665 factory.fLightRadius = lightRadius;
Jim Van Vertha783c362017-05-11 17:05:28 -0400666 SkRect devBounds;
667 viewMatrix.mapRect(&devBounds, path.getBounds());
Jim Van Verth8793e382017-05-22 15:52:21 -0400668 if (transparent ||
669 SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
670 SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400671 // if the translation of the shadow is big enough we're going to end up
672 // filling the entire umbra, so we can treat these as all the same
Jim Van Verth8793e382017-05-22 15:52:21 -0400673 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400674 } else if (factory.fOffset.length()*scale + scale < radius) {
Jim Van Vertha783c362017-05-11 17:05:28 -0400675 // if we don't translate more than the blur distance, can assume umbra is covered
Jim Van Verth78c8f302017-05-15 10:44:22 -0400676 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400677 } else {
Jim Van Verth78c8f302017-05-15 10:44:22 -0400678 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
Jim Van Vertha783c362017-05-11 17:05:28 -0400679 }
Jim Van Verth8793e382017-05-22 15:52:21 -0400680 // need to add this after we classify the shadow
681 factory.fOffset.fX += viewMatrix.getTranslateX();
682 factory.fOffset.fY += viewMatrix.getTranslateY();
Jim Van Vertha783c362017-05-11 17:05:28 -0400683#ifdef DEBUG_SHADOW_CHECKS
684 switch (factory.fOccluderType) {
685 case SpotVerticesFactory::OccluderType::kTransparent:
686 color = 0xFFD2B48C; // tan for transparent
687 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400688 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
Jim Van Vertha783c362017-05-11 17:05:28 -0400689 color = 0xFFFFA500; // orange for opaque
690 break;
Jim Van Verth78c8f302017-05-15 10:44:22 -0400691 case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
692 color = 0xFFE5E500; // corn yellow for covered
Jim Van Vertha783c362017-05-11 17:05:28 -0400693 break;
694 }
695#endif
Mike Reed4204da22017-05-17 08:53:36 -0400696 draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
Jim Van Verth37c5a962017-05-10 14:13:24 -0400697 }
Jim Van Verthb4366552017-03-27 14:25:29 -0400698 }
699}