diff --git a/gm/shadowutils.cpp b/gm/shadowutils.cpp index 53d9b78602..9d9eaae405 100644 --- a/gm/shadowutils.cpp +++ b/gm/shadowutils.cpp @@ -12,24 +12,17 @@ #include "SkShadowUtils.h" void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos, - SkScalar lightR, bool isAmbient, uint32_t flags, SkResourceCache* cache) { + SkScalar lightR, bool isAmbient, uint32_t flags) { SkScalar ambientAlpha = isAmbient ? .5f : 0.f; SkScalar spotAlpha = isAmbient ? 0.f : .5f; SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha, - color, flags, cache); + color, flags); } static constexpr int kW = 800; static constexpr int kH = 800; DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) { - // SkShadowUtils uses a cache of SkVertices meshes. The vertices are created in a local - // coordinate system and then translated when reused. The coordinate system depends on - // parameters to the generating draw. If other threads are hitting the cache while this GM is - // running then we may have different cache behavior leading to slight rendering differences. - // To avoid that we use our own isolated cache rather than the global cache. - SkResourceCache cache(1 << 20); - SkTArray paths; paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); SkRRect oddRRect; @@ -70,10 +63,8 @@ DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) { canvas->save(); canvas->concat(m); - draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags, - &cache); - draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags, - &cache); + draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags); + draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags); // Draw the path outline in green on top of the ambient and spot shadows. SkPaint paint; diff --git a/include/utils/SkShadowUtils.h b/include/utils/SkShadowUtils.h index 4a497bb397..bf804a581f 100644 --- a/include/utils/SkShadowUtils.h +++ b/include/utils/SkShadowUtils.h @@ -21,12 +21,14 @@ class SkShadowUtils { public: /** * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc - * light. + * light. The shadow may be cached, depending on the path type and canvas matrix. If the + * matrix is perspective or the path is volatile, it will not be cached. * * @param canvas The canvas on which to draw the shadows. * @param path The occluder used to generate the shadows. - * @param occluderHeight The vertical offset of the occluder from the canvas. This is - * independent of the canvas's current matrix. + * @param zPlaneParams Values for the plane function which returns the Z offset of the + * occluder from the canvas based on local x and y values (the current matrix is not applied). + * If the canvas matrix is not perspective, then only zPlaneParams.fZ is used. * @param lightPos The 3D position of the light relative to the canvas plane. This is * independent of the canvas's current matrix. * @param lightRadius The radius of the disc light. @@ -35,20 +37,47 @@ public: * @param color The shadow color. * @param flags Options controlling opaque occluder optimizations and shadow appearance. See * SkShadowFlags. - * @param cache Used for testing purposes. Clients should pass nullptr (default). */ + static void DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams, + const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha, + SkScalar spotAlpha, SkColor color, + uint32_t flags = SkShadowFlags::kNone_ShadowFlag); + + /** + * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc + * light. + * + * Deprecated version with height value (to be removed when Android and Flutter are updated). + * + * @param canvas The canvas on which to draw the shadows. + * @param path The occluder used to generate the shadows. + * @param occluderHeight The vertical offset of the occluder from the canvas. This is + * independent of the canvas's current matrix. + * @param lightPos The 3D position of the light relative to the canvas plane. This is + * independent of the canvas's current matrix. + * @param lightRadius The radius of the disc light. + * @param ambientAlpha The maximum alpha of the ambient shadow. + * @param spotAlpha The maxium alpha of the spot shadow. + * @param color The shadow color. + * @param flags Options controlling opaque occluder optimizations and shadow appearance. See + * SkShadowFlags. + */ static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight, const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, - uint32_t flags = SkShadowFlags::kNone_ShadowFlag, - SkResourceCache* cache = nullptr); + uint32_t flags = SkShadowFlags::kNone_ShadowFlag) { + SkPoint3 zPlane = SkPoint3::Make(0, 0, occluderHeight); + DrawShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha, + color, flags); + } +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK /** * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc * light. Takes a function to vary the z value based on the local x and y position. * This shadow will not be cached, as the assumption is that this will be used for animation. * - * Deprecated version with height functor (to be removed when Android is updated). + * Deprecated (to be removed when Android is updated). * * @param canvas The canvas on which to draw the shadows. * @param path The occluder used to generate the shadows. @@ -73,35 +102,10 @@ public: zPlane.fX = heightFunc(1, 0) - zPlane.fZ; zPlane.fY = heightFunc(0, 1) - zPlane.fZ; - DrawUncachedShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha, - color, flags); + DrawShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha, + color, flags); } - - /** - * Draw an offset spot shadow and outlining ambient shadow for the given path using a disc - * light. Uses a plane function to vary the z value based on the local x and y position. - * This shadow will not be cached, as the assumption is that this will be used for animation. - * - * @param canvas The canvas on which to draw the shadows. - * @param path The occluder used to generate the shadows. - * @param zPlaneParams Values for the plane function which returns the Z offset of the - * occluder from the canvas based on local x and y values (the current matrix is not applied). - * @param lightPos The 3D position of the light relative to the canvas plane. This is - * independent of the canvas's current matrix. - * @param lightRadius The radius of the disc light. - * @param ambientAlpha The maximum alpha of the ambient shadow. - * @param spotAlpha The maxium alpha of the spot shadow. - * @param color The shadow color. - * @param flags Options controlling opaque occluder optimizations and shadow appearance. See - * SkShadowFlags. - */ - static void DrawUncachedShadow(SkCanvas* canvas, const SkPath& path, - const SkPoint3& zPlaneParams, - const SkPoint3& lightPos, SkScalar lightRadius, - SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, - uint32_t flags = SkShadowFlags::kNone_ShadowFlag); - - +#endif }; #endif diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp index 81dc0cc2b6..5b070642bb 100644 --- a/samplecode/SampleAndroidShadows.cpp +++ b/samplecode/SampleAndroidShadows.cpp @@ -145,13 +145,9 @@ protected: if (fUseAlt) { flags |= SkShadowFlags::kGeometricOnly_ShadowFlag; } - //SkShadowUtils::DrawShadow(canvas, path, - // zValue, - // lightPos, lightWidth, - // ambientAlpha, spotAlpha, SK_ColorBLACK, flags); - SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams, - lightPos, lightWidth, - ambientAlpha, spotAlpha, SK_ColorBLACK, flags); + SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, + lightPos, lightWidth, + ambientAlpha, spotAlpha, SK_ColorBLACK, flags); if (fShowObject) { canvas->drawPath(path, paint); diff --git a/samplecode/SampleShadowUtils.cpp b/samplecode/SampleShadowUtils.cpp index 6d047be93d..e3838116f2 100755 --- a/samplecode/SampleShadowUtils.cpp +++ b/samplecode/SampleShadowUtils.cpp @@ -126,16 +126,12 @@ protected: if (fUseAlt) { flags |= SkShadowFlags::kGeometricOnly_ShadowFlag; } - //SkShadowUtils::DrawShadow(canvas, path, - // zValue, - // lightPos, lightWidth, - // ambientAlpha, spotAlpha, SK_ColorBLACK, flags); - SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams, - lightPos, lightWidth, - ambientAlpha, 0, SK_ColorRED, flags); - SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams, - lightPos, lightWidth, - 0, spotAlpha, SK_ColorBLUE, flags); + SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, + lightPos, lightWidth, + ambientAlpha, 0, SK_ColorRED, flags); + SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, + lightPos, lightWidth, + 0, spotAlpha, SK_ColorBLUE, flags); if (fShowObject) { canvas->drawPath(path, paint); diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index c095acfb08..caf918e4f2 100644 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -441,8 +441,7 @@ static void* kNamespace; * they are first found in SkResourceCache. */ template -void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color, - SkResourceCache* cache) { +void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color) { FindContext context(&path.viewMatrix(), &factory); SkResourceCache::Key* key = nullptr; @@ -453,11 +452,7 @@ void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, S key = new (keyStorage.begin()) SkResourceCache::Key(); path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key))); key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes); - if (cache) { - cache->find(*key, FindVisitor, &context); - } else { - SkResourceCache::Find(*key, FindVisitor, &context); - } + SkResourceCache::Find(*key, FindVisitor, &context); } sk_sp vertices; @@ -482,11 +477,7 @@ void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, S return; } auto rec = new CachedTessellationsRec(*key, std::move(tessellations)); - if (cache) { - cache->add(rec); - } else { - SkResourceCache::Add(rec); - } + SkResourceCache::Add(rec); } else { vertices = factory.makeVertices(path.path(), path.viewMatrix()); if (!vertices) { @@ -585,13 +576,13 @@ static SkColor compute_render_color(SkColor color, float alpha) { } // Draw an offset spot shadow and outlining ambient shadow for the given path. -void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight, +void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams, const SkPoint3& devLightPos, SkScalar lightRadius, SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, - uint32_t flags, SkResourceCache* cache) { + uint32_t flags) { // try fast paths bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag); - if (!skipAnalytic && draw_analytic_shadows(canvas, path, occluderHeight, devLightPos, + if (!skipAnalytic && draw_analytic_shadows(canvas, path, zPlaneParams.fZ, devLightPos, lightRadius, ambientAlpha, spotAlpha, color, flags)) { return; @@ -604,124 +595,63 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc ShadowedPath shadowedPath(&path, &viewMatrix); bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag); + bool uncached = viewMatrix.hasPerspective() || path.isVolatile(); if (ambientAlpha > 0) { ambientAlpha = SkTMin(ambientAlpha, 1.f); - AmbientVerticesFactory factory; - factory.fOccluderHeight = occluderHeight; - factory.fTransparent = transparent; - - SkColor renderColor = compute_render_color(color, ambientAlpha); - draw_shadow(factory, canvas, shadowedPath, renderColor, cache); - } - - if (spotAlpha > 0) { - spotAlpha = SkTMin(spotAlpha, 1.f); - SpotVerticesFactory factory; - float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f); - SkScalar radius = lightRadius * zRatio; - - // Compute the scale and translation for the spot shadow. - SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight); - - SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); - viewMatrix.mapPoints(¢er, 1); - factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX), - zRatio * (center.fY - devLightPos.fY)); - factory.fOccluderHeight = occluderHeight; - factory.fDevLightPos = devLightPos; - factory.fLightRadius = lightRadius; - - SkRRect rrect; - if (transparent) { - factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; + if (uncached) { + sk_sp vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix, + zPlaneParams, + transparent); + SkColor renderColor = compute_render_color(color, ambientAlpha); + SkPaint paint; + // Run the vertex color through a GaussianColorFilter and then modulate the grayscale + // result of that against our 'color' param. + paint.setColorFilter(SkColorFilter::MakeComposeFilter( + SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate), + SkGaussianColorFilter::Make())); + canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); } else { - factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque; - if (shadowedPath.isRRect(&rrect)) { - SkRRect devRRect; - if (rrect.transform(viewMatrix, &devRRect)) { - SkScalar s = 1.f - scale; - SkScalar w = devRRect.width(); - SkScalar h = devRRect.height(); - SkScalar hw = w / 2.f; - SkScalar hh = h / 2.f; - SkScalar umbraInsetX = s * hw + radius; - SkScalar umbraInsetY = s * hh + radius; - // The umbra is inset by radius along the diagonal, so adjust for that. - SkScalar d = 1.f / SkScalarSqrt(hw * hw + hh * hh); - umbraInsetX *= hw * d; - umbraInsetY *= hh * d; - if (umbraInsetX > hw || umbraInsetY > hh) { - // There is no umbra to occlude. - factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; - } else if (fabsf(factory.fOffset.fX) < umbraInsetX && - fabsf(factory.fOffset.fY) < umbraInsetY) { - factory.fOccluderType = - SpotVerticesFactory::OccluderType::kOpaqueCoversUmbra; - } else if (factory.fOffset.fX > w - umbraInsetX || - factory.fOffset.fY > h - umbraInsetY) { - // There umbra is fully exposed, there is nothing to omit. - factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; - } - } - } + AmbientVerticesFactory factory; + factory.fOccluderHeight = zPlaneParams.fZ; + factory.fTransparent = transparent; + + SkColor renderColor = compute_render_color(color, ambientAlpha); + draw_shadow(factory, canvas, shadowedPath, renderColor); } - if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) { - factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; - } - - SkColor renderColor = compute_render_color(color, spotAlpha); - draw_shadow(factory, canvas, shadowedPath, renderColor, cache); - } -} - -// Draw an offset spot shadow and outlining ambient shadow for the given path, -// without caching and using a function based on local position to compute the height. -void SkShadowUtils::DrawUncachedShadow(SkCanvas* canvas, const SkPath& path, - const SkPoint3& zPlaneParams, - const SkPoint3& lightPos, SkScalar lightRadius, - SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, - uint32_t flags) { - // try fast paths - bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag); - if (!skipAnalytic && draw_analytic_shadows(canvas, path, zPlaneParams.fZ, lightPos, - lightRadius, ambientAlpha, spotAlpha, color, - flags)) { - return; - } - - SkAutoCanvasRestore acr(canvas, true); - SkMatrix viewMatrix = canvas->getTotalMatrix(); - canvas->resetMatrix(); - - bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag); - - if (ambientAlpha > 0) { - ambientAlpha = SkTMin(ambientAlpha, 1.f); - sk_sp vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix, - zPlaneParams, transparent); - SkColor renderColor = compute_render_color(color, ambientAlpha); - SkPaint paint; - // Run the vertex color through a GaussianColorFilter and then modulate the grayscale - // result of that against our 'color' param. - paint.setColorFilter(SkColorFilter::MakeComposeFilter( - SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate), - SkGaussianColorFilter::Make())); - canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); } if (spotAlpha > 0) { spotAlpha = SkTMin(spotAlpha, 1.f); - sk_sp vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, zPlaneParams, - lightPos, lightRadius, - transparent); - SkColor renderColor = compute_render_color(color, spotAlpha); - SkPaint paint; - // Run the vertex color through a GaussianColorFilter and then modulate the grayscale - // result of that against our 'color' param. - paint.setColorFilter(SkColorFilter::MakeComposeFilter( - SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate), - SkGaussianColorFilter::Make())); - canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); + if (uncached) { + sk_sp vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, + zPlaneParams, + devLightPos, lightRadius, + transparent); + SkColor renderColor = compute_render_color(color, spotAlpha); + SkPaint paint; + // Run the vertex color through a GaussianColorFilter and then modulate the grayscale + // result of that against our 'color' param. + paint.setColorFilter(SkColorFilter::MakeComposeFilter( + SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate), + SkGaussianColorFilter::Make())); + canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); + } else { + SpotVerticesFactory factory; + SkScalar occluderHeight = zPlaneParams.fZ; + float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f); + SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); + viewMatrix.mapPoints(¢er, 1); + factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX), + zRatio * (center.fY - devLightPos.fY)); + factory.fOccluderHeight = occluderHeight; + factory.fDevLightPos = devLightPos; + factory.fLightRadius = lightRadius; + // the only valid choice we have right now + factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; + + SkColor renderColor = compute_render_color(color, spotAlpha); + draw_shadow(factory, canvas, shadowedPath, renderColor); + } } }