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);
};