Cache ambient and spot shadows at a canonical position
authorJim Van Verth <jvanverth@google.com>
Mon, 22 May 2017 19:52:21 +0000 (15:52 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 22 May 2017 20:37:57 +0000 (20:37 +0000)
Change-Id: I1f80931513f7d2268b358fb38c86dd331f32f064
Reviewed-on: https://skia-review.googlesource.com/17394
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
gm/shadowutils.cpp
samplecode/SampleShadowUtils.cpp
src/utils/SkShadowUtils.cpp

index 9748881..fbe73c6 100644 (file)
@@ -30,15 +30,10 @@ void draw_paths(SkCanvas* canvas, bool hideOccluders) {
     SkRRect oddRRect;
     oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
     paths.push_back().addRRect(oddRRect);
-    paths.back().setIsVolatile(true);
     paths.push_back().addRect(SkRect::MakeWH(50, 50));
-    paths.back().setIsVolatile(true);
     paths.push_back().addCircle(25, 25, 25);
-    paths.back().setIsVolatile(true);
     paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
-    paths.back().setIsVolatile(true);
     paths.push_back().addOval(SkRect::MakeWH(20, 60));
-    paths.back().setIsVolatile(true);
 
     static constexpr SkScalar kPad = 15.f;
     static constexpr SkScalar kLightR = 100.f;
index e592f97..8d8a0ae 100755 (executable)
@@ -154,10 +154,7 @@ protected:
         static constexpr SkScalar kHeight = 50.f;
         static constexpr SkScalar kAmbientAlpha = 0.5f;
         static constexpr SkScalar kSpotAlpha = 0.5f;
-
-        // transform light position relative to canvas to handle tiling
-        SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400);
-        SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 };
+        static constexpr SkPoint3 lightPos = { 250, 400, 500 };
 
         canvas->translate(3 * kPad, 3 * kPad);
         canvas->save();
index 16a1eee..2cbf14a 100644 (file)
@@ -161,28 +161,36 @@ uint64_t resource_cache_shared_id() {
 struct AmbientVerticesFactory {
     SkScalar fOccluderHeight = SK_ScalarNaN;  // NaN so that isCompatible will fail until init'ed.
     bool fTransparent;
+    SkVector fOffset;
 
     bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const {
         if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) {
             return false;
         }
-        translate->set(0, 0);
+        *translate = that.fOffset;
         return true;
     }
 
-    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
+    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
+                                   SkVector* translate) const {
         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
-        return SkShadowTessellator::MakeAmbient(path, ctm, zParams, fTransparent);
+        // pick a canonical place to generate shadow
+        SkMatrix noTrans(ctm);
+        if (!ctm.hasPerspective()) {
+            noTrans[SkMatrix::kMTransX] = 0;
+            noTrans[SkMatrix::kMTransY] = 0;
+        }
+        *translate = fOffset;
+        return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent);
     }
 };
 
 /** Factory for an spot shadow mesh with particular shadow properties. */
 struct SpotVerticesFactory {
     enum class OccluderType {
-        // The umbra cannot be dropped out because the occluder is not opaque.
+        // The umbra cannot be dropped out because either the occluder is not opaque,
+        // or the center of the umbra is visible.
         kTransparent,
-        // The occluder is opaque and the umbra is fully visible
-        kOpaqueFullUmbra,
         // The umbra can be dropped where it is occluded.
         kOpaquePartialUmbra,
         // It is known that the entire umbra is occluded.
@@ -190,6 +198,7 @@ struct SpotVerticesFactory {
     };
 
     SkVector fOffset;
+    SkPoint  fLocalCenter;
     SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed.
     SkPoint3 fDevLightPos;
     SkScalar fLightRadius;
@@ -202,11 +211,10 @@ struct SpotVerticesFactory {
         }
         switch (fOccluderType) {
             case OccluderType::kTransparent:
-            case OccluderType::kOpaqueFullUmbra:
             case OccluderType::kOpaqueNoUmbra:
                 // 'this' and 'that' will either both have no umbra removed or both have all the
                 // umbra removed.
-                *translate = that.fOffset - fOffset;
+                *translate = that.fOffset;
                 return true;
             case OccluderType::kOpaquePartialUmbra:
                 // In this case we partially remove the umbra differently for 'this' and 'that'
@@ -221,11 +229,26 @@ struct SpotVerticesFactory {
         return false;
     }
 
-    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
+    sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
+                                   SkVector* translate) const {
         bool transparent = OccluderType::kTransparent == fOccluderType;
         SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
-        return SkShadowTessellator::MakeSpot(path, ctm, zParams,
-                                             fDevLightPos, fLightRadius, transparent);
+        if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
+            translate->set(0, 0);
+            return SkShadowTessellator::MakeSpot(path, ctm, zParams,
+                                                 fDevLightPos, fLightRadius, transparent);
+        } else {
+            // pick a canonical place to generate shadow, with light centered over path
+            SkMatrix noTrans(ctm);
+            noTrans[SkMatrix::kMTransX] = 0;
+            noTrans[SkMatrix::kMTransY] = 0;
+            SkPoint devCenter(fLocalCenter);
+            noTrans.mapPoints(&devCenter, 1);
+            SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
+            *translate = fOffset;
+            return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
+                                                 centerLightPos, fLightRadius, transparent);
+        }
     }
 };
 
