always scale to an integer; compensate for fractional image sizes by leaving the fractional scale in the matrix

BUG=skia:
R=reed@google.com

Author: humper@google.com

Review URL: https://codereview.chromium.org/470233002
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index 8663216..3bbd3b1 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -33,9 +33,30 @@
 ## epoger will rebaseline by 25 Dec 2013
 #gradtext
 
-
 # Extending fontmgr testing
 ## http://skbug.com/2829 : ignoring failures on gradtext GM test
 fontmgr_iter_factory
 fontmgr_iter
 
+# To be rebaselined (fmalita)
+textblob
+
+# reed
+matrixconvolution
+
+#humper: https://codereview.chromium.org/470233002/
+downsamplebitmap_checkerboard_high_512_256
+downsamplebitmap_image_high_mandrill_512.png
+downsamplebitmap_text_high_72.00pt
+filterbitmap_checkerboard_192_192
+filterbitmap_checkerboard_32_2
+filterbitmap_checkerboard_32_32
+filterbitmap_checkerboard_32_8
+filterbitmap_checkerboard_4_4
+filterbitmap_image_mandrill_128.png
+filterbitmap_image_mandrill_16.png
+filterbitmap_image_mandrill_256.png
+filterbitmap_image_mandrill_32.png
+filterbitmap_image_mandrill_64.png
+filterbitmap_text_3.00pt
+filterbitmap_text_7.00pt
diff --git a/gyp/skia_for_chromium_defines.gypi b/gyp/skia_for_chromium_defines.gypi
index 93674ef..53fc7b9 100644
--- a/gyp/skia_for_chromium_defines.gypi
+++ b/gyp/skia_for_chromium_defines.gypi
@@ -13,6 +13,7 @@
     # If these become 'permanent', they should be moved into skia_common.gypi
     #
     'skia_for_chromium_defines': [
+      'SK_IGNORE_PROPER_FRACTIONAL_SCALING',
       'SK_SUPPORT_LEGACY_PICTURE_CLONE',
       'SK_SUPPORT_LEGACY_GETDEVICE',
       'SK_IGNORE_ETC1_SUPPORT',
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 7640573..2583cb5 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -149,21 +149,34 @@
 bool SkBitmapProcState::possiblyScaleImage() {
     SkASSERT(NULL == fBitmap);
 
+    fAdjustedMatrix = false;
+
     if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
         return false;
     }
     // Check to see if the transformation matrix is simple, and if we're
     // doing high quality scaling.  If so, do the bitmap scale here and
-    // remove the scaling component from the matrix.
+    // remove the (non-fractional) scaling component from the matrix.
+
+    SkScalar invScaleX = fInvMatrix.getScaleX();
+    SkScalar invScaleY = fInvMatrix.getScaleY();
+
+    float trueDestWidth  = fOrigBitmap.width() / invScaleX;
+    float trueDestHeight = fOrigBitmap.height() / invScaleY;
+
+#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
+    float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
+    float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
+#else
+    float roundedDestWidth = trueDestWidth;
+    float roundedDestHeight = trueDestHeight;
+#endif
 
     if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
         fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
         kN32_SkColorType == fOrigBitmap.colorType() &&
         cache_size_okay(fOrigBitmap, fInvMatrix)) {
 
-        SkScalar invScaleX = fInvMatrix.getScaleX();
-        SkScalar invScaleY = fInvMatrix.getScaleY();
-
         if (SkScalarNearlyEqual(invScaleX,1.0f) &&
             SkScalarNearlyEqual(invScaleY,1.0f)) {
             // short-circuit identity scaling; the output is supposed to
@@ -180,17 +193,14 @@
             return false;
         }
 
-        if (!SkBitmapCache::Find(fOrigBitmap, invScaleX, invScaleY, &fScaledBitmap)) {
-            float dest_width  = fOrigBitmap.width() / invScaleX;
-            float dest_height = fOrigBitmap.height() / invScaleY;
-
+        if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) {
             // All the criteria are met; let's make a new bitmap.
 
             if (!SkBitmapScaler::Resize(&fScaledBitmap,
                                         fOrigBitmap,
                                         SkBitmapScaler::RESIZE_BEST,
-                                        dest_width,
-                                        dest_height,
+                                        roundedDestWidth,
+                                        roundedDestHeight,
                                         SkScaledImageCache::GetAllocator())) {
                 // we failed to create fScaledBitmap, so just return and let
                 // the scanline proc handle it.
@@ -199,7 +209,7 @@
             }
 
             SkASSERT(NULL != fScaledBitmap.getPixels());
-            SkBitmapCache::Add(fOrigBitmap, invScaleX, invScaleY, fScaledBitmap);
+            SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap);
         }
 
         SkASSERT(NULL != fScaledBitmap.getPixels());
@@ -209,9 +219,19 @@
         fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
                                 fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
 
+#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
+        // reintroduce any fractional scaling missed by our integral scale done above.
+
+        float fractionalScaleX = trueDestWidth/roundedDestWidth;
+        float fractionalScaleY = trueDestHeight/roundedDestHeight;
+
+        fInvMatrix.postScale(fractionalScaleX, fractionalScaleY);
+#endif
+        fAdjustedMatrix = true;
+
         // Set our filter level to low -- the only post-filtering this
         // image might require is some interpolation if the translation
-        // is fractional.
+        // is fractional or if there's any remaining scaling to be done.
         fFilterLevel = SkPaint::kLow_FilterLevel;
         return true;
     }
@@ -367,7 +387,7 @@
     bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
                       SkShader::kClamp_TileMode == fTileModeY;
 
-    if (!(clampClamp || trivialMatrix)) {
+    if (!(fAdjustedMatrix || clampClamp || trivialMatrix)) {
         fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
     }
 
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
index 64dda2e..4e4eded 100644
--- a/src/core/SkBitmapProcState.h
+++ b/src/core/SkBitmapProcState.h
@@ -144,6 +144,7 @@
     SkBitmap            fScaledBitmap;      // chooseProcs
 
     SkAutoTUnref<const SkMipMap> fCurrMip;
+    bool                fAdjustedMatrix;    // set by possiblyScaleImage
 
     MatrixProc chooseMatrixProc(bool trivial_matrix);
     bool chooseProcs(const SkMatrix& inv, const SkPaint&);
diff --git a/tests/ScaledImageCache.cpp b/tests/ScaledImageCache.cpp
index 276a3cc..ffab2ad 100644
--- a/tests/ScaledImageCache.cpp
+++ b/tests/ScaledImageCache.cpp
@@ -17,7 +17,9 @@
                                      SkScalar xScale,
                                      SkScalar yScale) {
     SkBitmap scaled;
-    return SkBitmapCache::Find(orig, SkScalarInvert(xScale), SkScalarInvert(yScale), &scaled);
+    float roundedImageWidth = SkScalarRoundToScalar(orig.width() * xScale);
+    float roundedImageHeight = SkScalarRoundToScalar(orig.height() * xScale);
+    return SkBitmapCache::Find(orig, roundedImageWidth, roundedImageHeight, &scaled);
 }
 
 // Draw a scaled bitmap, then return true iff it has been cached.