| Index: src/core/SkBitmapProcState.cpp
|
| diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
|
| index ebb010fd0a656c1ff6f996fd30cb8513e4c9f723..2298398bd5dca47028db1b14fc7df41d92802623 100644
|
| --- a/src/core/SkBitmapProcState.cpp
|
| +++ b/src/core/SkBitmapProcState.cpp
|
| @@ -91,13 +91,26 @@ static bool valid_for_filtering(unsigned dimension) {
|
| return (dimension & ~0x3FFF) == 0;
|
| }
|
|
|
| +static bool effective_matrix_scale_sqrd(const SkMatrix& mat) {
|
| + SkPoint v1, v2;
|
| +
|
| + v1.fX = mat.getScaleX();
|
| + v1.fY = mat.getSkewY();
|
| +
|
| + v2.fX = mat.getSkewX();
|
| + v2.fY = mat.getScaleY();
|
| +
|
| + return SkMaxScalar(v1.lengthSqd(), v2.lengthSqd());
|
| +}
|
| +
|
| // TODO -- we may want to pass the clip into this function so we only scale
|
| // the portion of the image that we're going to need. This will complicate
|
| // the interface to the cache, but might be well worth it.
|
|
|
| void SkBitmapProcState::possiblyScaleImage() {
|
|
|
| - if (fFilterQuality != kHQ_BitmapFilter) {
|
| + if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
|
| + // none or low (bilerp) does not need to look any further
|
| return;
|
| }
|
|
|
| @@ -125,7 +138,7 @@ void SkBitmapProcState::possiblyScaleImage() {
|
| // doing high quality scaling. If so, do the bitmap scale here and
|
| // remove the scaling component from the matrix.
|
|
|
| - if (fFilterQuality == kHQ_BitmapFilter &&
|
| + if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
|
| fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
|
| fOrigBitmap.config() == SkBitmap::kARGB_8888_Config) {
|
|
|
| @@ -148,55 +161,73 @@ void SkBitmapProcState::possiblyScaleImage() {
|
|
|
| // no need for any further filtering; we just did it!
|
|
|
| - fFilterQuality = kNone_BitmapFilter;
|
| + fFilterLevel = SkPaint::kNone_FilterLevel;
|
|
|
| return;
|
| }
|
|
|
| - if (!fOrigBitmap.hasMipMap() && fFilterQuality != kNone_BitmapFilter) {
|
| -
|
| - // STEP 2: MIPMAP DOWNSAMPLE?
|
| -
|
| - // Check to see if the transformation matrix is scaling *down*.
|
| - // If so, automatically build mipmaps.
|
| -
|
| - SkPoint v1, v2;
|
| -
|
| - // conservatively estimate if the matrix is scaling down by seeing
|
| - // what its upper left 2x2 portion does to two unit vectors.
|
| + /*
|
| + * If we get here, the caller has requested either Med or High filter-level
|
| + *
|
| + * If High, then our special-case for scale-only did not take, and so we
|
| + * have to make a choice:
|
| + * 1. fall back on mipmaps + bilerp
|
| + * 2. fall back on scanline bicubic filter
|
| + * For now, we compute the "scale" value from the matrix, and have a
|
| + * threshold to decide when bicubic is better, and when mips are better.
|
| + * No doubt a fancier decision tree could be used uere.
|
| + *
|
| + * If Medium, then we just try to build a mipmap and select a level,
|
| + * setting the filter-level to kLow to signal that we just need bilerp
|
| + * to process the selected level.
|
| + */
|
|
|
| - v1.fX = fInvMatrix.getScaleX();
|
| - v1.fY = fInvMatrix.getSkewY();
|
| + SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);
|
| +
|
| + if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
|
| + // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
|
| + // than this, then the mipmaps quality may be greater (certainly faster)
|
| + // so we only keep High quality if the scale is greater than this.
|
| + //
|
| + // Since we're dealing with the inverse, we compare against its inverse.
|
| + const SkScalar bicubicLimit = SkFloatToScalar(4.0f);
|
| + const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
|
| + if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
|
| + return;
|
| + }
|
|
|
| - v2.fX = fInvMatrix.getSkewX();
|
| - v2.fY = fInvMatrix.getScaleY();
|
| + // else set the filter-level to Medium, since we're scaling down and
|
| + // want to reqeust mipmaps
|
| + fFilterLevel = SkPaint::kMedium_FilterLevel;
|
| + }
|
| +
|
| + SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
|
|
|
| - if (v1.fX * v1.fX + v1.fY * v1.fY > 1 ||
|
| - v2.fX * v2.fX + v2.fY * v2.fY > 1) {
|
| + /**
|
| + * Medium quality means use a mipmap for down-scaling, and just bilper
|
| + * for upscaling. Since we're examining the inverse matrix, we look for
|
| + * a scale > 1 to indicate down scaling by the CTM.
|
| + */
|
| + if (scaleSqd > SK_Scalar1) {
|
| + if (!fOrigBitmap.hasMipMap()) {
|
| fOrigBitmap.buildMipMap();
|
| -
|
| - // Now that we've built the mipmaps and we know we're downsampling,
|
| - // downgrade to bilinear interpolation for the mip level.
|
| -
|
| - fFilterQuality = kBilerp_BitmapFilter;
|
| + // build may fail, so we need to check again
|
| }
|
| - }
|
| -
|
| - if (fOrigBitmap.hasMipMap()) {
|
| -
|
| - // STEP 3: We've got mipmaps, let's choose the closest level as our render
|
| - // source and adjust the matrix accordingly.
|
| -
|
| - int shift = fOrigBitmap.extractMipLevel(&fScaledBitmap,
|
| - SkScalarToFixed(fInvMatrix.getScaleX()),
|
| - SkScalarToFixed(fInvMatrix.getSkewY()));
|
| -
|
| - if (shift > 0) {
|
| - SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
|
| - fInvMatrix.postScale(scale, scale);
|
| - fBitmap = &fScaledBitmap;
|
| + if (fOrigBitmap.hasMipMap()) {
|
| + int shift = fOrigBitmap.extractMipLevel(&fScaledBitmap,
|
| + SkScalarToFixed(fInvMatrix.getScaleX()),
|
| + SkScalarToFixed(fInvMatrix.getSkewY()));
|
| + if (shift > 0) {
|
| + SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
|
| + fInvMatrix.postScale(scale, scale);
|
| + fBitmap = &fScaledBitmap;
|
| + }
|
| }
|
| }
|
| +
|
| + // Now that we've built the mipmaps (if applicable), we set the filter-level
|
| + // bilinear interpolation.
|
| + fFilterLevel = SkPaint::kLow_FilterLevel;
|
| }
|
|
|
| void SkBitmapProcState::endContext() {
|
| @@ -225,14 +256,7 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
|
| // We may downgrade it later if we determine that we either don't need
|
| // or can't provide as high a quality filtering as the user requested.
|
|
|
| - fFilterQuality = kNone_BitmapFilter;
|
| - if (paint.isFilterBitmap()) {
|
| - if (paint.getFlags() & SkPaint::kHighQualityFilterBitmap_Flag) {
|
| - fFilterQuality = kHQ_BitmapFilter;
|
| - } else {
|
| - fFilterQuality = kBilerp_BitmapFilter;
|
| - }
|
| - }
|
| + fFilterLevel = paint.getFilterLevel();
|
|
|
| #ifndef SK_IGNORE_IMAGE_PRESCALE
|
| // possiblyScaleImage will look to see if it can rescale the image as a
|
| @@ -284,7 +308,7 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
|
|
|
| trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
|
|
|
| - if (kHQ_BitmapFilter == fFilterQuality) {
|
| + if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
|
| // If this is still set, that means we wanted HQ sampling
|
| // but couldn't do it as a preprocess. Let's try to install
|
| // the scanline version of the HQ sampler. If that process fails,
|
| @@ -300,17 +324,17 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
|
|
|
| fShaderProc32 = this->chooseBitmapFilterProc();
|
| if (!fShaderProc32) {
|
| - fFilterQuality = kBilerp_BitmapFilter;
|
| + fFilterLevel = SkPaint::kLow_FilterLevel;
|
| }
|
| }
|
|
|
| - if (kBilerp_BitmapFilter == fFilterQuality) {
|
| + if (SkPaint::kLow_FilterLevel == fFilterLevel) {
|
| // Only try bilerp if the matrix is "interesting" and
|
| // the image has a suitable size.
|
|
|
| if (fInvType <= SkMatrix::kTranslate_Mask ||
|
| - !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
|
| - fFilterQuality = kNone_BitmapFilter;
|
| + !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
|
| + fFilterLevel = SkPaint::kNone_FilterLevel;
|
| }
|
| }
|
|
|
| @@ -328,7 +352,7 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
|
| // still set to HQ by the time we get here, then we must have installed
|
| // the shader proc above and can skip all this.
|
|
|
| - if (fFilterQuality < kHQ_BitmapFilter) {
|
| + if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
|
|
|
| int index = 0;
|
| if (fAlphaScale < 256) { // note: this distinction is not used for D16
|
| @@ -337,7 +361,7 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
|
| if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
|
| index |= 2;
|
| }
|
| - if (fFilterQuality != kNone_BitmapFilter) {
|
| + if (fFilterLevel > SkPaint::kNone_FilterLevel) {
|
| index |= 4;
|
| }
|
| // bits 3,4,5 encoding the source bitmap format
|
| @@ -468,7 +492,7 @@ static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
|
| SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
|
| SkASSERT(s.fInvKy == 0);
|
| SkASSERT(count > 0 && colors != NULL);
|
| - SkASSERT(SkBitmapProcState::kNone_BitmapFilter == s.fFilterQuality);
|
| + SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
|
|
|
| const int maxX = s.fBitmap->width() - 1;
|
| const int maxY = s.fBitmap->height() - 1;
|
| @@ -542,7 +566,7 @@ static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
|
| SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
|
| SkASSERT(s.fInvKy == 0);
|
| SkASSERT(count > 0 && colors != NULL);
|
| - SkASSERT(SkBitmapProcState::kNone_BitmapFilter == s.fFilterQuality);
|
| + SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
|
|
|
| const int stopX = s.fBitmap->width();
|
| const int stopY = s.fBitmap->height();
|
| @@ -588,7 +612,7 @@ static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
|
| int iY1 SK_INIT_TO_AVOID_WARNING;
|
| int iSubY SK_INIT_TO_AVOID_WARNING;
|
|
|
| - if (s.fFilterQuality != SkBitmapProcState::kNone_BitmapFilter) {
|
| + if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
|
| SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
|
| uint32_t xy[2];
|
|
|
| @@ -669,7 +693,7 @@ static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
|
| const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
|
| SkPMColor color;
|
|
|
| - if (s.fFilterQuality != SkBitmapProcState::kNone_BitmapFilter) {
|
| + if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
|
| const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
|
|
|
| if (s.fAlphaScale < 256) {
|
| @@ -725,7 +749,7 @@ SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
|
| static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
|
|
|
| if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
|
| - if (kNone_BitmapFilter == fFilterQuality &&
|
| + if (SkPaint::kNone_FilterLevel == fFilterLevel &&
|
| fInvType <= SkMatrix::kTranslate_Mask &&
|
| !this->setupForTranslate()) {
|
| return DoNothing_shaderproc;
|
| @@ -739,7 +763,7 @@ SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
|
| if (fInvType > SkMatrix::kTranslate_Mask) {
|
| return NULL;
|
| }
|
| - if (fFilterQuality != kNone_BitmapFilter) {
|
| + if (SkPaint::kNone_FilterLevel != fFilterLevel) {
|
| return NULL;
|
| }
|
|
|
| @@ -835,9 +859,9 @@ void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
|
| // scale -vs- affine
|
| // filter -vs- nofilter
|
| if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
|
| - proc = state.fFilterQuality != kNone_BitmapFilter ? check_scale_filter : check_scale_nofilter;
|
| + proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
|
| } else {
|
| - proc = state.fFilterQuality != kNone_BitmapFilter ? check_affine_filter : check_affine_nofilter;
|
| + proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
|
| }
|
| proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
|
| }
|
| @@ -872,7 +896,7 @@ int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
|
| size >>= 2;
|
| }
|
|
|
| - if (fFilterQuality != kNone_BitmapFilter) {
|
| + if (fFilterLevel != SkPaint::kNone_FilterLevel) {
|
| size >>= 1;
|
| }
|
|
|
|
|