Make image scaling have floating point scales.  Third attempt to land this
due to some struggles with the CQ.  Original CL here:

https://codereview.chromium.org/298243003/

BUG=
R=bsalomon@google.com, epoger@google.com
TBR=bsalomon, epoger

Author: humper@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14920 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index d4cbff0..6fc1fe5 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -38,6 +38,35 @@
 # I will likely just delete the GM.
 canvas-layer-state
 
+# humper: https://codereview.chromium.org/300113008/
+# Needs rebaselining after image scaling tweak
+filterbitmap_image_mandrill_256
+filterbitmap_checkerboard_192_192
+downsamplebitmap_image_high_mandrill_512
+downsamplebitmap_text_high_72.00pt
+filterbitmap_text_7.00pt
+filterbitmap_image_mandrill_128
+filterbitmap_image_mandrill_64
+filterbitmap_image_mandrill_32
+filterbitmap_checkerboard_32_32
+filterbitmap_checkerboard_32_8
+downsamplebitmap_checkerboard_high_512_256
+filterbitmap_image_mandrill_16
+filterbitmap_text_3.00pt
+filterbitmap_checkerboard_32_2
+filterbitmap_checkerboard_4_4
+
+# bsalomon: https://codereview.chromium.org/264303008/
+# bsalomon@ will rebaseline this test
+ninepatch-stretch
+
+# bsalomon: https://codereview.chromium.org/282293004/
+# Conditionally fallback from bicubic filtering based on matrix.
+downsamplebitmap_checkerboard_high_512_256
+downsamplebitmap_image_high_mandrill_512.png
+filterbitmap_checkerboard_192_192
+downsamplebitmap_text_high_72.00pt
+
 # These are part of picture-version 27 -- removal of SkUnitMapp
 # just need to be rebaselined
 scaled_tilemode_bitmap
diff --git a/gm/filterindiabox.cpp b/gm/filterindiabox.cpp
new file mode 100644
index 0000000..c03dfba
--- /dev/null
+++ b/gm/filterindiabox.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkGradientShader.h"
+
+#include "SkTypeface.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+#include "SkImageEncoder.h"
+#include "SkBitmapScaler.h"
+#include "SkBitmapProcState.h"
+
+static SkSize computeSize(const SkBitmap& bm, const SkMatrix& mat) {
+    SkRect bounds = SkRect::MakeWH(SkIntToScalar(bm.width()),
+                                   SkIntToScalar(bm.height()));
+    mat.mapRect(&bounds);
+    return SkSize::Make(bounds.width(), bounds.height());
+}
+
+static void draw_row(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx) {
+    SkPaint paint;
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    canvas->drawBitmapMatrix(bm, mat, &paint);
+
+    paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+    canvas->translate(dx, 0);
+    canvas->drawBitmapMatrix(bm, mat, &paint);
+
+    paint.setFilterLevel(SkPaint::kMedium_FilterLevel);
+    canvas->translate(dx, 0);
+    canvas->drawBitmapMatrix(bm, mat, &paint);
+
+    paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+    canvas->translate(dx, 0);
+    canvas->drawBitmapMatrix(bm, mat, &paint);
+}
+
+class FilterIndiaBoxGM : public skiagm::GM {
+    void onOnceBeforeDraw() {
+
+        this->makeBitmap();
+
+        SkScalar cx = SkScalarHalf(fBM.width());
+        SkScalar cy = SkScalarHalf(fBM.height());
+
+        float vertScale = 30.0f/55.0f;
+        float horizScale = 150.0f/200.0f;
+
+        fMatrix[0].setScale(horizScale, vertScale);
+        fMatrix[1].setRotate(30, cx, cy); fMatrix[1].postScale(horizScale, vertScale);
+    }
+
+public:
+    SkBitmap    fBM;
+    SkMatrix    fMatrix[2];
+    SkString    fName;
+
+    FilterIndiaBoxGM()
+    {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    FilterIndiaBoxGM(const char filename[])
+      : fFilename(filename)
+    {
+        fName.printf("filterindiabox");
+    }
+
+protected:
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        return kSkipTiled_Flag;
+    }
+
+    virtual SkString onShortName() SK_OVERRIDE {
+        return fName;
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(1024, 768);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+        canvas->translate(10, 10);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrix); ++i) {
+            SkSize size = computeSize(fBM, fMatrix[i]);
+            size.fWidth += 20;
+            size.fHeight += 20;
+
+            draw_row(canvas, fBM, fMatrix[i], size.fWidth);
+            canvas->translate(0, size.fHeight);
+        }
+    }
+
+  protected:
+      SkString fFilename;
+      int fSize;
+
+      SkScalar getScale() {
+          return 192.f/fSize;
+      }
+
+      void makeBitmap() {
+          SkString path(skiagm::GM::gResourcePath);
+          path.append("/");
+          path.append(fFilename);
+
+          SkImageDecoder *codec = NULL;
+          SkFILEStream stream(path.c_str());
+          if (stream.isValid()) {
+              codec = SkImageDecoder::Factory(&stream);
+          }
+          if (codec) {
+              stream.rewind();
+              codec->decode(&stream, &fBM, SkBitmap::kARGB_8888_Config,
+                  SkImageDecoder::kDecodePixels_Mode);
+              SkDELETE(codec);
+          } else {
+              fBM.allocN32Pixels(1, 1);
+              *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad
+          }
+          fSize = fBM.height();
+      }
+  private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+DEF_GM( return new FilterIndiaBoxGM("box.gif"); )
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 1ebba69..15c7243 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -76,6 +76,7 @@
     '../gm/filltypes.cpp',
     '../gm/filltypespersp.cpp',
     '../gm/filterbitmap.cpp',
