示例#1
0
/*
 *  High quality is implemented by performing up-right scale-only filtering and then
 *  using bilerp for any remaining transformations.
 */
bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap) {
    if (fQuality != kHigh_SkFilterQuality) {
        return false;
    }
    
    // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
    // to a valid bitmap. If we succeed, we will set this to Low instead.
    fQuality = kMedium_SkFilterQuality;
    
    if (kN32_SkColorType != origBitmap.colorType() || !cache_size_okay(origBitmap, fInvMatrix) ||
        fInvMatrix.hasPerspective())
    {
        return false; // can't handle the reqeust
    }
    
    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();
    if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
        SkSize scale;
        if (!fInvMatrix.decomposeScale(&scale)) {
            return false;
        }
        invScaleX = scale.width();
        invScaleY = scale.height();
    }
    if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
        return false; // no need for HQ
    }
    
    SkScalar trueDestWidth  = origBitmap.width() / invScaleX;
    SkScalar trueDestHeight = origBitmap.height() / invScaleY;
    SkScalar roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
    SkScalar roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
    
    if (!SkBitmapCache::Find(origBitmap, roundedDestWidth, roundedDestHeight, &fResultBitmap)) {
        SkAutoPixmapUnlock src;
        if (!origBitmap.requestLock(&src)) {
            return false;
        }
        if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), SkBitmapScaler::RESIZE_BEST,
                                    roundedDestWidth, roundedDestHeight,
                                    SkResourceCache::GetAllocator())) {
            return false; // we failed to create fScaledBitmap
        }
        
        SkASSERT(fResultBitmap.getPixels());
        fResultBitmap.setImmutable();
        SkBitmapCache::Add(origBitmap, roundedDestWidth, roundedDestHeight, fResultBitmap);
    }
    
    SkASSERT(fResultBitmap.getPixels());
    
    fInvMatrix.postScale(roundedDestWidth / origBitmap.width(),
                         roundedDestHeight / origBitmap.height());
    fQuality = kLow_SkFilterQuality;
    return true;
}
示例#2
0
bool SkBitmapProcState::possiblyScaleImage() {
    SkASSERT(NULL == fBitmap);

    fAdjustedMatrix = false;

    if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
        return false;
    }
    // Check to see if the transformation matrix is simple, and if we're
    // doing high quality scaling.  If so, do the bitmap scale here and
    // remove the (non-fractional) scaling component from the matrix.

    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();

    float trueDestWidth  = fOrigBitmap.width() / invScaleX;
    float trueDestHeight = fOrigBitmap.height() / invScaleY;

#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
    float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
    float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
#else
    float roundedDestWidth = trueDestWidth;
    float roundedDestHeight = trueDestHeight;
#endif

    if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
        fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
        kN32_SkColorType == fOrigBitmap.colorType() &&
        cache_size_okay(fOrigBitmap, fInvMatrix)) {

        if (SkScalarNearlyEqual(invScaleX,1.0f) &&
            SkScalarNearlyEqual(invScaleY,1.0f)) {
            // short-circuit identity scaling; the output is supposed to
            // be the same as the input, so we might as well go fast.

            // Note(humper): We could also probably do this if the scales
            // are close to -1 as well, since the flip doesn't require
            // any fancy re-sampling...

            // Set our filter level to low -- the only post-filtering this
            // image might require is some interpolation if the translation
            // is fractional.
            fFilterLevel = SkPaint::kLow_FilterLevel;
            return false;
        }

        if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) {
            // All the criteria are met; let's make a new bitmap.

            if (!SkBitmapScaler::Resize(&fScaledBitmap,
                                        fOrigBitmap,
                                        SkBitmapScaler::RESIZE_BEST,
                                        roundedDestWidth,
                                        roundedDestHeight,
                                        SkResourceCache::GetAllocator())) {
                // we failed to create fScaledBitmap, so just return and let
                // the scanline proc handle it.
                return false;

            }

            SkASSERT(fScaledBitmap.getPixels());
            fScaledBitmap.setImmutable();
            SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap);
        }

        SkASSERT(fScaledBitmap.getPixels());
        fBitmap = &fScaledBitmap;

        // set the inv matrix type to translate-only;
        fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
                                fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());

#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
        // reintroduce any fractional scaling missed by our integral scale done above.

       float fractionalScaleX = roundedDestWidth/trueDestWidth;
       float fractionalScaleY = roundedDestHeight/trueDestHeight;

       fInvMatrix.postScale(fractionalScaleX, fractionalScaleY);
