Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkBitmap.h"
      9 #include "SkBitmapController.h"
     10 #include "SkBitmapProvider.h"
     11 #include "SkMatrix.h"
     12 #include "SkPixelRef.h"
     13 #include "SkTemplates.h"
     14 
     15 // RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment
     16 #define kHQ_RESIZE_METHOD   SkBitmapScaler::RESIZE_MITCHELL
     17 
     18 ///////////////////////////////////////////////////////////////////////////////////////////////////
     19 
     20 SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
     21                                                              const SkMatrix& inv,
     22                                                              SkFilterQuality quality,
     23                                                              void* storage, size_t storageSize) {
     24     State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
     25     if (state) {
     26         if (nullptr == state->fPixmap.addr()) {
     27             SkInPlaceDeleteCheck(state, storage);
     28             state = nullptr;
     29         }
     30     }
     31     return state;
     32 }
     33 
     34 ///////////////////////////////////////////////////////////////////////////////////////////////////
     35 
     36 #include "SkBitmapCache.h"
     37 #include "SkBitmapScaler.h"
     38 #include "SkMipMap.h"
     39 #include "SkResourceCache.h"
     40 
     41 class SkDefaultBitmapControllerState : public SkBitmapController::State {
     42 public:
     43     SkDefaultBitmapControllerState(const SkBitmapProvider&,
     44                                    const SkMatrix& inv,
     45                                    SkFilterQuality,
     46                                    bool canShadeHQ);
     47 
     48 private:
     49     SkBitmap                      fResultBitmap;
     50     sk_sp<const SkMipMap>         fCurrMip;
     51     bool                          fCanShadeHQ;
     52 
     53     bool processHQRequest(const SkBitmapProvider&);
     54     bool processMediumRequest(const SkBitmapProvider&);
     55 };
     56 
     57 // Check to see that the size of the bitmap that would be produced by
     58 // scaling by the given inverted matrix is less than the maximum allowed.
     59 static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
     60     size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
     61     if (0 == maximumAllocation) {
     62         return true;
     63     }
     64     // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
     65     // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
     66     // Skip the division step:
     67     const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
     68     SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY();
     69     return size < (maximumAllocation * SkScalarAbs(invScaleSqr));
     70 }
     71 
     72 /*
     73  *  High quality is implemented by performing up-right scale-only filtering and then
     74  *  using bilerp for any remaining transformations.
     75  */
     76 bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
     77     if (fQuality != kHigh_SkFilterQuality) {
     78         return false;
     79     }
     80 
     81     // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
     82     // to a valid bitmap. If we succeed, we will set this to Low instead.
     83     fQuality = kMedium_SkFilterQuality;
     84 #ifdef SK_USE_MIP_FOR_DOWNSCALE_HQ
     85     return false;
     86 #endif
     87 
     88     bool supported = false;
     89     switch (provider.info().colorType()) {
     90         case kRGBA_8888_SkColorType:
     91         case kBGRA_8888_SkColorType:
     92             supported = true;
     93             break;
     94         default:
     95             break;
     96     }
     97     if (!supported || !cache_size_okay(provider, fInvMatrix) || fInvMatrix.hasPerspective()) {
     98         return false; // can't handle the reqeust
     99     }
    100 
    101     SkScalar invScaleX = fInvMatrix.getScaleX();
    102     SkScalar invScaleY = fInvMatrix.getScaleY();
    103     if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
    104         SkSize scale;
    105         if (!fInvMatrix.decomposeScale(&scale)) {
    106             return false;
    107         }
    108         invScaleX = scale.width();
    109         invScaleY = scale.height();
    110     }
    111     invScaleX = SkScalarAbs(invScaleX);
    112     invScaleY = SkScalarAbs(invScaleY);
    113 
    114     if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
    115         return false; // no need for HQ
    116     }
    117 
    118     if (invScaleX > 1 || invScaleY > 1) {
    119         return false; // only use HQ when upsampling
    120     }
    121 
    122     // If the shader can natively handle HQ filtering, let it do it.
    123     if (fCanShadeHQ) {
    124         fQuality = kHigh_SkFilterQuality;
    125         SkAssertResult(provider.asBitmap(&fResultBitmap));
    126         return true;
    127     }
    128 
    129     const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
    130     const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
    131     const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
    132 
    133     if (!SkBitmapCache::Find(desc, &fResultBitmap)) {
    134         SkBitmap orig;
    135         if (!provider.asBitmap(&orig)) {
    136             return false;
    137         }
    138         SkPixmap src;
    139         if (!orig.peekPixels(&src)) {
    140             return false;
    141         }
    142 
    143         SkPixmap dst;
    144         SkBitmapCache::RecPtr rec;
    145         const SkImageInfo info = SkImageInfo::Make(desc.fScaledWidth, desc.fScaledHeight,
    146                                                    src.colorType(), src.alphaType());
    147         if (provider.isVolatile()) {
    148             if (!fResultBitmap.tryAllocPixels(info)) {
    149                 return false;
    150             }
    151             SkASSERT(fResultBitmap.getPixels());
    152             fResultBitmap.peekPixels(&dst);
    153             fResultBitmap.setImmutable();   // a little cheat, as we haven't resized yet, but ok
    154         } else {
    155             rec = SkBitmapCache::Alloc(desc, info, &dst);
    156             if (!rec) {
    157                 return false;
    158             }
    159         }
    160         if (!SkBitmapScaler::Resize(dst, src, kHQ_RESIZE_METHOD)) {
    161             return false; // we failed to create fScaledBitmap
    162         }
    163         if (rec) {
    164             SkBitmapCache::Add(std::move(rec), &fResultBitmap);
    165             SkASSERT(fResultBitmap.getPixels());
    166             provider.notifyAddedToCache();
    167         }
    168     }
    169 
    170     SkASSERT(fResultBitmap.getPixels());
    171     SkASSERT(fResultBitmap.isImmutable());
    172 
    173     fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
    174                          SkIntToScalar(dstH) / provider.height());
    175     fQuality = kLow_SkFilterQuality;
    176     return true;
    177 }
    178 
    179 /*
    180  *  Modulo internal errors, this should always succeed *if* the matrix is downscaling
    181  *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
    182  */
    183 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
    184     SkASSERT(fQuality <= kMedium_SkFilterQuality);
    185     if (fQuality != kMedium_SkFilterQuality) {
    186         return false;
    187     }
    188 
    189     // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
    190     // to a valid bitmap.
    191     fQuality = kLow_SkFilterQuality;
    192 
    193     SkSize invScaleSize;
    194     if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
    195         return false;
    196     }
    197 
    198     SkDestinationSurfaceColorMode colorMode = provider.dstColorSpace()
    199         ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
    200         : SkDestinationSurfaceColorMode::kLegacy;
    201     if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
    202         fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc(), colorMode));
    203         if (nullptr == fCurrMip.get()) {
    204             SkBitmap orig;
    205             if (!provider.asBitmap(&orig)) {
    206                 return false;
    207             }
    208             fCurrMip.reset(SkMipMapCache::AddAndRef(orig, colorMode));
    209             if (nullptr == fCurrMip.get()) {
    210                 return false;
    211             }
    212         }
    213         // diagnostic for a crasher...
    214         if (nullptr == fCurrMip->data()) {
    215             sk_throw();
    216         }
    217 
    218         const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
    219                                           SkScalarInvert(invScaleSize.height()));
    220         SkMipMap::Level level;
    221         if (fCurrMip->extractLevel(scale, &level)) {
    222             const SkSize& invScaleFixup = level.fScale;
    223             fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
    224 
    225             // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
    226             //       that here, and not need to explicitly track it ourselves.
    227             return fResultBitmap.installPixels(level.fPixmap);
    228         } else {
    229             // failed to extract, so release the mipmap
    230             fCurrMip.reset(nullptr);
    231         }
    232     }
    233     return false;
    234 }
    235 
    236 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
    237                                                                const SkMatrix& inv,
    238                                                                SkFilterQuality qual,
    239                                                                bool canShadeHQ) {
    240     fInvMatrix = inv;
    241     fQuality = qual;
    242     fCanShadeHQ = canShadeHQ;
    243 
    244     bool processed = this->processHQRequest(provider) || this->processMediumRequest(provider);
    245 
    246     if (processed) {
    247         SkASSERT(fResultBitmap.getPixels());
    248     } else {
    249         (void)provider.asBitmap(&fResultBitmap);
    250     }
    251     SkASSERT(fCanShadeHQ || fQuality <= kLow_SkFilterQuality);
    252 
    253     // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
    254     // and will destroy us if it is nullptr.
    255     fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
    256 }
    257 
    258 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
    259                                                                       const SkMatrix& inverse,
    260                                                                       SkFilterQuality quality,
    261                                                                       void* storage, size_t size) {
    262     return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size,
    263                                                              bm, inverse, quality, fCanShadeHQ);
    264 }
    265