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@11258 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 25f90d6..9ecfc22 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 @@
 // 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);
-
             }
         }
     }