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     if (!provider.validForDrawing()) {
     25         return nullptr;
     26     }
     27 
     28     State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
     29     if (state) {
     30         if (nullptr == state->fPixmap.addr()) {
     31             SkInPlaceDeleteCheck(state, storage);
     32             state = nullptr;
     33         }
     34     }
     35     return state;
     36 }
     37 
     38 ///////////////////////////////////////////////////////////////////////////////////////////////////
     39 
     40 #include "SkBitmapCache.h"
     41 #include "SkBitmapScaler.h"
     42 #include "SkMipMap.h"
     43 #include "SkResourceCache.h"
     44 
     45 class SkDefaultBitmapControllerState : public SkBitmapController::State {
     46 public:
     47     SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
     48 
     49 private:
     50     SkBitmap                     fResultBitmap;
     51     SkAutoTUnref<const SkMipMap> fCurrMip;
     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 
     85     if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
     86         fInvMatrix.hasPerspective())
     87     {
     88         return false; // can't handle the reqeust
     89     }
     90 
     91     SkScalar invScaleX = fInvMatrix.getScaleX();
     92     SkScalar invScaleY = fInvMatrix.getScaleY();
     93     if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
     94         SkSize scale;
     95         if (!fInvMatrix.decomposeScale(&scale)) {
     96             return false;
     97         }
     98         invScaleX = scale.width();
     99         invScaleY = scale.height();
    100     }
    101     invScaleX = SkScalarAbs(invScaleX);
    102     invScaleY = SkScalarAbs(invScaleY);
    103 
    104     if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
    105         return false; // no need for HQ
    106     }
    107 
    108     if (invScaleX > 1 || invScaleY > 1) {
    109         return false; // only use HQ when upsampling
    110     }
    111 
    112     const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
    113     const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
    114     const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);
    115 
    116     if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
    117         SkBitmap orig;
    118         if (!provider.asBitmap(&orig)) {
    119             return false;
    120         }
    121         SkAutoPixmapUnlock src;
    122         if (!orig.requestLock(&src)) {
    123             return false;
    124         }
    125         if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
    126                                     dstW, dstH, SkResourceCache::GetAllocator())) {
    127             return false; // we failed to create fScaledBitmap
    128         }
    129 
    130         SkASSERT(fResultBitmap.getPixels());
    131         fResultBitmap.setImmutable();
    132         if (!provider.isVolatile()) {
    133             if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
    134                 provider.notifyAddedToCache();
    135             }
    136         }
    137     }
    138 
    139     SkASSERT(fResultBitmap.getPixels());
    140 
    141     fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
    142                          SkIntToScalar(dstH) / provider.height());
    143     fQuality = kLow_SkFilterQuality;
    144     return true;
    145 }
    146 
    147 /*
    148  *  Modulo internal errors, this should always succeed *if* the matrix is downscaling
    149  *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
    150  */
    151 bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
    152     SkASSERT(fQuality <= kMedium_SkFilterQuality);
    153     if (fQuality != kMedium_SkFilterQuality) {
    154         return false;
    155     }
    156 
    157     // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
    158     // to a valid bitmap.
    159     fQuality = kLow_SkFilterQuality;
    160 
    161     SkSize invScaleSize;
    162     if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
    163         return false;
    164     }
    165 
    166     if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
    167         fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
    168         if (nullptr == fCurrMip.get()) {
    169             SkBitmap orig;
    170             if (!provider.asBitmap(&orig)) {
    171                 return false;
    172             }
    173             fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
    174             if (nullptr == fCurrMip.get()) {
    175                 return false;
    176             }
    177         }
    178         // diagnostic for a crasher...
    179         if (nullptr == fCurrMip->data()) {
    180             sk_throw();
    181         }
    182 
    183         const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
    184                                           SkScalarInvert(invScaleSize.height()));
    185         SkMipMap::Level level;
    186         if (fCurrMip->extractLevel(scale, &level)) {
    187             const SkSize& invScaleFixup = level.fScale;
    188             fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
    189 
    190             // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
    191             //       that here, and not need to explicitly track it ourselves.
    192             return fResultBitmap.installPixels(level.fPixmap);
    193         } else {
    194             // failed to extract, so release the mipmap
    195             fCurrMip.reset(nullptr);
    196         }
    197     }
    198     return false;
    199 }
    200 
    201 SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
    202                                                                const SkMatrix& inv,
    203                                                                SkFilterQuality qual) {
    204     fInvMatrix = inv;
    205     fQuality = qual;
    206 
    207     if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
    208         SkASSERT(fResultBitmap.getPixels());
    209     } else {
    210         (void)provider.asBitmap(&fResultBitmap);
    211         fResultBitmap.lockPixels();
    212         // lock may fail to give us pixels
    213     }
    214     SkASSERT(fQuality <= kLow_SkFilterQuality);
    215 
    216     // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
    217     // and will destroy us if it is nullptr.
    218     fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes(),
    219                   fResultBitmap.getColorTable());
    220 }
    221 
    222 SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
    223                                                                       const SkMatrix& inverse,
    224                                                                       SkFilterQuality quality,
    225                                                                       void* storage, size_t size) {
    226     return SkInPlaceNewCheck<SkDefaultBitmapControllerState>(storage, size, bm, inverse, quality);
    227 }
    228 
    229