| Index: src/core/SkBitmapProcState.cpp
 | 
| diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
 | 
| index 25f90d60af4e76930102082d2f63c9da7028e312..9ecfc2280bd8e055afd632d34342d5677f7c256d 100644
 | 
| --- a/src/core/SkBitmapProcState.cpp
 | 
| +++ b/src/core/SkBitmapProcState.cpp
 | 
| @@ -13,6 +13,7 @@
 | 
|  #include "SkUtilsArm.h"
 | 
|  #include "SkBitmapScaler.h"
 | 
|  #include "SkMipMap.h"
 | 
| +#include "SkPixelRef.h"
 | 
|  #include "SkScaledImageCache.h"
 | 
|  
 | 
|  #if !SK_ARM_NEON_IS_NONE
 | 
| @@ -109,15 +110,14 @@ static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) {
 | 
|  // 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() {
 | 
| +bool SkBitmapProcState::possiblyScaleImage() {
 | 
| +    SkASSERT(NULL == fBitmap);
 | 
| +    SkASSERT(NULL == fScaledCacheID);
 | 
|  
 | 
|      if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
 | 
| -        // none or low (bilerp) does not need to look any further
 | 
| -        return;
 | 
| +        return false;
 | 
|      }
 | 
|  
 | 
| -    // STEP 1: Highest quality direct scale?
 | 
| -
 | 
|      // 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.
 | 
| @@ -129,7 +129,6 @@ void SkBitmapProcState::possiblyScaleImage() {
 | 
|          SkScalar invScaleX = fInvMatrix.getScaleX();
 | 
|          SkScalar invScaleY = fInvMatrix.getScaleY();
 | 
|  
 | 
| -        SkASSERT(NULL == fScaledCacheID);
 | 
|          fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
 | 
|                                                           invScaleX, invScaleY,
 | 
|                                                           &fScaledBitmap);
 | 
| @@ -151,7 +150,7 @@ void SkBitmapProcState::possiblyScaleImage() {
 | 
|                                          simd)) {
 | 
|                  // we failed to create fScaledBitmap, so just return and let
 | 
|                  // the scanline proc handle it.
 | 
| -                return;
 | 
| +                return true;
 | 
|  
 | 
|              }
 | 
|              fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
 | 
| @@ -159,25 +158,19 @@ void SkBitmapProcState::possiblyScaleImage() {
 | 
|                                                              invScaleY,
 | 
|                                                              fScaledBitmap);
 | 
|          }
 | 
| -        fScaledBitmap.lockPixels();
 | 
| -
 | 
| +        fScaledBitmap.lockPixels(); // wonder if Resize() should have locked this
 | 
|          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;
 | 
| -
 | 
| -        return;
 | 
| +        return true;
 | 
|      }
 | 
|  
 | 
|      /*
 | 
| -     *  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
 | 
| @@ -202,7 +195,7 @@ void SkBitmapProcState::possiblyScaleImage() {
 | 
|          const SkScalar bicubicLimit = SkFloatToScalar(4.0f);
 | 
|          const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
 | 
|          if (scaleSqd < bicubicLimitSqd) {  // use bicubic scanline
 | 
| -            return;
 | 
| +            return false;
 | 
|          }
 | 
|  
 | 
|          // else set the filter-level to Medium, since we're scaling down and
 | 
| @@ -247,15 +240,61 @@ void SkBitmapProcState::possiblyScaleImage() {
 | 
|                                          level.fRowBytes);
 | 
|                  fScaledBitmap.setPixels(level.fPixels);
 | 
|                  fBitmap = &fScaledBitmap;
 | 
| +                fFilterLevel = SkPaint::kLow_FilterLevel;
 | 
| +                return true;
 | 
|              }
 | 
|          }
 | 
|      }
 | 
|  
 | 
| +    return false;
 | 
| +}
 | 
| +
 | 
| +static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
 | 
| +    SkPixelRef* pr = src.pixelRef();
 | 
| +    if (pr && pr->decodeInto(pow2, dst)) {
 | 
| +        return true;
 | 
| +    }
 | 
| +
 | 
|      /*
 | 
| -     *  At this point, we may or may not have built a mipmap. Regardless, we
 | 
| -     *  now fall back on Low so will bilerp whatever fBitmap now points at.
 | 
| +     *  If decodeInto() fails, it is possibe that we have an old subclass that
 | 
| +     *  does not, or cannot, implement that. In that case we fall back to the
 | 
| +     *  older protocol of having the pixelRef handle the caching for us.
 | 
|       */
 | 