+    '../gm/filterindiabox.cpp',
     '../gm/fontcache.cpp',
     '../gm/fontmgr.cpp',
     '../gm/fontscaler.cpp',
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index eecfbbc..220eb56 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -15,6 +15,7 @@
 #include "SkMipMap.h"
 #include "SkPixelRef.h"
 #include "SkScaledImageCache.h"
+#include "SkImageEncoder.h"
 
 #if !SK_ARM_NEON_IS_NONE
 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
@@ -166,8 +167,13 @@
         }
 
         if (NULL == fScaledCacheID) {
-            int dest_width  = SkScalarCeilToInt(fOrigBitmap.width() / invScaleX);
-            int dest_height = SkScalarCeilToInt(fOrigBitmap.height() / invScaleY);
+            float dest_width  = fOrigBitmap.width() / invScaleX;
+            float dest_height = fOrigBitmap.height() / invScaleY;
+
+#ifdef SK_IGNORE_CORRECT_HIGH_QUALITY_IMAGE_SCALE
+            dest_width = SkScalarCeilToScalar(dest_width);
+            dest_height = SkScalarCeilToScalar(dest_height);
+#endif
 
             // All the criteria are met; let's make a new bitmap.
 
@@ -187,6 +193,7 @@
                 return false;
 
             }
+
             SkASSERT(NULL != fScaledBitmap.getPixels());
             fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
                                                             invScaleX,
diff --git a/src/core/SkBitmapScaler.cpp b/src/core/SkBitmapScaler.cpp
index 599e9d8..ebcccf2 100644
--- a/src/core/SkBitmapScaler.cpp
+++ b/src/core/SkBitmapScaler.cpp
@@ -13,8 +13,8 @@
 public:
     SkResizeFilter(SkBitmapScaler::ResizeMethod method,
                    int srcFullWidth, int srcFullHeight,
-                   int destWidth, int destHeight,
-                   const SkIRect& destSubset,
+                   float destWidth, float destHeight,
+                   const SkRect& destSubset,
                    const SkConvolutionProcs& convolveProcs);
     ~SkResizeFilter() {
         SkDELETE( fBitmapFilter );
@@ -40,7 +40,7 @@
     // for the transform is also specified.
 
     void computeFilters(int srcSize,
-                        int destSubsetLo, int destSubsetSize,
+                        float destSubsetLo, float destSubsetSize,
                         float scale,
                         SkConvolutionFilter1D* output,
                         const SkConvolutionProcs& convolveProcs);
@@ -51,8 +51,8 @@
 
 SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method,
                                int srcFullWidth, int srcFullHeight,
-                               int destWidth, int destHeight,
-                               const SkIRect& destSubset,
+                               float destWidth, float destHeight,
+                               const SkRect& destSubset,
                                const SkConvolutionProcs& convolveProcs) {
 
     // method will only ever refer to an "algorithm method".
@@ -82,10 +82,8 @@
     }
 
 
-    float scaleX = static_cast<float>(destWidth) /
-                   static_cast<float>(srcFullWidth);
-    float scaleY = static_cast<float>(destHeight) /
-                   static_cast<float>(srcFullHeight);
+    float scaleX = destWidth / srcFullWidth;
+    float scaleY = destHeight / srcFullHeight;
 
     this->computeFilters(srcFullWidth, destSubset.fLeft, destSubset.width(),
                          scaleX, &fXFilter, convolveProcs);
