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