| -    fFilterLevel = SkPaint::kLow_FilterLevel;
 | 
| +    *dst = src;
 | 
| +    dst->lockPixels();
 | 
| +    return SkToBool(dst->getPixels());
 | 
| +}
 | 
| +
 | 
| +bool SkBitmapProcState::lockBaseBitmap() {
 | 
| +    SkPixelRef* pr = fOrigBitmap.pixelRef();
 | 
| +
 | 
| +    if (pr->isLocked() || !pr->implementsDecodeInto()) {
 | 
| +        // fast-case, no need to look in our cache
 | 
| +        fScaledBitmap = fOrigBitmap;
 | 
| +    } else {
 | 
| +        fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
 | 
| +                                                         SK_Scalar1, SK_Scalar1,
 | 
| +                                                         &fScaledBitmap);
 | 
| +        if (NULL == fScaledCacheID) {
 | 
| +            if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
 | 
| +                return false;
 | 
| +            }
 | 
| +
 | 
| +            // TODO: if fScaled comes back at a different width/height than fOrig,
 | 
| +            // we need to update the matrix we are using to sample from this guy.
 | 
| +
 | 
| +            fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
 | 
| +                                                            SK_Scalar1, SK_Scalar1,
 | 
| +                                                            fScaledBitmap);
 | 
| +            if (!fScaledCacheID) {
 | 
| +                fScaledBitmap.reset();
 | 
| +                return false;
 | 
| +            }
 | 
| +        }
 | 
| +    }
 | 
| +    fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :(
 | 
| +    fBitmap = &fScaledBitmap;
 | 
| +    return true;
 | 
|  }
 | 
|  
 | 
|  void SkBitmapProcState::endContext() {
 | 
| @@ -277,17 +316,10 @@ SkBitmapProcState::~SkBitmapProcState() {
 | 
|  }
 | 
|  
 | 
|  bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
 | 
| -    if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
 | 
| -        return false;
 | 
| -    }
 | 
| +    SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
 | 
|  
 | 
| -    fBitmap = &fOrigBitmap;
 | 
| +    fBitmap = NULL;
 | 
|      fInvMatrix = inv;
 | 
| -
 | 
| -    // initialize our filter quality to the one requested by the caller.
 | 
| -    // 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.
 | 
| -
 | 
|      fFilterLevel = paint.getFilterLevel();
 | 
|  
 | 
|      // possiblyScaleImage will look to see if it can rescale the image as a
 | 
| @@ -295,8 +327,13 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
 | 
|      // a nearby mipmap level.  If it does, it will adjust the working
 | 
|      // matrix as well as the working bitmap.  It may also adjust the filter
 | 
|      // quality to avoid re-filtering an already perfectly scaled image.
 | 
| -
 | 
| -    this->possiblyScaleImage();
 | 
| +    if (!this->possiblyScaleImage()) {
 | 
| +        if (!this->lockBaseBitmap()) {
 | 
| +            return false;
 | 
| +        }
 | 
| +    }
 | 
| +    
 | 
| +    SkASSERT(fBitmap);
 | 
|  
 | 
|      bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
 | 
|      bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
 | 
| @@ -322,7 +359,6 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
 | 
|                  SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
 | 
|                  SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
 | 
|                  fInvMatrix.setTranslate(tx, ty);
 | 
| -
 | 
|              }
 | 
|          }
 | 
|      }
 | 
| 
 |