diff options
-rw-r--r-- | expectations/gm/ignored-tests.txt | 7 | ||||
-rw-r--r-- | src/core/SkBitmapProcShader.cpp | 42 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 24 | ||||
-rw-r--r-- | src/gpu/effects/GrBicubicEffect.cpp | 33 | ||||
-rw-r--r-- | src/gpu/effects/GrBicubicEffect.h | 10 |
5 files changed, 73 insertions, 43 deletions
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index f78b79ac44..c134382420 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -45,3 +45,10 @@ ninepatch-stretch # humper: https://codereview.chromium.org/292773003/ # changed texture coordinate generation for GPU rrect blur; will rebaseline after land. simpleblurroundrect + +# 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
\ No newline at end of file diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp index 3ac26f0a19..8e25530cee 100644 --- a/src/core/SkBitmapProcShader.cpp +++ b/src/core/SkBitmapProcShader.cpp @@ -382,17 +382,6 @@ void SkBitmapProcShader::toString(SkString* str) const { #include "effects/GrSimpleTextureEffect.h" #include "SkGr.h" -// Note that this will return -1 if either matrix is perspective. -static SkScalar get_combined_min_stretch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix) { - if (localMatrix.isIdentity()) { - return viewMatrix.getMinScale(); - } else { - SkMatrix combined; - combined.setConcat(viewMatrix, localMatrix); - return combined.getMinScale(); - } -} - GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint, const SkMatrix* localMatrix) const { SkMatrix matrix; @@ -420,37 +409,32 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. - SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); + bool useBicubic = false; GrTextureParams::FilterMode textureFilterMode; - switch(paintFilterLevel) { + switch(paint.getFilterLevel()) { case SkPaint::kNone_FilterLevel: textureFilterMode = GrTextureParams::kNone_FilterMode; break; case SkPaint::kLow_FilterLevel: textureFilterMode = GrTextureParams::kBilerp_FilterMode; break; - case SkPaint::kMedium_FilterLevel: - if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) < - SK_Scalar1) { + case SkPaint::kMedium_FilterLevel: { + SkMatrix matrix; + matrix.setConcat(context->getMatrix(), this->getLocalMatrix()); + if (matrix.getMinScale() < SK_Scalar1) { textureFilterMode = GrTextureParams::kMipMap_FilterMode; } else { // Don't trigger MIP level generation unnecessarily. textureFilterMode = GrTextureParams::kBilerp_FilterMode; } break; - case SkPaint::kHigh_FilterLevel: - // Minification can look bad with bicubic filtering. - if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) >= - 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; - } + } + case SkPaint::kHigh_FilterLevel: { + SkMatrix matrix; + matrix.setConcat(context->getMatrix(), this->getLocalMatrix()); + useBicubic = GrBicubicEffect::ShouldUseBicubic(matrix, &textureFilterMode); break; + } default: SkErrorInternals::SetError( kInvalidPaint_SkError, "Sorry, I don't understand the filtering " @@ -470,7 +454,7 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& } GrEffectRef* effect = NULL; - if (paintFilterLevel == SkPaint::kHigh_FilterLevel) { + if (useBicubic) { effect = GrBicubicEffect::Create(texture, matrix, tm); } else { effect = GrSimpleTextureEffect::Create(texture, matrix, params); diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index cddf50a98c..ec471ecc68 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1170,20 +1170,16 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); GrTextureParams::FilterMode textureFilterMode; - int tileFilterPad; bool doBicubic = false; switch(paintFilterLevel) { case SkPaint::kNone_FilterLevel: - tileFilterPad = 0; textureFilterMode = GrTextureParams::kNone_FilterMode; break; case SkPaint::kLow_FilterLevel: - tileFilterPad = 1; textureFilterMode = GrTextureParams::kBilerp_FilterMode; break; case SkPaint::kMedium_FilterLevel: - tileFilterPad = 1; if (fContext->getMatrix().getMinScale() < SK_Scalar1) { textureFilterMode = GrTextureParams::kMipMap_FilterMode; } else { @@ -1193,26 +1189,26 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, break; case SkPaint::kHigh_FilterLevel: // Minification can look bad with the bicubic effect. - if (fContext->getMatrix().getMinScale() >= SK_Scalar1) { - // We will install an effect that does the filtering in the shader. - textureFilterMode = GrTextureParams::kNone_FilterMode; - tileFilterPad = GrBicubicEffect::kFilterTexelPad; - doBicubic = true; - } else { - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - tileFilterPad = 1; - } + doBicubic = + GrBicubicEffect::ShouldUseBicubic(fContext->getMatrix(), &textureFilterMode); break; default: SkErrorInternals::SetError( kInvalidPaint_SkError, "Sorry, I don't understand the filtering " "mode you asked for. Falling back to " "MIPMaps."); - tileFilterPad = 1; textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; } + int tileFilterPad; + if (doBicubic) { + tileFilterPad = GrBicubicEffect::kFilterTexelPad; + } else if (GrTextureParams::kNone_FilterMode == textureFilterMode) { + tileFilterPad = 0; + } else { + tileFilterPad = 1; + } params.setFilterMode(textureFilterMode); int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp index 89124ff667..9c6d1a3f8c 100644 --- a/src/gpu/effects/GrBicubicEffect.cpp +++ b/src/gpu/effects/GrBicubicEffect.cpp @@ -177,3 +177,36 @@ GrEffectRef* GrBicubicEffect::TestCreate(SkRandom* random, } return GrBicubicEffect::Create(textures[texIdx], coefficients); } + +////////////////////////////////////////////////////////////////////////////// + +bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix, + GrTextureParams::FilterMode* filterMode) { + if (matrix.isIdentity()) { + *filterMode = GrTextureParams::kNone_FilterMode; + return false; + } + + SkScalar scales[2]; + if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) { + // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped + // entirely, + *filterMode = GrTextureParams::kMipMap_FilterMode; + return false; + } + // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling. + if (scales[1] == SK_Scalar1) { + if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) && + SkScalarIsInt(matrix.getTranslateY())) { + *filterMode = GrTextureParams::kNone_FilterMode; + } else { + // Use bilerp to handle rotation or fractional translation. + *filterMode = GrTextureParams::kBilerp_FilterMode; + } + return false; + } + // When we use the bicubic filtering effect each sample is read from the texture using + // nearest neighbor sampling. + *filterMode = GrTextureParams::kNone_FilterMode; + return true; +} diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h index cc8b120863..1998e68780 100644 --- a/src/gpu/effects/GrBicubicEffect.h +++ b/src/gpu/effects/GrBicubicEffect.h @@ -78,6 +78,16 @@ public: return CreateEffectRef(effect); } + /** + * Determines whether the bicubic effect should be used based on the transformation from the + * local coords to the device. Returns true if the bicubic effect should be used. filterMode + * is set to appropriate filtering mode to use regardless of the return result (e.g. when this + * returns false it may indicate that the best fallback is to use kMipMap, kBilerp, or + * kNearest). + */ + static bool ShouldUseBicubic(const SkMatrix& localCoordsToDevice, + GrTextureParams::FilterMode* filterMode); + private: GrBicubicEffect(GrTexture*, const SkScalar coefficients[16], const SkMatrix &matrix, const SkShader::TileMode tileModes[2]); |