Allow shadow zParams to be applied to affine transformations
authorJim Van Verth <jvanverth@google.com>
Mon, 15 May 2017 17:49:21 +0000 (13:49 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 15 May 2017 18:21:39 +0000 (18:21 +0000)
Change-Id: Iedfded98ce82d15945667232fde22d046d5106b3
Reviewed-on: https://skia-review.googlesource.com/16879
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Mike Reed <reed@google.com>
include/utils/SkShadowUtils.h
src/utils/SkShadowTessellator.cpp
src/utils/SkShadowUtils.cpp

index bf804a5..2385a48 100644 (file)
@@ -28,7 +28,6 @@ public:
      * @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).
-     *  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.
index d8990d3..0c36c53 100755 (executable)
@@ -64,7 +64,7 @@ protected:
     std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
     SkScalar                                fZOffset;
     // members for perspective height function
-    SkPoint3                                fPerspZParams;
+    SkPoint3                                fTransformedZParams;
     SkScalar                                fPartialDeterminants[3];
 
     // first two points
@@ -268,7 +268,7 @@ bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc)
 }
 
 bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
-    if (!ctm.hasPerspective()) {
+    if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
         fTransformedHeightFunc = [this](const SkPoint& p) {
             return fZPlaneParams.fZ;
         };
@@ -277,9 +277,8 @@ bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
         if (!ctm.invert(&ctmInverse)) {
             return false;
         }
-
         // multiply by transpose
-        fPerspZParams = SkPoint3::Make(
+        fTransformedZParams = SkPoint3::Make(
             ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
             ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
             ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
@@ -293,33 +292,41 @@ bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
             ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
         );
 
-        // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
-        // so pre-compute those values that are independent of X and Y.
-        // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
-        fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
-                                  ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
-        fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
-                                  ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
-        fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
-                                  ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
-        SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
-                                  ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
-                                  ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
-
-        // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
-        // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
-        fPerspZParams.fX *= ctmDeterminant;
-        fPerspZParams.fY *= ctmDeterminant;
-        fPerspZParams.fZ *= ctmDeterminant;
-
-        fTransformedHeightFunc = [this](const SkPoint& p) {
-            SkScalar denom = p.fX * fPartialDeterminants[0] +
-                             p.fY * fPartialDeterminants[1] +
-                             fPartialDeterminants[2];
-            SkScalar w = SkScalarFastInvert(denom);
-            return (fPerspZParams.fX * p.fX + fPerspZParams.fY * p.fY + fPerspZParams.fZ)*w +
-                   fZOffset;
-        };
+        if (ctm.hasPerspective()) {
+            // We use Cramer's rule to solve for the W value for a given post-divide X and Y,
+            // so pre-compute those values that are independent of X and Y.
+            // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
+            fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
+                                      ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
+            fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
+                                      ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
+            fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
+                                      ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
+            SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
+                                      ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
+                                      ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
+
+            // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
+            // TODO: this may introduce numerical instability, but I haven't seen any issues yet.
+            fTransformedZParams.fX *= ctmDeterminant;
+            fTransformedZParams.fY *= ctmDeterminant;
+            fTransformedZParams.fZ *= ctmDeterminant;
+
+            fTransformedHeightFunc = [this](const SkPoint& p) {
+                SkScalar denom = p.fX * fPartialDeterminants[0] +
+                                 p.fY * fPartialDeterminants[1] +
+                                 fPartialDeterminants[2];
+                SkScalar w = SkScalarFastInvert(denom);
+                return fZOffset + w*(fTransformedZParams.fX * p.fX +
+                                     fTransformedZParams.fY * p.fY +
+                                     fTransformedZParams.fZ);
+            };
+        } else {
+            fTransformedHeightFunc = [this](const SkPoint& p) {
+                return fZOffset + fTransformedZParams.fX * p.fX +
+                       fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
+            };
+        }
     }
 
     return true;
index d84c330..806c98e 100644 (file)
@@ -583,8 +583,11 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoi
                                const SkPoint3& devLightPos, SkScalar lightRadius,
                                SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
                                uint32_t flags) {
+    // check z plane
+    bool tiltZPlane = !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
+
     // try fast paths
-    bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag);
+    bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag) || tiltZPlane;
     if (!skipAnalytic && draw_analytic_shadows(canvas, path, zPlaneParams.fZ, devLightPos,
                                                lightRadius, ambientAlpha, spotAlpha, color,
                                                flags)) {
@@ -598,7 +601,7 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoi
     ShadowedPath shadowedPath(&path, &viewMatrix);
 
     bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
-    bool uncached = viewMatrix.hasPerspective() || path.isVolatile();
+    bool uncached = tiltZPlane || path.isVolatile();
 
     if (ambientAlpha > 0) {
         ambientAlpha = SkTMin(ambientAlpha, 1.f);