SkShadowUtils.cpp revision ab244f045a0740fa6106ed21a4e5824cd09f84f3
1/* 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" 10#include "SkColorFilter.h" 11#include "SkColorPriv.h" 12#include "SkPath.h" 13#include "SkRandom.h" 14#include "SkResourceCache.h" 15#include "SkShadowTessellator.h" 16#include "SkString.h" 17#include "SkTLazy.h" 18#include "SkVertices.h" 19#if SK_SUPPORT_GPU 20#include "GrShape.h" 21#include "effects/GrBlurredEdgeFragmentProcessor.h" 22#endif 23#include "../../src/effects/shadows/SkAmbientShadowMaskFilter.h" 24#include "../../src/effects/shadows/SkSpotShadowMaskFilter.h" 25 26/** 27* Gaussian color filter -- produces a Gaussian ramp based on the color's B value, 28* then blends with the color's G value. 29* Final result is black with alpha of Gaussian(B)*G. 30* The assumption is that the original color's alpha is 1. 31*/ 32class SK_API SkGaussianColorFilter : public SkColorFilter { 33public: 34 static sk_sp<SkColorFilter> Make() { 35 return sk_sp<SkColorFilter>(new SkGaussianColorFilter); 36 } 37 38 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override; 39 40#if SK_SUPPORT_GPU 41 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override; 42#endif 43 44 SK_TO_STRING_OVERRIDE() 45 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianColorFilter) 46 47protected: 48 void flatten(SkWriteBuffer&) const override {} 49 50private: 51 SkGaussianColorFilter() : INHERITED() {} 52 53 typedef SkColorFilter INHERITED; 54}; 55 56static void build_table() { 57 SkDebugf("const uint16_t gByteExpU16Table[256] = {"); 58 for (int i = 0; i <= 255; ++i) { 59 if (!(i % 8)) { 60 SkDebugf("\n"); 61 } 62 SkScalar factor = SK_Scalar1 - i / 255.f; 63 factor = SkScalarExp(-factor * factor * 4) - 0.018f; 64 int v = (int)(factor * 65536); 65 SkDebugf(" 0x%04X,", v); 66 } 67 SkDebugf("\n};\n"); 68} 69 70const uint16_t gByteExpU16Table[256] = { 71 0x0014, 0x003A, 0x0062, 0x008A, 0x00B3, 0x00DE, 0x010A, 0x0136, 72 0x0165, 0x0194, 0x01C4, 0x01F6, 0x0229, 0x025E, 0x0294, 0x02CB, 73 0x0304, 0x033E, 0x0379, 0x03B7, 0x03F5, 0x0435, 0x0477, 0x04BB, 74 0x0500, 0x0546, 0x058F, 0x05D9, 0x0625, 0x0673, 0x06C3, 0x0714, 75 0x0768, 0x07BD, 0x0814, 0x086E, 0x08C9, 0x0926, 0x0986, 0x09E8, 76 0x0A4B, 0x0AB1, 0x0B1A, 0x0B84, 0x0BF1, 0x0C60, 0x0CD2, 0x0D46, 77 0x0DBC, 0x0E35, 0x0EB0, 0x0F2E, 0x0FAF, 0x1032, 0x10B7, 0x1140, 78 0x11CB, 0x1258, 0x12E9, 0x137C, 0x1412, 0x14AB, 0x1547, 0x15E6, 79 0x1688, 0x172D, 0x17D5, 0x187F, 0x192D, 0x19DE, 0x1A92, 0x1B4A, 80 0x1C04, 0x1CC2, 0x1D83, 0x1E47, 0x1F0E, 0x1FD9, 0x20A7, 0x2178, 81 0x224D, 0x2325, 0x2401, 0x24E0, 0x25C2, 0x26A8, 0x2792, 0x287F, 82 0x296F, 0x2A63, 0x2B5A, 0x2C56, 0x2D54, 0x2E56, 0x2F5C, 0x3065, 83 0x3172, 0x3283, 0x3397, 0x34AE, 0x35CA, 0x36E9, 0x380B, 0x3931, 84 0x3A5B, 0x3B88, 0x3CB9, 0x3DED, 0x3F25, 0x4061, 0x41A0, 0x42E2, 85 0x4428, 0x4572, 0x46BF, 0x480F, 0x4963, 0x4ABA, 0x4C14, 0x4D72, 86 0x4ED3, 0x5038, 0x519F, 0x530A, 0x5478, 0x55E9, 0x575D, 0x58D4, 87 0x5A4F, 0x5BCC, 0x5D4C, 0x5ECF, 0x6054, 0x61DD, 0x6368, 0x64F6, 88 0x6686, 0x6819, 0x69AE, 0x6B45, 0x6CDF, 0x6E7B, 0x701A, 0x71BA, 89 0x735D, 0x7501, 0x76A7, 0x784F, 0x79F9, 0x7BA4, 0x7D51, 0x7F00, 90 0x80AF, 0x8260, 0x8413, 0x85C6, 0x877A, 0x8930, 0x8AE6, 0x8C9C, 91 0x8E54, 0x900C, 0x91C4, 0x937D, 0x9535, 0x96EE, 0x98A7, 0x9A60, 92 0x9C18, 0x9DD1, 0x9F88, 0xA13F, 0xA2F6, 0xA4AB, 0xA660, 0xA814, 93 0xA9C6, 0xAB78, 0xAD27, 0xAED6, 0xB082, 0xB22D, 0xB3D6, 0xB57D, 94 0xB722, 0xB8C5, 0xBA65, 0xBC03, 0xBD9E, 0xBF37, 0xC0CD, 0xC25F, 95 0xC3EF, 0xC57B, 0xC704, 0xC889, 0xCA0B, 0xCB89, 0xCD04, 0xCE7A, 96 0xCFEC, 0xD15A, 0xD2C4, 0xD429, 0xD58A, 0xD6E6, 0xD83D, 0xD990, 97 0xDADD, 0xDC25, 0xDD68, 0xDEA6, 0xDFDE, 0xE111, 0xE23E, 0xE365, 98 0xE486, 0xE5A2, 0xE6B7, 0xE7C6, 0xE8CF, 0xE9D1, 0xEACD, 0xEBC3, 99 0xECB2, 0xED9A, 0xEE7C, 0xEF56, 0xF02A, 0xF0F6, 0xF1BC, 0xF27A, 100 0xF332, 0xF3E1, 0xF48A, 0xF52B, 0xF5C5, 0xF657, 0xF6E1, 0xF764, 101 0xF7DF, 0xF852, 0xF8BE, 0xF922, 0xF97E, 0xF9D2, 0xFA1E, 0xFA62, 102 0xFA9F, 0xFAD3, 0xFAFF, 0xFB23, 0xFB40, 0xFB54, 0xFB60, 0xFB64, 103}; 104 105void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { 106 // to re-build the table, call build_table() which will dump it out using SkDebugf. 107 if (false) { 108 build_table(); 109 } 110 for (int i = 0; i < count; ++i) { 111 SkPMColor c = src[i]; 112 uint8_t a = gByteExpU16Table[SkGetPackedB32(c)] * SkGetPackedG32(c) >> 16; 113 dst[i] = SkPackARGB32(a, a, a, a); 114 } 115} 116 117sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) { 118 return Make(); 119} 120 121#ifndef SK_IGNORE_TO_STRING 122void SkGaussianColorFilter::toString(SkString* str) const { 123 str->append("SkGaussianColorFilter "); 124} 125#endif 126 127#if SK_SUPPORT_GPU 128 129sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*, 130 SkColorSpace*) const { 131 return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode); 132} 133#endif 134 135/////////////////////////////////////////////////////////////////////////////////////////////////// 136 137namespace { 138 139uint64_t resource_cache_shared_id() { 140 return 0x2020776f64616873llu; // 'shadow ' 141} 142 143/** Factory for an ambient shadow mesh with particular shadow properties. */ 144struct AmbientVerticesFactory { 145 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed. 146 SkScalar fAmbientAlpha; 147 bool fTransparent; 148 149 bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const { 150 if (fOccluderHeight != that.fOccluderHeight || fAmbientAlpha != that.fAmbientAlpha || 151 fTransparent != that.fTransparent) { 152 return false; 153 } 154 translate->set(0, 0); 155 return true; 156 } 157 158 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const { 159 SkScalar z = fOccluderHeight; 160 return SkShadowTessellator::MakeAmbient(path, ctm, 161 [z](SkScalar, SkScalar) { return z; }, 162 fAmbientAlpha, fTransparent); 163 } 164}; 165 166/** Factory for an spot shadow mesh with particular shadow properties. */ 167struct SpotVerticesFactory { 168 enum class OccluderType { 169 // The umbra cannot be dropped out because the occluder is not opaque. 170 kTransparent, 171 // The umbra can be dropped where it is occluded. 172 kOpaque, 173 // It is known that the entire umbra is occluded. 174 kOpaqueCoversUmbra 175 }; 176 177 SkVector fOffset; 178 SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed. 179 SkPoint3 fDevLightPos; 180 SkScalar fLightRadius; 181 SkScalar fSpotAlpha; 182 OccluderType fOccluderType; 183 184 bool isCompatible(const SpotVerticesFactory& that, SkVector* translate) const { 185 if (fOccluderHeight != that.fOccluderHeight || fDevLightPos.fZ != that.fDevLightPos.fZ || 186 fLightRadius != that.fLightRadius || fSpotAlpha != that.fSpotAlpha || 187 fOccluderType != that.fOccluderType) { 188 return false; 189 } 190 switch (fOccluderType) { 191 case OccluderType::kTransparent: 192 case OccluderType::kOpaqueCoversUmbra: 193 // 'this' and 'that' will either both have no umbra removed or both have all the 194 // umbra removed. 195 *translate = that.fOffset - fOffset; 196 return true; 197 case OccluderType::kOpaque: 198 // In this case we partially remove the umbra differently for 'this' and 'that' 199 // if the offsets don't match. 200 if (fOffset == that.fOffset) { 201 translate->set(0, 0); 202 return true; 203 } 204 return false; 205 } 206 SkFAIL("Uninitialized occluder type?"); 207 return false; 208 } 209 210 sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const { 211 bool transparent = OccluderType::kTransparent == fOccluderType; 212 SkScalar z = fOccluderHeight; 213 return SkShadowTessellator::MakeSpot(path, ctm, 214 [z](SkScalar, SkScalar) -> SkScalar { return z; }, 215 fDevLightPos, fLightRadius, 216 fSpotAlpha, transparent); 217 } 218}; 219 220/** 221 * This manages a set of tessellations for a given shape in the cache. Because SkResourceCache 222 * records are immutable this is not itself a Rec. When we need to update it we return this on 223 * the FindVisitor and let the cache destory the Rec. We'll update the tessellations and then add 224 * a new Rec with an adjusted size for any deletions/additions. 225 */ 226class CachedTessellations : public SkRefCnt { 227public: 228 size_t size() const { return fAmbientSet.size() + fSpotSet.size(); } 229 230 sk_sp<SkVertices> find(const AmbientVerticesFactory& ambient, const SkMatrix& matrix, 231 SkVector* translate) const { 232 return fAmbientSet.find(ambient, matrix, translate); 233 } 234 235 sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient, 236 const SkMatrix& matrix) { 237 return fAmbientSet.add(devPath, ambient, matrix); 238 } 239 240 sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix, 241 SkVector* translate) const { 242 return fSpotSet.find(spot, matrix, translate); 243 } 244 245 sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot, 246 const SkMatrix& matrix) { 247 return fSpotSet.add(devPath, spot, matrix); 248 } 249 250private: 251 template <typename FACTORY, int MAX_ENTRIES> 252 class Set { 253 public: 254 size_t size() const { return fSize; } 255 256 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix, 257 SkVector* translate) const { 258 for (int i = 0; i < MAX_ENTRIES; ++i) { 259 if (fEntries[i].fFactory.isCompatible(factory, translate)) { 260 const SkMatrix& m = fEntries[i].fMatrix; 261 if (matrix.hasPerspective() || m.hasPerspective()) { 262 if (matrix != fEntries[i].fMatrix) { 263 continue; 264 } 265 } else if (matrix.getScaleX() != m.getScaleX() || 266 matrix.getSkewX() != m.getSkewX() || 267 matrix.getScaleY() != m.getScaleY() || 268 matrix.getSkewY() != m.getSkewY()) { 269 continue; 270 } 271 *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(), 272 matrix.getTranslateY() - m.getTranslateY()}; 273 return fEntries[i].fVertices; 274 } 275 } 276 return nullptr; 277 } 278 279 sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) { 280 sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix); 281 if (!vertices) { 282 return nullptr; 283 } 284 int i; 285 if (fCount < MAX_ENTRIES) { 286 i = fCount++; 287 } else { 288 i = gRandom.nextULessThan(MAX_ENTRIES); 289 fSize -= fEntries[i].fVertices->approximateSize(); 290 } 291 fEntries[i].fFactory = factory; 292 fEntries[i].fVertices = vertices; 293 fEntries[i].fMatrix = matrix; 294 fSize += vertices->approximateSize(); 295 return vertices; 296 } 297 298 private: 299 struct Entry { 300 FACTORY fFactory; 301 sk_sp<SkVertices> fVertices; 302 SkMatrix fMatrix; 303 }; 304 Entry fEntries[MAX_ENTRIES]; 305 int fCount = 0; 306 size_t fSize = 0; 307 }; 308 309 Set<AmbientVerticesFactory, 4> fAmbientSet; 310 Set<SpotVerticesFactory, 4> fSpotSet; 311 312 static SkRandom gRandom; 313}; 314 315SkRandom CachedTessellations::gRandom; 316 317/** 318 * A record of shadow vertices stored in SkResourceCache of CachedTessellations for a particular 319 * path. The key represents the path's geometry and not any shadow params. 320 */ 321class CachedTessellationsRec : public SkResourceCache::Rec { 322public: 323 CachedTessellationsRec(const SkResourceCache::Key& key, 324 sk_sp<CachedTessellations> tessellations) 325 : fTessellations(std::move(tessellations)) { 326 fKey.reset(new uint8_t[key.size()]); 327 memcpy(fKey.get(), &key, key.size()); 328 } 329 330 const Key& getKey() const override { 331 return *reinterpret_cast<SkResourceCache::Key*>(fKey.get()); 332 } 333 334 size_t bytesUsed() const override { return fTessellations->size(); } 335 336 const char* getCategory() const override { return "tessellated shadow masks"; } 337 338 sk_sp<CachedTessellations> refTessellations() const { return fTessellations; } 339 340 template <typename FACTORY> 341 sk_sp<SkVertices> find(const FACTORY& factory, const SkMatrix& matrix, 342 SkVector* translate) const { 343 return fTessellations->find(factory, matrix, translate); 344 } 345 346private: 347 std::unique_ptr<uint8_t[]> fKey; 348 sk_sp<CachedTessellations> fTessellations; 349}; 350 351/** 352 * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the 353 * vertices and a translation vector. If the CachedTessellations does not contain a suitable 354 * mesh then we inform SkResourceCache to destroy the Rec and we return the CachedTessellations 355 * to the caller. The caller will update it and reinsert it back into the cache. 356 */ 357template <typename FACTORY> 358struct FindContext { 359 FindContext(const SkMatrix* viewMatrix, const FACTORY* factory) 360 : fViewMatrix(viewMatrix), fFactory(factory) {} 361 const SkMatrix* const fViewMatrix; 362 // If this is valid after Find is called then we found the vertices and they should be drawn 363 // with fTranslate applied. 364 sk_sp<SkVertices> fVertices; 365 SkVector fTranslate = {0, 0}; 366 367 // If this is valid after Find then the caller should add the vertices to the tessellation set 368 // and create a new CachedTessellationsRec and insert it into SkResourceCache. 369 sk_sp<CachedTessellations> fTessellationsOnFailure; 370 371 const FACTORY* fFactory; 372}; 373 374/** 375 * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of 376 * the FindContext are used to determine if the vertices are reusable. If so the vertices and 377 * necessary translation vector are set on the FindContext. 378 */ 379template <typename FACTORY> 380bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) { 381 FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx; 382 const CachedTessellationsRec& rec = static_cast<const CachedTessellationsRec&>(baseRec); 383 findContext->fVertices = 384 rec.find(*findContext->fFactory, *findContext->fViewMatrix, &findContext->fTranslate); 385 if (findContext->fVertices) { 386 return true; 387 } 388 // We ref the tessellations and let the cache destroy the Rec. Once the tessellations have been 389 // manipulated we will add a new Rec. 390 findContext->fTessellationsOnFailure = rec.refTessellations(); 391 return false; 392} 393 394class ShadowedPath { 395public: 396 ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix) 397 : fPath(path) 398 , fViewMatrix(viewMatrix) 399#if SK_SUPPORT_GPU 400 , fShapeForKey(*path, GrStyle::SimpleFill()) 401#endif 402 {} 403 404 const SkPath& path() const { return *fPath; } 405 const SkMatrix& viewMatrix() const { return *fViewMatrix; } 406#if SK_SUPPORT_GPU 407 /** Negative means the vertices should not be cached for this path. */ 408 int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); } 409 void writeKey(void* key) const { 410 fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key)); 411 } 412 bool isRRect(SkRRect* rrect) { return fShapeForKey.asRRect(rrect, nullptr, nullptr, nullptr); } 413#else 414 int keyBytes() const { return -1; } 415 void writeKey(void* key) const { SkFAIL("Should never be called"); } 416 bool isRRect(SkRRect* rrect) { return false; } 417#endif 418 419private: 420 const SkPath* fPath; 421 const SkMatrix* fViewMatrix; 422#if SK_SUPPORT_GPU 423 GrShape fShapeForKey; 424#endif 425}; 426 427// This creates a domain of keys in SkResourceCache used by this file. 428static void* kNamespace; 429 430/** 431 * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless 432 * they are first found in SkResourceCache. 433 */ 434template <typename FACTORY> 435void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color, 436 SkResourceCache* cache) { 437 FindContext<FACTORY> context(&path.viewMatrix(), &factory); 438 439 SkResourceCache::Key* key = nullptr; 440 SkAutoSTArray<32 * 4, uint8_t> keyStorage; 441 int keyDataBytes = path.keyBytes(); 442 if (keyDataBytes >= 0) { 443 keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key)); 444 key = new (keyStorage.begin()) SkResourceCache::Key(); 445 path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key))); 446 key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes); 447 if (cache) { 448 cache->find(*key, FindVisitor<FACTORY>, &context); 449 } else { 450 SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context); 451 } 452 } 453 454 sk_sp<SkVertices> vertices; 455 const SkVector* translate; 456 static constexpr SkVector kZeroTranslate = {0, 0}; 457 bool foundInCache = SkToBool(context.fVertices); 458 if (foundInCache) { 459 vertices = std::move(context.fVertices); 460 translate = &context.fTranslate; 461 } else { 462 // TODO: handle transforming the path as part of the tessellator 463 if (key) { 464 // Update or initialize a tessellation set and add it to the cache. 465 sk_sp<CachedTessellations> tessellations; 466 if (context.fTessellationsOnFailure) { 467 tessellations = std::move(context.fTessellationsOnFailure); 468 } else { 469 tessellations.reset(new CachedTessellations()); 470 } 471 vertices = tessellations->add(path.path(), factory, path.viewMatrix()); 472 if (!vertices) { 473 return; 474 } 475 auto rec = new CachedTessellationsRec(*key, std::move(tessellations)); 476 if (cache) { 477 cache->add(rec); 478 } else { 479 SkResourceCache::Add(rec); 480 } 481 } else { 482 vertices = factory.makeVertices(path.path(), path.viewMatrix()); 483 if (!vertices) { 484 return; 485 } 486 } 487 translate = &kZeroTranslate; 488 } 489 490 SkPaint paint; 491 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of 492 // that against our 'color' param. 493 paint.setColorFilter(SkColorFilter::MakeComposeFilter( 494 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate), 495 SkGaussianColorFilter::Make())); 496 if (translate->fX || translate->fY) { 497 canvas->save(); 498 canvas->translate(translate->fX, translate->fY); 499 } 500 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); 501 if (translate->fX || translate->fY) { 502 canvas->restore(); 503 } 504} 505} 506 507static bool draw_analytic_shadows(SkCanvas* canvas, const SkPath& path, SkScalar occluderZ, 508 const SkPoint3& devLightPos, SkScalar lightRadius, 509 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, 510 uint32_t flags) { 511 // only supported in GPU code 512 if (!canvas->getGrContext()) { 513 return false; 514 } 515 516 SkRect rect; 517 SkRRect rrect; 518 if (canvas->getTotalMatrix().isSimilarity()) { 519 if (path.isRect(&rect)) { 520 SkPaint newPaint; 521 newPaint.setColor(color); 522 if (ambientAlpha > 0) { 523 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ, 524 ambientAlpha, flags)); 525 canvas->drawRect(rect, newPaint); 526 } 527 if (spotAlpha > 0) { 528 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos, 529 lightRadius, spotAlpha, 530 flags)); 531 canvas->drawRect(rect, newPaint); 532 } 533 return true; 534 } else if (path.isRRect(&rrect) && rrect.isSimpleCircular() && 535 rrect.radii(SkRRect::kUpperLeft_Corner).fX > SK_ScalarNearlyZero) { 536 SkPaint newPaint; 537 newPaint.setColor(color); 538 if (ambientAlpha > 0) { 539 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ, 540 ambientAlpha, flags)); 541 canvas->drawRRect(rrect, newPaint); 542 } 543 if (spotAlpha > 0) { 544 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos, 545 lightRadius, spotAlpha, 546 flags)); 547 canvas->drawRRect(rrect, newPaint); 548 } 549 return true; 550 } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) && 551 rect.width() > SK_ScalarNearlyZero) { 552 SkPaint newPaint; 553 newPaint.setColor(color); 554 if (ambientAlpha > 0) { 555 newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderZ, 556 ambientAlpha, flags)); 557 canvas->drawOval(rect, newPaint); 558 } 559 if (spotAlpha > 0) { 560 newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderZ, devLightPos, 561 lightRadius, spotAlpha, 562 flags)); 563 canvas->drawOval(rect, newPaint); 564 } 565 return true; 566 } 567 } 568 569 return false; 570} 571 572// Draw an offset spot shadow and outlining ambient shadow for the given path. 573void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight, 574 const SkPoint3& devLightPos, SkScalar lightRadius, 575 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, 576 uint32_t flags, SkResourceCache* cache) { 577 // try fast paths 578 if (draw_analytic_shadows(canvas, path, occluderHeight, devLightPos, lightRadius, 579 ambientAlpha, spotAlpha, color, flags)) { 580 return; 581 } 582 583 SkAutoCanvasRestore acr(canvas, true); 584 SkMatrix viewMatrix = canvas->getTotalMatrix(); 585 canvas->resetMatrix(); 586 587 ShadowedPath shadowedPath(&path, &viewMatrix); 588 589 bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag); 590 591 if (ambientAlpha > 0) { 592 ambientAlpha = SkTMin(ambientAlpha, 1.f); 593 AmbientVerticesFactory factory; 594 factory.fOccluderHeight = occluderHeight; 595 factory.fAmbientAlpha = ambientAlpha; 596 factory.fTransparent = transparent; 597 598 draw_shadow(factory, canvas, shadowedPath, color, cache); 599 } 600 601 if (spotAlpha > 0) { 602 spotAlpha = SkTMin(spotAlpha, 1.f); 603 SpotVerticesFactory factory; 604 float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f); 605 SkScalar radius = lightRadius * zRatio; 606 607 // Compute the scale and translation for the spot shadow. 608 SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight); 609 610 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); 611 viewMatrix.mapPoints(¢er, 1); 612 factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX), 613 zRatio * (center.fY - devLightPos.fY)); 614 factory.fOccluderHeight = occluderHeight; 615 factory.fDevLightPos = devLightPos; 616 factory.fLightRadius = lightRadius; 617 factory.fSpotAlpha = spotAlpha; 618 619 SkRRect rrect; 620 if (transparent) { 621 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; 622 } else { 623 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque; 624 if (shadowedPath.isRRect(&rrect)) { 625 SkRRect devRRect; 626 if (rrect.transform(viewMatrix, &devRRect)) { 627 SkScalar s = 1.f - scale; 628 SkScalar w = devRRect.width(); 629 SkScalar h = devRRect.height(); 630 SkScalar hw = w / 2.f; 631 SkScalar hh = h / 2.f; 632 SkScalar umbraInsetX = s * hw + radius; 633 SkScalar umbraInsetY = s * hh + radius; 634 // The umbra is inset by radius along the diagonal, so adjust for that. 635 SkScalar d = 1.f / SkScalarSqrt(hw * hw + hh * hh); 636 umbraInsetX *= hw * d; 637 umbraInsetY *= hh * d; 638 if (umbraInsetX > hw || umbraInsetY > hh) { 639 // There is no umbra to occlude. 640 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; 641 } else if (fabsf(factory.fOffset.fX) < umbraInsetX && 642 fabsf(factory.fOffset.fY) < umbraInsetY) { 643 factory.fOccluderType = 644 SpotVerticesFactory::OccluderType::kOpaqueCoversUmbra; 645 } else if (factory.fOffset.fX > w - umbraInsetX || 646 factory.fOffset.fY > h - umbraInsetY) { 647 // There umbra is fully exposed, there is nothing to omit. 648 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; 649 } 650 } 651 } 652 } 653 if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) { 654 factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; 655 } 656 draw_shadow(factory, canvas, shadowedPath, color, cache); 657 } 658} 659 660// Draw an offset spot shadow and outlining ambient shadow for the given path, 661// without caching and using a function based on local position to compute the height. 662void SkShadowUtils::DrawUncachedShadow(SkCanvas* canvas, const SkPath& path, 663 std::function<SkScalar(SkScalar, SkScalar)> heightFunc, 664 const SkPoint3& lightPos, SkScalar lightRadius, 665 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, 666 uint32_t flags) { 667 // try fast paths 668 if (draw_analytic_shadows(canvas, path, heightFunc(0, 0), lightPos, lightRadius, 669 ambientAlpha, spotAlpha, color, flags)) { 670 return; 671 } 672 673 SkAutoCanvasRestore acr(canvas, true); 674 SkMatrix viewMatrix = canvas->getTotalMatrix(); 675 canvas->resetMatrix(); 676 677 bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag); 678 679 if (ambientAlpha > 0) { 680 ambientAlpha = SkTMin(ambientAlpha, 1.f); 681 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix, 682 heightFunc, ambientAlpha, 683 transparent); 684 SkPaint paint; 685 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale 686 // result of that against our 'color' param. 687 paint.setColorFilter(SkColorFilter::MakeComposeFilter( 688 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate), 689 SkGaussianColorFilter::Make())); 690 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); 691 } 692 693 if (spotAlpha > 0) { 694 spotAlpha = SkTMin(spotAlpha, 1.f); 695 sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, heightFunc, 696 lightPos, lightRadius, 697 spotAlpha, transparent); 698 SkPaint paint; 699 // Run the vertex color through a GaussianColorFilter and then modulate the grayscale 700 // result of that against our 'color' param. 701 paint.setColorFilter(SkColorFilter::MakeComposeFilter( 702 SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate), 703 SkGaussianColorFilter::Make())); 704 canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); 705 } 706} 707