#endif
        fAdjustedMatrix = true;

        // Set our filter level to low -- the only post-filtering this
        // image might require is some interpolation if the translation
        // is fractional or if there's any remaining scaling to be done.
        fFilterLevel = SkPaint::kLow_FilterLevel;
        return true;
    }

    /*
     *  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.
     */

    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 = 4.0f;
        const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
        if (scaleSqd < bicubicLimitSqd) {  // use bicubic scanline
            return false;
        }

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

    /**
     *  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) {
        fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
        if (NULL == fCurrMip.get()) {
            fCurrMip.reset(SkMipMap::Build(fOrigBitmap));
            if (NULL == fCurrMip.get()) {
                return false;
            }
            SkMipMapCache::Add(fOrigBitmap, fCurrMip);
        }

        SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
        SkMipMap::Level level;
        if (fCurrMip->extractLevel(levelScale, &level)) {
            SkScalar invScaleFixup = level.fScale;
            fInvMatrix.postScale(invScaleFixup, invScaleFixup);

            const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, level.fHeight);
            // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
            //       that here, and not need to explicitly track it ourselves.
            fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
            fBitmap = &fScaledBitmap;
            fFilterLevel = SkPaint::kLow_FilterLevel;
            return true;
        }
    }

    return false;
}
示例#3
0
bool SkBitmapProcState::possiblyScaleImage() {
    AutoScaledCacheUnlocker unlocker(&fScaledCacheID);

    SkASSERT(NULL == fBitmap);
    SkASSERT(NULL == fScaledCacheID);

    if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
        return false;
    }
    // Check to see if the transformation matrix is simple, and if we're
    // doing high quality scaling.  If so, do the bitmap scale here and
    // remove the scaling component from the matrix.

    if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
        fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
        kN32_SkColorType == fOrigBitmap.colorType() &&
        cache_size_okay(fOrigBitmap, fInvMatrix)) {

        SkScalar invScaleX = fInvMatrix.getScaleX();
        SkScalar invScaleY = fInvMatrix.getScaleY();

        fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
                                                         invScaleX, invScaleY,
                                                         &fScaledBitmap);
        if (fScaledCacheID) {
            fScaledBitmap.lockPixels();
            if (!fScaledBitmap.getPixels()) {
                fScaledBitmap.unlockPixels();
                // found a purged entry (discardablememory?), release it
                SkScaledImageCache::Unlock(fScaledCacheID);
                fScaledCacheID = NULL;
                // fall through to rebuild
            }
        }

        if (NULL == fScaledCacheID) {
            float dest_width  = fOrigBitmap.width() / invScaleX;
            float dest_height = fOrigBitmap.height() / invScaleY;

            // All the criteria are met; let's make a new bitmap.

            if (!SkBitmapScaler::Resize(&fScaledBitmap,
                                        fOrigBitmap,
                                        SkBitmapScaler::RESIZE_BEST,
                                        dest_width,
                                        dest_height,
                                        SkScaledImageCache::GetAllocator())) {
                // we failed to create fScaledBitmap, so just return and let
                // the scanline proc handle it.
                return false;

            }

            SkASSERT(NULL != fScaledBitmap.getPixels());
            fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
                                                            invScaleX,
                                                            invScaleY,
                                                            fScaledBitmap);
            if (!fScaledCacheID) {
                fScaledBitmap.reset();
                return false;
            }
            SkASSERT(NULL != fScaledBitmap.getPixels());
        }

        SkASSERT(NULL != fScaledBitmap.getPixels());
        fBitmap = &fScaledBitmap;

        // set the inv matrix type to translate-only;
        fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
                                fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());

        // no need for any further filtering; we just did it!
        fFilterLevel = SkPaint::kNone_FilterLevel;
        unlocker.release();
        return true;
    }

    /*
     *  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.
     */

    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 = 4.0f;
        const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
        if (scaleSqd < bicubicLimitSqd) {  // use bicubic scanline
            return false;
        }

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

    /**
     *  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) {
        const SkMipMap* mip = NULL;

        SkASSERT(NULL == fScaledCacheID);
        fScaledCacheID = SkScaledImageCache::FindAndLockMip(fOrigBitmap, &mip);
        if (!fScaledCacheID) {
            SkASSERT(NULL == mip);
            mip = SkMipMap::Build(fOrigBitmap);
            if (mip) {
                fScaledCacheID = SkScaledImageCache::AddAndLockMip(fOrigBitmap,
                                                                   mip);
                SkASSERT(mip->getRefCnt() > 1);
                mip->unref();   // the cache took a ref
                SkASSERT(fScaledCacheID);
            }
        } else {
            SkASSERT(mip);
        }

        if (mip) {
            SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
            SkMipMap::Level level;
            if (mip->extractLevel(levelScale, &level)) {
                SkScalar invScaleFixup = level.fScale;
                fInvMatrix.postScale(invScaleFixup, invScaleFixup);

                SkImageInfo info = fOrigBitmap.info();
                info.fWidth = level.fWidth;
                info.fHeight = level.fHeight;
                fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
                fBitmap = &fScaledBitmap;
                fFilterLevel = SkPaint::kLow_FilterLevel;
                unlocker.release();
                return true;
            }
        }
    }

    return false;
}
示例#4
0
/*
 *  High quality is implemented by performing up-right scale-only filtering and then
 *  using bilerp for any remaining transformations.
 */
bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
    if (fQuality != kHigh_SkFilterQuality) {
        return false;
    }

    // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
    // to a valid bitmap. If we succeed, we will set this to Low instead.
    fQuality = kMedium_SkFilterQuality;

    if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
            fInvMatrix.hasPerspective())
    {
        return false; // can't handle the reqeust
    }

    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();
    if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
        SkSize scale;
        if (!fInvMatrix.decomposeScale(&scale)) {
            return false;
        }
        invScaleX = scale.width();
        invScaleY = scale.height();
    }
    if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
        return false; // no need for HQ
    }

#ifndef SK_SUPPORT_LEGACY_HQ_DOWNSAMPLING
    if (invScaleX > 1 || invScaleY > 1) {
        return false; // only use HQ when upsampling
    }
#endif

    const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
    const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
    const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);

    if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
        SkBitmap orig;
        if (!provider.asBitmap(&orig)) {
            return false;
        }
        SkAutoPixmapUnlock src;
        if (!orig.requestLock(&src)) {
            return false;
        }
        if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
                                    dstW, dstH, SkResourceCache::GetAllocator())) {
            return false; // we failed to create fScaledBitmap
        }

        SkASSERT(fResultBitmap.getPixels());
        fResultBitmap.setImmutable();
        if (!provider.isVolatile()) {
            if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
                provider.notifyAddedToCache();
            }
        }
    }

    SkASSERT(fResultBitmap.getPixels());

    fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
                         SkIntToScalar(dstH) / provider.height());
    fQuality = kLow_SkFilterQuality;
    return true;
}