Do not use GrBicubic effect when downscaling. Also, don't use glTexStorage as it interferes with deleyed mipmap generation.

R=robertphillips@google.com

Author: bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12576 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index ded1b72..fcfcdbf 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -358,13 +358,12 @@
     SkMatrix matrix;
     matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
 
-    if (this->hasLocalMatrix()) {
-        SkMatrix inverse;
-        if (!this->getLocalMatrix().invert(&inverse)) {
-            return NULL;
-        }
-        matrix.preConcat(inverse);
+    SkMatrix inverse;
+    if (!this->getLocalMatrix().invert(&inverse)) {
+        return NULL;
     }
+    matrix.preConcat(inverse);
+
     SkShader::TileMode tm[] = {
         (TileMode)fState.fTileModeX,
         (TileMode)fState.fTileModeY,
@@ -384,9 +383,21 @@
             textureFilterMode = GrTextureParams::kMipMap_FilterMode;
             break;
         case SkPaint::kHigh_FilterLevel:
-            // fall back to no filtering here; we will install another
-            // shader that will do the HQ filtering.
-            textureFilterMode = GrTextureParams::kNone_FilterMode;
+            // 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.
+                textureFilterMode = GrTextureParams::kNone_FilterMode;
+            } else {
+                // Fall back to mip-mapping.
+                paintFilterLevel = SkPaint::kMedium_FilterLevel;
+                textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            }
             break;
         default:
             SkErrorInternals::SetError( kInvalidPaint_SkError,
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 3caf51a..474f272 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1857,45 +1857,71 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkScalar SkMatrix::getMaxStretch() const {
-    TypeMask mask = this->getType();
+enum MinOrMax {
+    kMin_MinOrMax,
+    kMax_MinOrMax
+};
 
-    if (this->hasPerspective()) {
+template <MinOrMax MIN_OR_MAX> SkScalar get_stretch_factor(SkMatrix::TypeMask typeMask,
+                                                           const SkScalar m[9]) {
+    if (typeMask & SkMatrix::kPerspective_Mask) {
         return -SK_Scalar1;
     }
-    if (this->isIdentity()) {
+    if (SkMatrix::kIdentity_Mask == typeMask) {
         return SK_Scalar1;
     }
-    if (!(mask & kAffine_Mask)) {
-        return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]),
-                           SkScalarAbs(fMat[kMScaleY]));
+    if (!(typeMask & SkMatrix::kAffine_Mask)) {
+        if (kMin_MinOrMax == MIN_OR_MAX) {
+             return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
+                                SkScalarAbs(m[SkMatrix::kMScaleY]));
+        } else {
+             return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
+                                SkScalarAbs(m[SkMatrix::kMScaleY]));
+        }
     }
     // ignore the translation part of the matrix, just look at 2x2 portion.
-    // compute singular values, take largest abs value.
+    // compute singular values, take largest or smallest abs value.
     // [a b; b c] = A^T*A
-    SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) +
-                 SkScalarMul(fMat[kMSkewY],  fMat[kMSkewY]);
-    SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) +
-                 SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
-    SkScalar c = SkScalarMul(fMat[kMSkewX],  fMat[kMSkewX]) +
-                 SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
+    SkScalar a = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX]) +
+                 SkScalarMul(m[SkMatrix::kMSkewY],  m[SkMatrix::kMSkewY]);
+    SkScalar b = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX]) +
+                 SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
+    SkScalar c = SkScalarMul(m[SkMatrix::kMSkewX],  m[SkMatrix::kMSkewX]) +
+                 SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
     // eigenvalues of A^T*A are the squared singular values of A.
     // characteristic equation is det((A^T*A) - l*I) = 0
     // l^2 - (a + c)l + (ac-b^2)
     // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
-    // and roots are guaraunteed to be pos and real).
-    SkScalar largerRoot;
+    // and roots are guaranteed to be pos and real).
+    SkScalar chosenRoot;
     SkScalar bSqd = SkScalarMul(b,b);
     // if upper left 2x2 is orthogonal save some math
     if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
-        largerRoot = SkMaxScalar(a, c);
+        if (kMin_MinOrMax == MIN_OR_MAX) {
+            chosenRoot = SkMinScalar(a, c);
+        } else {
+            chosenRoot = SkMaxScalar(a, c);
+        }
     } else {
         SkScalar aminusc = a - c;
         SkScalar apluscdiv2 = SkScalarHalf(a + c);
         SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
-        largerRoot = apluscdiv2 + x;
+        if (kMin_MinOrMax == MIN_OR_MAX) {
+            chosenRoot = apluscdiv2 - x;
+        } else {
+            chosenRoot = apluscdiv2 + x;
+        }
     }
-    return SkScalarSqrt(largerRoot);
+    SkASSERT(chosenRoot >= 0);
+    return SkScalarSqrt(chosenRoot);
+}
+
+SkScalar SkMatrix::getMinStretch() const {
+    return get_stretch_factor<kMin_MinOrMax>(this->getType(), fMat);
+}
+
+SkScalar SkMatrix::getMaxStretch() const {
+    return get_stretch_factor<kMax_MinOrMax>(this->getType(), fMat);
 }
 
 static void reset_identity_matrix(SkMatrix* identity) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 2555b41..f45572b 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1194,8 +1194,10 @@
             tileFilterPad = 1;
             textureFilterMode = GrTextureParams::kMipMap_FilterMode;
             break;
-        case SkPaint::kHigh_FilterLevel:
-            if (flags & SkCanvas::kBleed_DrawBitmapRectFlag) {
+        case SkPaint::kHigh_FilterLevel: {
+            // Minification can look bad with the bicubic effect.
+            if (fContext->getMatrix().getMinStretch() >= SK_Scalar1 &&
+                (flags & SkCanvas::kBleed_DrawBitmapRectFlag)) {
                 // We will install an effect that does the filtering in the shader.
                 textureFilterMode = GrTextureParams::kNone_FilterMode;
                 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
@@ -1207,6 +1209,7 @@
                 tileFilterPad = 1;
             }
             break;
+        }
         default:
             SkErrorInternals::SetError( kInvalidPaint_SkError,
                                         "Sorry, I don't understand the filtering "
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 00d27b3..4b5221c 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -552,7 +552,12 @@
     SkAutoSMalloc<128 * 128> tempStorage;
 
     // paletted textures cannot be partially updated
-    bool useTexStorage = isNewTexture &&
+    // We currently lazily create MIPMAPs when the we see a draw with
+    // GrTextureParams::kMipMap_FilterMode. Using texture storage requires that the
+    // MIP levels are all created when the texture is created. So for now we don't use
+    // texture storage.
+    bool useTexStorage = false &&
+                         isNewTexture &&
                          desc.fConfig != kIndex_8_GrPixelConfig &&
                          this->glCaps().texStorageSupport();
 
@@ -638,8 +643,7 @@
         desc.fWidth == width && desc.fHeight == height) {
         CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
         if (useTexStorage) {
-            // We never resize  or change formats of textures. We don't use
-            // mipmaps currently.
+            // We never resize  or change formats of textures.
             GL_ALLOC_CALL(this->glInterface(),
                           TexStorage2D(GR_GL_TEXTURE_2D,
                                        1, // levels