Unify ShadowUtils interface

Bug: skia:
Change-Id: I116bec82783d297e91ef061217b5e61f7ff16a76
Reviewed-on: https://skia-review.googlesource.com/16371
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2017-05-10 14:13:24 -04:00 committed by Skia Commit-Bot
parent 77ced29102
commit 37c5a96bbd
5 changed files with 107 additions and 190 deletions

View File

@ -12,24 +12,17 @@
#include "SkShadowUtils.h" #include "SkShadowUtils.h"
void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos, 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 ambientAlpha = isAmbient ? .5f : 0.f;
SkScalar spotAlpha = isAmbient ? 0.f : .5f; SkScalar spotAlpha = isAmbient ? 0.f : .5f;
SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha, SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha,
color, flags, cache); color, flags);
} }
static constexpr int kW = 800; static constexpr int kW = 800;
static constexpr int kH = 800; static constexpr int kH = 800;
DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) { 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<SkPath> paths; SkTArray<SkPath> paths;
paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
SkRRect oddRRect; SkRRect oddRRect;
@ -70,10 +63,8 @@ DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
canvas->save(); canvas->save();
canvas->concat(m); canvas->concat(m);
draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags, draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags);
&cache); draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags);
draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags,
&cache);
// Draw the path outline in green on top of the ambient and spot shadows. // Draw the path outline in green on top of the ambient and spot shadows.
SkPaint paint; SkPaint paint;

View File

