| Index: src/core/SkBitmapController.cpp
 | 
| diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp
 | 
| index 3c3e69413ab2e12f3dbbc66ca28fd56aa996a446..c75d2ea6bc825c497862c7193b7d74815b105669 100644
 | 
| --- a/src/core/SkBitmapController.cpp
 | 
| +++ b/src/core/SkBitmapController.cpp
 | 
| @@ -7,7 +7,9 @@
 | 
|  
 | 
|  #include "SkBitmap.h"
 | 
|  #include "SkBitmapController.h"
 | 
| +#include "SkImage_Base.h"
 | 
|  #include "SkMatrix.h"
 | 
| +#include "SkPixelRef.h"
 | 
|  #include "SkTemplates.h"
 | 
|  
 | 
|  // RESIZE_LANCZOS3 is another good option, but chrome prefers mitchell at the moment
 | 
| @@ -15,36 +17,85 @@
 | 
|  
 | 
|  ///////////////////////////////////////////////////////////////////////////////////////////////////
 | 
|  
 | 
| -static bool valid_for_drawing(const SkBitmap& bm) {
 | 
| -    if (0 == bm.width() || 0 == bm.height()) {
 | 
| -        return false;   // nothing to draw
 | 
| -    }
 | 
| -    if (nullptr == bm.pixelRef()) {
 | 
| -        return false;   // no pixels to read
 | 
| -    }
 | 
| -    if (bm.getTexture()) {
 | 
| -        // we can handle texture (ugh) since lockPixels will perform a read-back
 | 
| -        return true;
 | 
| -    }
 | 
| -    if (kIndex_8_SkColorType == bm.colorType()) {
 | 
| -        SkAutoLockPixels alp(bm); // but we need to call it before getColorTable() is safe.
 | 
| -        if (!bm.getColorTable()) {
 | 
| +int SkBitmapProvider::width() const {
 | 
| +    return fImage ? fImage->width() : fBitmap.width();
 | 
| +}
 | 
| +
 | 
| +int SkBitmapProvider::height() const {
 | 
| +    return fImage ? fImage->height() : fBitmap.height();
 | 
| +}
 | 
| +
 | 
| +uint32_t SkBitmapProvider::getID() const {
 | 
| +    return fImage ? fImage->uniqueID() : fBitmap.getGenerationID();
 | 
| +}
 | 
| +
 | 
| +bool SkBitmapProvider::validForDrawing() const {
 | 
| +    if (!fImage) {
 | 
| +        if (0 == fBitmap.width() || 0 == fBitmap.height()) {
 | 
|              return false;
 | 
|          }
 | 
| +        if (nullptr == fBitmap.pixelRef()) {
 | 
| +            return false;   // no pixels to read
 | 
| +        }
 | 
| +        if (fBitmap.getTexture()) {
 | 
| +            // we can handle texture (ugh) since lockPixels will perform a read-back
 | 
| +            return true;
 | 
| +        }
 | 
| +        if (kIndex_8_SkColorType == fBitmap.colorType()) {
 | 
| +            SkAutoLockPixels alp(fBitmap); // but we need to call it before getColorTable() is safe.
 | 
| +            if (!fBitmap.getColorTable()) {
 | 
| +                return false;
 | 
| +            }
 | 
| +        }
 | 
|      }
 | 
|      return true;
 | 
|  }
 | 
|  
 | 
| -SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmap& bm,
 | 
| +SkImageInfo SkBitmapProvider::info() const {
 | 
| +    if (fImage) {
 | 
| +        SkAlphaType at = fImage->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
 | 
| +        return SkImageInfo::MakeN32(fImage->width(), fImage->height(), at);
 | 
| +    } else {
 | 
| +        return fBitmap.info();
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc(int w, int h) const {
 | 
| +    return fImage ? SkBitmapCacheDesc::Make(fImage, w, h) : SkBitmapCacheDesc::Make(fBitmap, w, h);
 | 
| +}
 | 
| +
 | 
| +SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc() const {
 | 
| +    return fImage ? SkBitmapCacheDesc::Make(fImage) : SkBitmapCacheDesc::Make(fBitmap);
 | 
| +}
 | 
| +
 | 
| +void SkBitmapProvider::notifyAddedToCache() const {
 | 
| +    if (fImage) {
 | 
| +        // TODO
 | 
| +    } else {
 | 
| +        fBitmap.pixelRef()->notifyAddedToCache();
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +bool SkBitmapProvider::asBitmap(SkBitmap* bm) const {
 | 
| +    if (fImage) {
 | 
| +        return as_IB(fImage)->getROPixels(bm);
 | 
| +    } else {
 | 
| +        *bm = fBitmap;
 | 
| +        return true;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////////////////////////
 | 
| +
 | 
| +SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmapProvider& provider,
 | 
|                                                               const SkMatrix& inv,
 | 
|                                                               SkFilterQuality quality,
 | 
|                                                               void* storage, size_t storageSize) {
 | 
| -    
 | 
| -    if (!valid_for_drawing(bm)) {
 | 
| +    if (!provider.validForDrawing()) {
 | 
|          return nullptr;
 | 
|      }
 | 
|  
 | 
| -    State* state = this->onRequestBitmap(bm, inv, quality, storage, storageSize);
 | 
| +    State* state = this->onRequestBitmap(provider, inv, quality, storage, storageSize);
 | 
|      if (state) {
 | 
|          if (nullptr == state->fPixmap.addr()) {
 | 
|              SkInPlaceDeleteCheck(state, storage);
 | 
| @@ -63,19 +114,19 @@ SkBitmapController::State* SkBitmapController::requestBitmap(const SkBitmap& bm,
 | 
|  
 | 
|  class SkDefaultBitmapControllerState : public SkBitmapController::State {
 | 
|  public:
 | 
| -    SkDefaultBitmapControllerState(const SkBitmap& src, const SkMatrix& inv, SkFilterQuality qual);
 | 
| +    SkDefaultBitmapControllerState(const SkBitmapProvider&, const SkMatrix& inv, SkFilterQuality);
 | 
|  
 | 
|  private:
 | 
|      SkBitmap                     fResultBitmap;
 | 
|      SkAutoTUnref<const SkMipMap> fCurrMip;
 | 
|      
 | 
| -    bool processHQRequest(const SkBitmap& orig);
 | 
| -    bool processMediumRequest(const SkBitmap& orig);
 | 
| +    bool processHQRequest(const SkBitmapProvider&);
 | 
| +    bool processMediumRequest(const SkBitmapProvider&);
 | 
|  };
 | 
|  
 | 
|  // Check to see that the size of the bitmap that would be produced by
 | 
|  // scaling by the given inverted matrix is less than the maximum allowed.
 | 
| -static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
 | 
| +static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMatrix& invMat) {
 | 
|      size_t maximumAllocation = SkResourceCache::GetEffectiveSingleAllocationByteLimit();
 | 
|      if (0 == maximumAllocation) {
 | 
|          return true;
 | 
| @@ -83,7 +134,7 @@ static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
 | 
|      // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY);
 | 
|      // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize);
 | 
|      // Skip the division step:
 | 
| -    const size_t size = bm.info().getSafeSize(bm.info().minRowBytes());
 | 
| +    const size_t size = provider.info().getSafeSize(provider.info().minRowBytes());
 | 
|      return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY());
 | 
|  }
 | 
|  
 | 
| @@ -91,7 +142,7 @@ static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
 | 
|   *  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) {
 | 
| +bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
 | 
|      if (fQuality != kHigh_SkFilterQuality) {
 | 
|          return false;
 | 
|      }
 | 
| @@ -100,7 +151,7 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap
 | 
|      // 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) ||
 | 
| +    if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
 | 
|          fInvMatrix.hasPerspective())
 | 
|      {
 | 
|          return false; // can't handle the reqeust
 | 
| @@ -120,12 +171,17 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap
 | 
|          return false; // no need for HQ
 | 
|      }
 | 
|      
 | 
| -    const int dstW = SkScalarRoundToScalar(origBitmap.width() / invScaleX);
 | 
| -    const int dstH = SkScalarRoundToScalar(origBitmap.height() / invScaleY);
 | 
| -    
 | 
| -    if (!SkBitmapCache::FindWH(origBitmap, dstW, dstH, &fResultBitmap)) {
 | 
| +    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 (!origBitmap.requestLock(&src)) {
 | 
| +        if (!orig.requestLock(&src)) {
 | 
|              return false;
 | 
|          }
 | 
|          if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
 | 
| @@ -135,13 +191,15 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap
 | 
|          
 | 
|          SkASSERT(fResultBitmap.getPixels());
 | 
|          fResultBitmap.setImmutable();
 | 
| -        SkBitmapCache::AddWH(origBitmap, dstW, dstH, fResultBitmap);
 | 
| +        if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
 | 
| +            provider.notifyAddedToCache();
 | 
| +        }
 | 
|      }
 | 
|      
 | 
|      SkASSERT(fResultBitmap.getPixels());
 | 
|      
 | 
| -    fInvMatrix.postScale(SkIntToScalar(dstW) / origBitmap.width(),
 | 
| -                         SkIntToScalar(dstH) / origBitmap.height());
 | 
| +    fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
 | 
| +                         SkIntToScalar(dstH) / provider.height());
 | 
|      fQuality = kLow_SkFilterQuality;
 | 
|      return true;
 | 
|  }
 | 
| @@ -150,7 +208,7 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap
 | 
|   *  Modulo internal errors, this should always succeed *if* the matrix is downscaling
 | 
|   *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
 | 
|   */
 | 
| -bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBitmap) {
 | 
| +bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider& provider) {
 | 
|      SkASSERT(fQuality <= kMedium_SkFilterQuality);
 | 
|      if (fQuality != kMedium_SkFilterQuality) {
 | 
|          return false;
 | 
| @@ -167,9 +225,13 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi
 | 
|      SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
 | 
|      
 | 
|      if (invScale > SK_Scalar1) {
 | 
| -        fCurrMip.reset(SkMipMapCache::FindAndRef(origBitmap));
 | 
| +        fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc()));
 | 
|          if (nullptr == fCurrMip.get()) {
 | 
| -            fCurrMip.reset(SkMipMapCache::AddAndRef(origBitmap));
 | 
| +            SkBitmap orig;
 | 
| +            if (!provider.asBitmap(&orig)) {
 | 
| +                return false;
 | 
| +            }
 | 
| +            fCurrMip.reset(SkMipMapCache::AddAndRef(orig));
 | 
|              if (nullptr == fCurrMip.get()) {
 | 
|                  return false;
 | 
|              }
 | 
| @@ -185,7 +247,7 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi
 | 
|              SkScalar invScaleFixup = level.fScale;
 | 
|              fInvMatrix.postScale(invScaleFixup, invScaleFixup);
 | 
|              
 | 
| -            const SkImageInfo info = origBitmap.info().makeWH(level.fWidth, level.fHeight);
 | 
| +            const SkImageInfo info = provider.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.
 | 
|              return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes);
 | 
| @@ -197,16 +259,16 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBi
 | 
|      return false;
 | 
|  }
 | 
|  
 | 
| -SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmap& src,
 | 
| +SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmapProvider& provider,
 | 
|                                                                 const SkMatrix& inv,
 | 
|                                                                 SkFilterQuality qual) {
 | 
|      fInvMatrix = inv;
 | 
|      fQuality = qual;
 | 
|  
 | 
| -    if (this->processHQRequest(src) || this->processMediumRequest(src)) {
 | 
| +    if (this->processHQRequest(provider) || this->processMediumRequest(provider)) {
 | 
|          SkASSERT(fResultBitmap.getPixels());
 | 
|      } else {
 | 
| -        fResultBitmap = src;
 | 
| +        (void)provider.asBitmap(&fResultBitmap);
 | 
|          fResultBitmap.lockPixels();
 | 
|          // lock may fail to give us pixels
 | 
|      }
 | 
| @@ -218,7 +280,7 @@ SkDefaultBitmapControllerState::SkDefaultBitmapControllerState(const SkBitmap& s
 | 
|                    fResultBitmap.getColorTable());
 | 
|  }
 | 
|  
 | 
| -SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmap& bm,
 | 
| +SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBitmapProvider& bm,
 | 
|                                                                        const SkMatrix& inverse,
 | 
|                                                                        SkFilterQuality quality,
 | 
|                                                                        void* storage, size_t size) {
 | 
| 
 |