Unify ShadowUtils interface
authorJim Van Verth <jvanverth@google.com>
Wed, 10 May 2017 18:13:24 +0000 (14:13 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 10 May 2017 19:27:39 +0000 (19:27 +0000)
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>

gm/shadowutils.cpp
include/utils/SkShadowUtils.h
samplecode/SampleAndroidShadows.cpp
samplecode/SampleShadowUtils.cpp
src/utils/SkShadowUtils.cpp

index 53d9b78..9d9eaae 100644 (file)
 #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<SkPath> 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;
index 4a497bb..bf804a5 100644 (file)
@@ -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
index 81dc0cc..5b07064 100644 (file)
@@ -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);
index 6d047be..e383811 100755 (executable)
@@ -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);
index c095acf..caf918e 100644 (file)
@@ -441,8 +441,7 @@ static void* kNamespace;
  * they are first found in SkResourceCache.
  */
 template <typename FACTORY>
-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<FACTORY> 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<FACTORY>, &context);
-        } else {
-            SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
-        }
+        SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
     }
 
     sk_sp<SkVertices> 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;
+        if (uncached) {
+            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);
+        } else {
+            AmbientVerticesFactory factory;
+            factory.fOccluderHeight = zPlaneParams.fZ;
+            factory.fTransparent = transparent;
 
-        SkColor renderColor = compute_render_color(color, ambientAlpha);
-        draw_shadow(factory, canvas, shadowedPath, renderColor, cache);
+            SkColor renderColor = compute_render_color(color, ambientAlpha);
+            draw_shadow(factory, canvas, shadowedPath, renderColor);
+        }
     }
 
     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(&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;
+        if (uncached) {
+            sk_sp<SkVertices> 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 {
-            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;
-                    }
-                }
-            }
-        }
-        if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) {
+            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, 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) {
-        spotAlpha = SkTMin(spotAlpha, 1.f);
-        sk_sp<SkVertices> 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);
+            SkColor renderColor = compute_render_color(color, spotAlpha);
+            draw_shadow(factory, canvas, shadowedPath, renderColor);
+        }
     }
 }