@ -21,12 +21,14 @@ class SkShadowUtils {
public: public:
/** /**
* Draw an offset spot shadow and outlining ambient shadow for the given path using a disc * 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 canvas The canvas on which to draw the shadows.
* @param path The occluder used to generate the shadows. * @param path The occluder used to generate the shadows.
* @param occluderHeight The vertical offset of the occluder from the canvas. This is * @param zPlaneParams Values for the plane function which returns the Z offset of the
* independent of the canvas's current matrix. * 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 * @param lightPos The 3D position of the light relative to the canvas plane. This is
* independent of the canvas's current matrix. * independent of the canvas's current matrix.
* @param lightRadius The radius of the disc light. * @param lightRadius The radius of the disc light.
@ -35,20 +37,47 @@ public:
* @param color The shadow color. * @param color The shadow color.
* @param flags Options controlling opaque occluder optimizations and shadow appearance. See * @param flags Options controlling opaque occluder optimizations and shadow appearance. See
* SkShadowFlags. * 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, static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha, const SkPoint3& lightPos, SkScalar lightRadius, SkScalar ambientAlpha,
SkScalar spotAlpha, SkColor color, SkScalar spotAlpha, SkColor color,
uint32_t flags = SkShadowFlags::kNone_ShadowFlag, uint32_t flags = SkShadowFlags::kNone_ShadowFlag) {
SkResourceCache* cache = nullptr); 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 * 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. * 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. * 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 canvas The canvas on which to draw the shadows.
* @param path The occluder used to generate the shadows. * @param path The occluder used to generate the shadows.
@ -73,35 +102,10 @@ public:
zPlane.fX = heightFunc(1, 0) - zPlane.fZ; zPlane.fX = heightFunc(1, 0) - zPlane.fZ;
zPlane.fY = heightFunc(0, 1) - zPlane.fZ; zPlane.fY = heightFunc(0, 1) - zPlane.fZ;
DrawUncachedShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha, DrawShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha,
color, flags); color, flags);
} }
#endif
/**
* 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

View File

@ -145,13 +145,9 @@ protected:
if (fUseAlt) { if (fUseAlt) {
flags |= SkShadowFlags::kGeometricOnly_ShadowFlag; flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
} }
//SkShadowUtils::DrawShadow(canvas, path, SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
// zValue, lightPos, lightWidth,
// lightPos, lightWidth, ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
// ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams,
lightPos, lightWidth,
ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
if (fShowObject) { if (fShowObject) {
canvas->drawPath(path, paint); canvas->drawPath(path, paint);

View File

@ -126,16 +126,12 @@ protected:
if (fUseAlt) { if (fUseAlt) {
flags |= SkShadowFlags::kGeometricOnly_ShadowFlag; flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
} }
//SkShadowUtils::DrawShadow(canvas, path, SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
// zValue, lightPos, lightWidth,
// lightPos, lightWidth, ambientAlpha, 0, SK_ColorRED, flags);
// ambientAlpha, spotAlpha, SK_ColorBLACK, flags); SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams, lightPos, lightWidth,
lightPos, lightWidth, 0, spotAlpha, SK_ColorBLUE, flags);
ambientAlpha, 0, SK_ColorRED, flags);
SkShadowUtils::DrawUncachedShadow(canvas, path, zPlaneParams,
lightPos, lightWidth,
0, spotAlpha, SK_ColorBLUE, flags);
if (fShowObject) { if (fShowObject) {
canvas->drawPath(path, paint); canvas->drawPath(path, paint);

View File

@ -441,8 +441,7 @@ static void* kNamespace;
* they are first found in SkResourceCache. * they are first found in SkResourceCache.
*/ */
template <typename FACTORY> template <typename FACTORY>
void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color, void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color) {
SkResourceCache* cache) {
FindContext<FACTORY> context(&path.viewMatrix(), &factory); FindContext<FACTORY> context(&path.viewMatrix(), &factory);
SkResourceCache::Key* key = nullptr; 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(); key = new (keyStorage.begin()) SkResourceCache::Key();
path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key))); path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes); key->init(&kNamespace, resource_cache_shared_id(), keyDataBytes);
if (cache) { SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
cache->find(*key, FindVisitor<FACTORY>, &context);
} else {
SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
}
} }
sk_sp<SkVertices> vertices; sk_sp<SkVertices> vertices;
@ -482,11 +477,7 @@ void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, S
return; return;
} }
auto rec = new CachedTessellationsRec(*key, std::move(tessellations)); auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
if (cache) { SkResourceCache::Add(rec);
cache->add(rec);
} else {
SkResourceCache::Add(rec);
}
} else { } else {
vertices = factory.makeVertices(path.path(), path.viewMatrix()); vertices = factory.makeVertices(path.path(), path.viewMatrix());
if (!vertices) { 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. // 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, const SkPoint3& devLightPos, SkScalar lightRadius,
SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
uint32_t flags, SkResourceCache* cache) { uint32_t flags) {
// try fast paths // try fast paths
bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag); 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, lightRadius, ambientAlpha, spotAlpha, color,
flags)) { flags)) {
return; return;
@ -604,124 +595,63 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc
ShadowedPath shadowedPath(&path, &viewMatrix); ShadowedPath shadowedPath(&path, &viewMatrix);
bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag); bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
bool uncached = viewMatrix.hasPerspective() || path.isVolatile();
if (ambientAlpha > 0) { if (ambientAlpha > 0) {
ambientAlpha = SkTMin(ambientAlpha, 1.f); ambientAlpha = SkTMin(ambientAlpha, 1.f);
AmbientVerticesFactory factory; if (uncached) {
factory.fOccluderHeight = occluderHeight; sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
factory.fTransparent = transparent; zPlaneParams,
transparent);
SkColor renderColor = compute_render_color(color, ambientAlpha); SkColor renderColor = compute_render_color(color, ambientAlpha);
draw_shadow(factory, canvas, shadowedPath, renderColor, cache); SkPaint paint;
} // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
// result of that against our 'color' param.
if (spotAlpha > 0) { paint.setColorFilter(SkColorFilter::MakeComposeFilter(
spotAlpha = SkTMin(spotAlpha, 1.f); SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate),
SpotVerticesFactory factory; SkGaussianColorFilter::Make()));
float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f); canvas->drawVertices(vertices, SkBlendMode::kModulate, paint);
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(&center, 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;
} else { } else {
factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaque; AmbientVerticesFactory factory;
if (shadowedPath.isRRect(&rrect)) { factory.fOccluderHeight = zPlaneParams.fZ;
SkRRect devRRect; factory.fTransparent = transparent;
if (rrect.transform(viewMatrix, &devRRect)) {
SkScalar s = 1.f - scale; SkColor renderColor = compute_render_color(color, ambientAlpha);
SkScalar w = devRRect.width(); draw_shadow(factory, canvas, shadowedPath, renderColor);
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;
}
}
}
} }
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<SkVertices> 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) { if (spotAlpha > 0) {
spotAlpha = SkTMin(spotAlpha, 1.f); spotAlpha = SkTMin(spotAlpha, 1.f);
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix, zPlaneParams, if (uncached) {
lightPos, lightRadius, sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
transparent); zPlaneParams,
SkColor renderColor = compute_render_color(color, spotAlpha); devLightPos, lightRadius,
SkPaint paint; transparent);
// Run the vertex color through a GaussianColorFilter and then modulate the grayscale SkColor renderColor = compute_render_color(color, spotAlpha);
// result of that against our 'color' param. SkPaint paint;
paint.setColorFilter(SkColorFilter::MakeComposeFilter( // Run the vertex color through a GaussianColorFilter and then modulate the grayscale
SkColorFilter::MakeModeFilter(renderColor, SkBlendMode::kModulate), // result of that against our 'color' param.
SkGaussianColorFilter::Make())); paint.setColorFilter(SkColorFilter::MakeComposeFilter(
canvas->drawVertices(vertices, SkBlendMode::kModulate, paint); 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(&center, 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);
}
} }
} }