@@ -245,8 +268,8 @@ public:
     }
 
     sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient,
-                          const SkMatrix& matrix) {
-        return fAmbientSet.add(devPath, ambient, matrix);
+                          const SkMatrix& matrix, SkVector* translate) {
+        return fAmbientSet.add(devPath, ambient, matrix, translate);
     }
 
     sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix,
@@ -255,8 +278,8 @@ public:
     }
 
     sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot,
-                          const SkMatrix& matrix) {
-        return fSpotSet.add(devPath, spot, matrix);
+                          const SkMatrix& matrix, SkVector* translate) {
+        return fSpotSet.add(devPath, spot, matrix, translate);
     }
 
 private:
@@ -280,16 +303,15 @@ private:
                                matrix.getSkewY() != m.getSkewY()) {
                         continue;
                     }
-                    *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(),
-                                           matrix.getTranslateY() - m.getTranslateY()};
                     return fEntries[i].fVertices;
                 }
             }
             return nullptr;
         }
 
-        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) {
-            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix);
+        sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix,
+                              SkVector* translate) {
+            sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate);
             if (!vertices) {
                 return nullptr;
             }
@@ -461,12 +483,9 @@ template <typename FACTORY>
     }
 
     sk_sp<SkVertices> vertices;
-    const SkVector* translate;
-    static constexpr SkVector kZeroTranslate = {0, 0};
     bool foundInCache = SkToBool(context.fVertices);
     if (foundInCache) {
         vertices = std::move(context.fVertices);
-        translate = &context.fTranslate;
     } else {
         // TODO: handle transforming the path as part of the tessellator
         if (key) {
@@ -477,19 +496,20 @@ template <typename FACTORY>
             } else {
                 tessellations.reset(new CachedTessellations());
             }
-            vertices = tessellations->add(path.path(), factory, path.viewMatrix());
+            vertices = tessellations->add(path.path(), factory, path.viewMatrix(),
+                                          &context.fTranslate);
             if (!vertices) {
                 return;
             }
             auto rec = new CachedTessellationsRec(*key, std::move(tessellations));
             SkResourceCache::Add(rec);
         } else {
-            vertices = factory.makeVertices(path.path(), path.viewMatrix());
+            vertices = factory.makeVertices(path.path(), path.viewMatrix(),
+                                            &context.fTranslate);
             if (!vertices) {
                 return;
             }
         }
-        translate = &kZeroTranslate;
     }
 
     SkPaint paint;
@@ -499,7 +519,8 @@ template <typename FACTORY>
             SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
             SkGaussianColorFilter::Make()));
 
-    drawProc(vertices.get(), SkBlendMode::kModulate, paint, translate->fX, translate->fY);
+    drawProc(vertices.get(), SkBlendMode::kModulate, paint,
+             context.fTranslate.fX, context.fTranslate.fY);
 }
 }
 
@@ -583,6 +604,12 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
             AmbientVerticesFactory factory;
             factory.fOccluderHeight = zPlaneParams.fZ;
             factory.fTransparent = transparent;
+            if (viewMatrix.hasPerspective()) {
+                factory.fOffset.set(0, 0);
+            } else {
+                factory.fOffset.fX = viewMatrix.getTranslateX();
+                factory.fOffset.fY = viewMatrix.getTranslateY();
+            }
 
             SkColor renderColor = compute_render_color(color, ambientAlpha);
             draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
@@ -614,6 +641,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
             // 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());
+            factory.fLocalCenter = center;
             viewMatrix.mapPoints(&center, 1);
             factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
                                              zRatio * (center.fY - devLightPos.fY));
@@ -622,28 +650,26 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
             factory.fLightRadius = lightRadius;
             SkRect devBounds;
             viewMatrix.mapRect(&devBounds, path.getBounds());
-            if (transparent) {
-                factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
-            } else if (SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
-                       SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
+            if (transparent ||
+                SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
+                SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
                 // if the translation of the shadow is big enough we're going to end up
                 // filling the entire umbra, so we can treat these as all the same
-                factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueFullUmbra;
+                factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
             } else if (factory.fOffset.length()*scale + scale < radius) {
                 // if we don't translate more than the blur distance, can assume umbra is covered
                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra;
             } else {
                 factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra;
             }
-
+            // need to add this after we classify the shadow
+            factory.fOffset.fX += viewMatrix.getTranslateX();
+            factory.fOffset.fY += viewMatrix.getTranslateY();
 #ifdef DEBUG_SHADOW_CHECKS
             switch (factory.fOccluderType) {
                 case SpotVerticesFactory::OccluderType::kTransparent:
                     color = 0xFFD2B48C;  // tan for transparent
                     break;
-                case SpotVerticesFactory::OccluderType::kOpaqueFullUmbra:
-                    color = 0xFF614126;   // brown for umBra
-                    break;
                 case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra:
                     color = 0xFFFFA500;   // orange for opaque
                     break;