Tweaks in how to apply bitmap filter levels in GPU.

Fix fallback to MIP from bicubic for bitmap shaders
Skip MIP level generation on GPU when not minifying
Add medium quality and mixed up/down matrix test cases to filterbitmap tests

R=robertphillips@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/103913012

git-svn-id: http://skia.googlecode.com/svn/trunk@12697 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index bb16119..9a94676 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -354,22 +354,36 @@
 #include "effects/GrSimpleTextureEffect.h"
 #include "SkGr.h"
 
+// Note that this will return -1 if either matrix is perspective.
+static SkScalar get_combined_min_stretch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix) {
+    if (localMatrix.isIdentity()) {
+        return viewMatrix.getMinStretch();
+    } else {
+        SkMatrix combined;
+        combined.setConcat(viewMatrix, localMatrix);
+        return combined.getMinStretch();
+    }
+}
+
 GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
     SkMatrix matrix;
     matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
 
-    SkMatrix inverse;
-    if (!this->getLocalMatrix().invert(&inverse)) {
+    SkMatrix lmInverse;
+    if (!this->getLocalMatrix().invert(&lmInverse)) {
         return NULL;
     }
-    matrix.preConcat(inverse);
+    matrix.preConcat(lmInverse);
 
     SkShader::TileMode tm[] = {
         (TileMode)fState.fTileModeX,
         (TileMode)fState.fTileModeY,
     };
 
-    // Must set wrap and filter on the sampler before requesting a texture.
+    // Must set wrap and filter on the sampler before requesting a texture. In two places below
+    // we check the matrix scale factors to determine how to interpret the filter quality setting.
+    // This completely ignores the complexity of the drawVertices case where explicit local coords
+    // are provided by the caller.
     SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
     GrTextureParams::FilterMode textureFilterMode;
     switch(paintFilterLevel) {
@@ -380,21 +394,23 @@
             textureFilterMode = GrTextureParams::kBilerp_FilterMode;
             break;
         case SkPaint::kMedium_FilterLevel:
-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) <
+                SK_Scalar1) {
+                textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            } else {
+                // Don't trigger MIP level generation unnecessarily.
+                textureFilterMode = GrTextureParams::kBilerp_FilterMode;
+            }
             break;
         case SkPaint::kHigh_FilterLevel:
-            // Minification can look bad with the bicubic effect. This is an overly aggressive
-            // check for MIP fallbacks. It doesn't consider the fact that minification in the local
-            // matrix could be offset by the view matrix and vice versa. We also don't know whether
-            // the draw has explicit local coords (e.g. drawVertices) where the scale factor is
-            // unknown and varies.
-            if (context->getMatrix().getMinStretch() >= SK_Scalar1 &&
-                this->getLocalMatrix().getMaxStretch() <= SK_Scalar1) {
-                // fall back to no filtering here; we will install another
-                // shader that will do the HQ filtering.
+            // Minification can look bad with bicubic filtering.
+            if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) >=
+                SK_Scalar1) {
+                // fall back to no filtering here; we will install another shader that will do the
+                // HQ filtering.
                 textureFilterMode = GrTextureParams::kNone_FilterMode;
             } else {
-                // Fall back to mip-mapping.
+                // Fall back to MIP-mapping.
                 paintFilterLevel = SkPaint::kMedium_FilterLevel;
                 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
             }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index a0692d0..71d0ebf 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1204,9 +1204,14 @@
             break;
         case SkPaint::kMedium_FilterLevel:
             tileFilterPad = 1;
-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            if (fContext->getMatrix().getMinStretch() < SK_Scalar1) {
+                textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            } else {
+                // Don't trigger MIP level generation unnecessarily.
+                textureFilterMode = GrTextureParams::kBilerp_FilterMode;
+            }
             break;
-        case SkPaint::kHigh_FilterLevel: {
+        case SkPaint::kHigh_FilterLevel:
             // Minification can look bad with the bicubic effect.
             if (fContext->getMatrix().getMinStretch() >= SK_Scalar1) {
                 // We will install an effect that does the filtering in the shader.
@@ -1214,13 +1219,10 @@
                 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
                 doBicubic = true;
             } else {
-                // We don't yet support doing bicubic filtering with an interior clamp. Fall back
-                // to MIPs
                 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
                 tileFilterPad = 1;
             }
             break;
-        }
         default:
             SkErrorInternals::SetError( kInvalidPaint_SkError,
                                         "Sorry, I don't understand the filtering "