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