Use SkBitmapCache to optimize readPixels on a texture-backed bitmap

BUG=skia:2786
R=junov@chromium.org, reed@google.com, bsalomon@google.com

Author: piotaixr@chromium.org

Review URL: https://codereview.chromium.org/533323002
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
index d5be965..8b68448 100644
--- a/src/gpu/SkGrPixelRef.cpp
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -9,8 +9,10 @@
 
 
 #include "SkGrPixelRef.h"
+
 #include "GrContext.h"
 #include "GrTexture.h"
+#include "SkBitmapCache.h"
 #include "SkGr.h"
 #include "SkRect.h"
 
@@ -143,7 +145,7 @@
     if (NULL == fSurface) {
         return NULL;
     }
-    
+
     // Note that when copying a render-target-backed pixel ref, we
     // return a texture-backed pixel ref instead.  This is because
     // render-target pixel refs are usually created in conjunction with
@@ -153,30 +155,59 @@
     return copyToTexturePixelRef(fSurface->asTexture(), dstCT, subset);
 }
 
+static bool tryAllocBitmapPixels(SkBitmap* bitmap) {
+    SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator();
+    if (NULL != allocator) {
+        return allocator->allocPixelRef(bitmap, 0);
+    } else {
+        // DiscardableMemory is not available, fallback to default allocator
+        return bitmap->tryAllocPixels();
+    }
+}
+
 bool SkGrPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
     if (NULL == fSurface || fSurface->wasDestroyed()) {
         return false;
     }
 
-    int left, top, width, height;
+    SkIRect bounds;
     if (subset) {
-        left = subset->fLeft;
-        width = subset->width();
-        top = subset->fTop;
-        height = subset->height();
+        bounds = *subset;
     } else {
-        left = 0;
-        width = this->info().width();
-        top = 0;
-        height = this->info().height();
+        bounds = SkIRect::MakeWH(this->info().width(), this->info().height());
     }
-    if (!dst->tryAllocN32Pixels(width, height)) {
-        SkDebugf("SkGrPixelRef::onReadPixels failed to alloc bitmap for result!\n");
-        return false;
-    }
-    SkAutoLockPixels al(*dst);
-    void* buffer = dst->getPixels();
-    return fSurface->readPixels(left, top, width, height,
+
+    //Check the cache
+    if(!SkBitmapCache::Find(this->getGenerationID(), bounds, dst)) {
+        //Cache miss
+
+        SkBitmap cachedBitmap;
+        cachedBitmap.setInfo(this->info().makeWH(bounds.width(), bounds.height()));
+
+        // If we can't alloc the pixels, then fail
+        if (!tryAllocBitmapPixels(&cachedBitmap)) {
+            return false;
+        }
+
+        // Try to read the pixels from the surface
+        void* buffer = cachedBitmap.getPixels();
+        bool readPixelsOk = fSurface->readPixels(bounds.fLeft, bounds.fTop,
+                                bounds.width(), bounds.height(),
                                 kSkia8888_GrPixelConfig,
-                                buffer, dst->rowBytes());
+                                buffer, cachedBitmap.rowBytes());
+
+        if (!readPixelsOk) {
+            return false;
+        }
+
+        // If we are here, pixels were read correctly from the surface.
+        cachedBitmap.setImmutable();
+        //Add to the cache
+        SkBitmapCache::Add(this->getGenerationID(), bounds, cachedBitmap);
+
+        dst->swap(cachedBitmap);
+    }
+
+    return true;
+
 }