@@ -112,11 +110,11 @@
 // the coefficients can be shared. For periods of 1 we can consider
 // loading the factors only once outside the borders.
 void SkResizeFilter::computeFilters(int srcSize,
-                                  int destSubsetLo, int destSubsetSize,
+                                  float destSubsetLo, float destSubsetSize,
                                   float scale,
                                   SkConvolutionFilter1D* output,
                                   const SkConvolutionProcs& convolveProcs) {
-  int destSubsetHi = destSubsetLo + destSubsetSize;  // [lo, hi)
+  float destSubsetHi = destSubsetLo + destSubsetSize;  // [lo, hi)
 
   // When we're doing a magnification, the scale will be larger than one. This
   // means the destination pixels are much smaller than the source pixels, and
@@ -138,7 +136,7 @@
   // Loop over all pixels in the output range. We will generate one set of
   // filter values for each one. Those values will tell us how to blend the
   // source pixels to compute the destination pixel.
-  for (int destSubsetI = destSubsetLo; destSubsetI < destSubsetHi;
+  for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScalarCeilToInt(destSubsetHi);
        destSubsetI++) {
     // Reset the arrays. We don't declare them inside so they can re-use the
     // same malloc-ed buffer.
@@ -247,22 +245,23 @@
 bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
                             const SkBitmap& source,
                             ResizeMethod method,
-                            int destWidth, int destHeight,
-                            const SkIRect& destSubset,
+                            float destWidth, float destHeight,
                             const SkConvolutionProcs& convolveProcs,
                             SkBitmap::Allocator* allocator) {
+
+  SkRect destSubset = { 0, 0, destWidth, destHeight };
+
   // Ensure that the ResizeMethod enumeration is sound.
     SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
         (method <= RESIZE_LAST_QUALITY_METHOD)) ||
         ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
         (method <= RESIZE_LAST_ALGORITHM_METHOD)));
 
-    SkIRect dest = { 0, 0, destWidth, destHeight };
+    SkRect dest = { 0, 0, destWidth, destHeight };
     if (!dest.contains(destSubset)) {
         SkErrorInternals::SetError( kInvalidArgument_SkError,
-                                    "Sorry, you passed me a bitmap resize "
-                                    " method I have never heard of: %d",
-                                    method );
+                                    "Sorry, the destination bitmap scale subset "
+                                    "falls outside the full destination bitmap." );
     }
 
     // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
@@ -297,8 +296,8 @@
 
     // Convolve into the result.
     SkBitmap result;
-    result.setConfig(SkImageInfo::MakeN32(destSubset.width(),
-                                          destSubset.height(),
+    result.setConfig(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()),
+                                          SkScalarCeilToInt(destSubset.height()),
                                           source.alphaType()));
     result.allocPixels(allocator, NULL);
     if (!result.readyToDraw()) {
@@ -316,15 +315,3 @@
     SkASSERT(NULL != resultPtr->getPixels());
     return true;
 }
-
-// static
-bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
-                            const SkBitmap& source,
-                            ResizeMethod method,
-                            int destWidth, int destHeight,
-                            const SkConvolutionProcs& convolveProcs,
-                            SkBitmap::Allocator* allocator) {
-    SkIRect destSubset = { 0, 0, destWidth, destHeight };
-    return Resize(resultPtr, source, method, destWidth, destHeight, destSubset,
-                  convolveProcs, allocator);
-}
diff --git a/src/core/SkBitmapScaler.h b/src/core/SkBitmapScaler.h
index c8d8a84..d6636cf 100644
--- a/src/core/SkBitmapScaler.h
+++ b/src/core/SkBitmapScaler.h
@@ -79,28 +79,10 @@
         RESIZE_LAST_ALGORITHM_METHOD = RESIZE_MITCHELL,
     };
 
-    // Resizes the given source bitmap using the specified resize method, so that
-    // the entire image is (dest_size) big. The dest_subset is the rectangle in
-    // this destination image that should actually be returned.
-    //
-    // The output image will be (dest_subset.width(), dest_subset.height()). This
-    // will save work if you do not need the entire bitmap.
-    //
-    // The destination subset must be smaller than the destination image.
     static bool Resize(SkBitmap* result,
                        const SkBitmap& source,
                        ResizeMethod method,
-                       int dest_width, int dest_height,
-                       const SkIRect& dest_subset,
-                       const SkConvolutionProcs&,
-                       SkBitmap::Allocator* allocator = NULL);
-
-    // Alternate version for resizing and returning the entire bitmap rather than
-    // a subset.
-    static bool Resize(SkBitmap* result,
-                       const SkBitmap& source,
-                       ResizeMethod method,
-                       int dest_width, int dest_height,
+                       float dest_width, float dest_height,
                        const SkConvolutionProcs&,
                        SkBitmap::Allocator* allocator = NULL);
 };