start to remove lockPixels from bitmapshader

BUG=
R=scroggo@google.com

Review URL: https://codereview.chromium.org/23591030

git-svn-id: http://skia.googlecode.com/svn/trunk/src@11258 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/core/SkBitmapProcShader.cpp b/core/SkBitmapProcShader.cpp
index 3f65715..ded1b72 100644
--- a/core/SkBitmapProcShader.cpp
+++ b/core/SkBitmapProcShader.cpp
@@ -80,24 +80,37 @@
     return fRawBitmap.isOpaque();
 }
 
+static bool valid_for_drawing(const SkBitmap& bm) {
+    if (0 == bm.width() || 0 == bm.height()) {
+        return false;   // nothing to draw
+    }
+    if (NULL == bm.pixelRef()) {
+        return false;   // no pixels to read
+    }
+    if (SkBitmap::kIndex8_Config == bm.config()) {
+        // ugh, I have to lock-pixels to inspect the colortable
+        SkAutoLockPixels alp(bm);
+        if (!bm.getColorTable()) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool SkBitmapProcShader::setContext(const SkBitmap& device,
                                     const SkPaint& paint,
                                     const SkMatrix& matrix) {
+    if (!fRawBitmap.getTexture() && !valid_for_drawing(fRawBitmap)) {
+        return false;
+    }
+
     // do this first, so we have a correct inverse matrix
     if (!this->INHERITED::setContext(device, paint, matrix)) {
         return false;
     }
 
     fState.fOrigBitmap = fRawBitmap;
-    fState.fOrigBitmap.lockPixels();
-    if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
-        fState.fOrigBitmap.unlockPixels();
-        this->INHERITED::endContext();
-        return false;
-    }
-
     if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
-        fState.fOrigBitmap.unlockPixels();
         this->INHERITED::endContext();
         return false;
     }
@@ -147,7 +160,6 @@
 }
 
 void SkBitmapProcShader::endContext() {
-    fState.fOrigBitmap.unlockPixels();
     fState.endContext();
     this->INHERITED::endContext();
 }
diff --git a/core/SkBitmapProcState.cpp b/core/SkBitmapProcState.cpp
index 25f90d6..9ecfc22 100644
--- a/core/SkBitmapProcState.cpp
+++ b/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 @@
 // 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 @@
         SkScalar invScaleX = fInvMatrix.getScaleX();
         SkScalar invScaleY = fInvMatrix.getScaleY();
 
-        SkASSERT(NULL == fScaledCacheID);
         fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
                                                          invScaleX, invScaleY,
                                                          &fScaledBitmap);
@@ -151,7 +150,7 @@
                                         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 @@
                                                             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 @@
         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 @@
                                         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 @@
 }
 
 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 @@
     // 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 @@
                 SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
                 SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
                 fInvMatrix.setTranslate(tx, ty);
-
             }
         }
     }
diff --git a/core/SkBitmapProcState.h b/core/SkBitmapProcState.h
index 2522a69..d5a354e 100644
--- a/core/SkBitmapProcState.h
+++ b/core/SkBitmapProcState.h
@@ -13,6 +13,7 @@
 #include "SkBitmap.h"
 #include "SkBitmapFilter.h"
 #include "SkMatrix.h"
+#include "SkPaint.h"
 #include "SkScaledImageCache.h"
 
 #define FractionalInt_IS_64BIT
@@ -160,7 +161,13 @@
     bool chooseProcs(const SkMatrix& inv, const SkPaint&);
     ShaderProc32 chooseShaderProc32();
 
-    void possiblyScaleImage();
+    // returns false if we did not try to scale the image. In that case, we
+    // will need to "lock" its pixels some other way.
+    bool possiblyScaleImage();
+
+    // returns false if we failed to "lock" the pixels at all. Typically this
+    // means we have to abort the shader.
+    bool lockBaseBitmap();
 
     SkBitmapFilter* fBitmapFilter;
 
diff --git a/core/SkPixelRef.cpp b/core/SkPixelRef.cpp
index bc548a2..08775f2 100644
--- a/core/SkPixelRef.cpp
+++ b/core/SkPixelRef.cpp
@@ -167,6 +167,14 @@
     return true;
 }
 
+bool SkPixelRef::onImplementsDecodeInto() {
+    return false;
+}
+
+bool SkPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
+    return false;
+}
+
 uint32_t SkPixelRef::getGenerationID() const {
     if (0 == fGenerationID) {
         fGenerationID = SkNextPixelRefGenerationID();
diff --git a/lazy/SkLazyPixelRef.cpp b/lazy/SkLazyPixelRef.cpp
index dc9aef9..8700560 100644
--- a/lazy/SkLazyPixelRef.cpp
+++ b/lazy/SkLazyPixelRef.cpp
@@ -145,3 +145,56 @@
     fData->ref();
     return fData;
 }
+
+#include "SkImagePriv.h"
+
+static bool init_from_info(SkBitmap* bm, const SkImage::Info& info,
+                           size_t rowBytes) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+    if (SkBitmap::kNo_Config == config) {
+        return false;
+    }
+
+    bm->setConfig(config, info.fWidth, info.fHeight, rowBytes);
+    bm->setIsOpaque(isOpaque);
+    return bm->allocPixels();
+}
+
+bool SkLazyPixelRef::onImplementsDecodeInto() {
+    return true;
+}
+
+bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
+    SkASSERT(fData != NULL && fData->size() > 0);
+    if (fErrorInDecoding) {
+        return false;
+    }
+
+    SkImage::Info info;
+    // Determine the size of the image in order to determine how much memory to allocate.
+    // FIXME: As an optimization, only do this part once.
+    fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
+    if (fErrorInDecoding) {
+        return false;
+    }
+    
+    SkBitmapFactory::Target target;
+    (void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
+    
+    SkBitmap tmp;
+    if (!init_from_info(&tmp, info, target.fRowBytes)) {
+        return false;
+    }
+    
+    target.fAddr = tmp.getPixels();
+    fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
+    if (fErrorInDecoding) {
+        return false;
+    }
+
+    *bitmap = tmp;
+    return true;
+}
+
+
diff --git a/lazy/SkLazyPixelRef.h b/lazy/SkLazyPixelRef.h
index fd41dd4..c51675d 100644
--- a/lazy/SkLazyPixelRef.h
+++ b/lazy/SkLazyPixelRef.h
@@ -61,6 +61,8 @@
     virtual void onUnlockPixels() SK_OVERRIDE;
     virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
     virtual SkData* onRefEncodedData() SK_OVERRIDE;
+    virtual bool onImplementsDecodeInto() SK_OVERRIDE;
+    virtual bool onDecodeInto(int pow2, SkBitmap*) SK_OVERRIDE;
 
 private:
     bool                        fErrorInDecoding;