Big Cleanup: SkBitmapFactory, SkLazyPixelRef, SkImageCache

Removed SkBitmapFactory since no clients were using it.  New cache
selection mechanism can simply pass a SkDiscardableMemory::Factory
into the SkDiscardablePixelRef if non-default SkDiscardableMemory
should be used.  Removed BitmapFactoryTest.

SkDiscardableMemory::Factory interface.  Android will need this
functionality in the future inside their BitmapFactory.

Removed SkLazyPixelRef, since it's functionality is now subsumed into
SkDiscardablePixelRef.  Removed LazyPixelRef test.

Modified SkDiscardablePixelRef to optionally allow it to use a
SkDiscardableMemory::Factory.  This tiny change makes it a replacement
for SkLazyPixelRef.  This functioanlity is also necessary for moving
Android over to SkDiscardablePixelRef from SkImageRef in a later CL.
Added a test for this.

SkDecodingImageGenerator::Install can optionally pass a factory in to
SkDiscardablePixelRef.

Removed SkImageCache, SkLruImageCache, and SkPurgeableImageCache.
This functionality can be handled much more cleanly by
SkDiscardableMemory.

New SkDiscardableMemoryPool class to replace SkLruImageCache.  In a
later CL, we will replace SkImageRef_GlobalPool (used by android) as
well.  This is a concrete implementation of
SkDiscardableMemory::Factory.  Added a test for this.

modified gm/factory.cpp to remove dependnce on SkBitmapFactory +
SkLruImageCache.  Now uses SkDecodingImageGenerator +
SkDiscardablePixelRef + SkDiscardableMemoryPool.

SkImageDecoder::Target replaces SkBitmapFactory::Target.  The
DecodeMemoryToTarget function may disappear in the future.

Moved SkLazyCachingPixelRef::DecodeProc replaces
SkBitmapFactory::DecodeProc.  This is a short term change, since
another CL changes SkLazyCachingPixelRef to use SkImageGenerator
instead of DecodeProc.

Modified DrawBitmapRectTest to use SkDiscardablePixelRef instead of
SkLazyPixelRef.

tools/LazyDecodeBitmap.cpp now uses SkDecodingImageGenerator +
SkDiscardablePixelRef instead of a SkBitmapFactory.

bench_pictures uses the Global SkDiscardableMemoryPool instead of a
global gLruImageCache.

R=reed@google.com, scroggo@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12515 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkDiscardableMemory.h b/src/core/SkDiscardableMemory.h
index e1634ee..f3159fe 100644
--- a/src/core/SkDiscardableMemory.h
+++ b/src/core/SkDiscardableMemory.h
@@ -8,6 +8,7 @@
 #ifndef SkDiscardableMemory_DEFINED
 #define SkDiscardableMemory_DEFINED
 
+#include "SkRefCnt.h"
 #include "SkTypes.h"
 
 /**
@@ -15,7 +16,6 @@
  *  embedder.
  */
 class SK_API SkDiscardableMemory {
-
 public:
     /**
      *  Factory method that creates, initializes and locks an SkDiscardableMemory
@@ -23,6 +23,17 @@
      */
     static SkDiscardableMemory* Create(size_t bytes);
 
+    /**
+     *  Factory class that creates, initializes and locks an SkDiscardableMemory
+     *  object. If either of these steps fails, a NULL pointer will be returned.
+     */
+    class Factory : public SkRefCnt {
+    public:
+        virtual SkDiscardableMemory* create(size_t bytes) = 0;
+    private:
+        typedef SkRefCnt INHERITED;
+    };
+
     /** Must not be called while locked.
      */
     virtual ~SkDiscardableMemory() {}
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
index 65fa6fd..05651c7 100644
--- a/src/images/SkDecodingImageGenerator.cpp
+++ b/src/images/SkDecodingImageGenerator.cpp
@@ -6,8 +6,6 @@
  */
 
 #include "SkDecodingImageGenerator.h"
-
-#include "SkBitmapFactory.h"
 #include "SkData.h"
 #include "SkDiscardablePixelRef.h"
 #include "SkImageDecoder.h"
@@ -23,6 +21,7 @@
 }
 
 SkData* SkDecodingImageGenerator::refEncodedData() {
+    // This functionality is used in `gm --serialize`
     fData->ref();
     return fData;
 }
@@ -38,15 +37,16 @@
                                          void* pixels,
                                          size_t rowBytes) {
     SkASSERT(pixels != NULL);
-    SkBitmapFactory::Target target = {pixels, rowBytes};
+    SkImageDecoder::Target target = {pixels, rowBytes};
     SkImageInfo tmpInfo = info;
     return SkImageDecoder::DecodeMemoryToTarget(fData->data(),
                                                 fData->size(),
                                                 &tmpInfo, &target);
 }
-bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst) {
+bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst,
+                                       SkDiscardableMemory::Factory* factory) {
     SkASSERT(data != NULL);
     SkASSERT(dst != NULL);
     SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data)));
-    return SkDiscardablePixelRef::Install(gen, dst);
+    return SkDiscardablePixelRef::Install(gen, dst, factory);
 }
diff --git a/src/images/SkDecodingImageGenerator.h b/src/images/SkDecodingImageGenerator.h
index 682aeb6..49f1295 100644
--- a/src/images/SkDecodingImageGenerator.h
+++ b/src/images/SkDecodingImageGenerator.h
@@ -8,6 +8,7 @@
 #ifndef SkDecodingImageGenerator_DEFINED
 #define SkDecodingImageGenerator_DEFINED
 
+#include "SkDiscardableMemory.h"
 #include "SkImageGenerator.h"
 
 class SkBitmap;
@@ -36,8 +37,21 @@
     /**
      *  Install the SkData into the destination bitmap, using a new
      *  SkDiscardablePixelRef and a new SkDecodingImageGenerator.
+     *
+     *  @param data Contains the encoded image data that will be used
+     *  by the SkDecodingImageGenerator.  Will be ref()ed.
+     *
+     *  @param destination Upon success, this bitmap will be
+     *  configured and have a pixelref installed.
+     *
+     *  @param factory If not NULL, this object will be used as a
+     *  source of discardable memory when decoding.  If NULL, then
+     *  SkDiscardableMemory::Create() will be called.
+     *
+     *  @return true iff successful.
      */
-    static bool Install(SkData* data, SkBitmap* destination);
+    static bool Install(SkData* data, SkBitmap* destination,
+                        SkDiscardableMemory::Factory* factory = NULL);
 
 private:
     SkData* fData;
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 5fb3c72..ee7ad5e 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -387,7 +387,7 @@
 
 bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
                                           SkImageInfo* info,
-                                          const SkBitmapFactory::Target* target) {
+                                          const SkImageDecoder::Target* target) {
     // FIXME: Just to get this working, implement in terms of existing
     // ImageDecoder calls.
     SkBitmap bm;
diff --git a/src/lazy/SkBitmapFactory.cpp b/src/lazy/SkBitmapFactory.cpp
deleted file mode 100644
index 17ecf47..0000000
--- a/src/lazy/SkBitmapFactory.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmapFactory.h"
-
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkImageCache.h"
-#include "SkImagePriv.h"
-#include "SkLazyPixelRef.h"
-
-SkBitmapFactory::SkBitmapFactory(SkBitmapFactory::DecodeProc proc)
-    : fDecodeProc(proc)
-    , fImageCache(NULL)
-    , fCacheSelector(NULL) {
-    SkASSERT(fDecodeProc != NULL);
-}
-
-SkBitmapFactory::~SkBitmapFactory() {
-    SkSafeUnref(fImageCache);
-    SkSafeUnref(fCacheSelector);
-}
-
-void SkBitmapFactory::setImageCache(SkImageCache *cache) {
-    SkRefCnt_SafeAssign(fImageCache, cache);
-    if (cache != NULL) {
-        SkSafeUnref(fCacheSelector);
-        fCacheSelector = NULL;
-    }
-}
-
-void SkBitmapFactory::setCacheSelector(CacheSelector* selector) {
-    SkRefCnt_SafeAssign(fCacheSelector, selector);
-    if (selector != NULL) {
-        SkSafeUnref(fImageCache);
-        fImageCache = NULL;
-    }
-}
-
-bool SkBitmapFactory::installPixelRef(SkData* data, SkBitmap* dst) {
-    if (NULL == data || 0 == data->size() || dst == NULL) {
-        return false;
-    }
-
-    SkImageInfo info;
-    if (!fDecodeProc(data->data(), data->size(), &info, NULL)) {
-        return false;
-    }
-
-    SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
-
-    Target target;
-    // FIMXE: There will be a problem if this rowbytes is calculated differently from
-    // in SkLazyPixelRef.
-    target.fRowBytes = SkImageMinRowBytes(info);
-    dst->setConfig(config, info.fWidth, info.fHeight, target.fRowBytes, info.fAlphaType);
-
-    // fImageCache and fCacheSelector are mutually exclusive.
-    SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
-
-    SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector->selectCache(info);
-
-    if (cache != NULL) {
-        // Now set a new LazyPixelRef on dst.
-        SkAutoTUnref<SkLazyPixelRef> lazyRef(SkNEW_ARGS(SkLazyPixelRef,
-                                                        (data, fDecodeProc, cache)));
-        dst->setPixelRef(lazyRef);
-        return true;
-    } else {
-        dst->allocPixels();
-        target.fAddr = dst->getPixels();
-        return fDecodeProc(data->data(), data->size(), &info, &target);
-    }
-}
diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp
new file mode 100644
index 0000000..a1b2438
--- /dev/null
+++ b/src/lazy/SkDiscardableMemoryPool.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDiscardableMemoryPool.h"
+#include "SkOnce.h"
+
+// Note:
+// A PoolDiscardableMemory is memory that is counted in a pool.
+// A DiscardableMemoryPool is a pool of PoolDiscardableMemorys.
+
+/**
+ *  A SkPoolDiscardableMemory is a SkDiscardableMemory that relies on
+ *  a SkDiscardableMemoryPool object to manage the memory.
+ */
+class SkPoolDiscardableMemory : public SkDiscardableMemory {
+public:
+    SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
+                            void* pointer, size_t bytes);
+    virtual ~SkPoolDiscardableMemory();
+    virtual bool lock() SK_OVERRIDE;
+    virtual void* data() SK_OVERRIDE;
+    virtual void unlock() SK_OVERRIDE;
+    friend class SkDiscardableMemoryPool;
+private:
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(SkPoolDiscardableMemory);
+    SkDiscardableMemoryPool* const fPool;
+    bool                           fLocked;
+    void*                          fPointer;
+    const size_t                   fBytes;
+};
+
+SkPoolDiscardableMemory::SkPoolDiscardableMemory(SkDiscardableMemoryPool* pool,
+                                                 void* pointer,
+                                                 size_t bytes)
+    : fPool(pool)
+    , fLocked(true)
+    , fPointer(pointer)
+    , fBytes(bytes) {
+    SkASSERT(fPool != NULL);
+    SkASSERT(fPointer != NULL);
+    SkASSERT(fBytes > 0);
+    fPool->ref();
+}
+
+SkPoolDiscardableMemory::~SkPoolDiscardableMemory() {
+    fPool->free(this);
+    fPool->unref();
+}
+
+bool SkPoolDiscardableMemory::lock() {
+    return fPool->lock(this);
+}
+
+void* SkPoolDiscardableMemory::data() {
+    return fLocked ? fPointer : NULL;
+}
+
+void SkPoolDiscardableMemory::unlock() {
+    fPool->unlock(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkDiscardableMemoryPool::SkDiscardableMemoryPool(size_t budget,
+                                                 SkBaseMutex* mutex)
+    : fMutex(mutex)
+    , fBudget(budget)
+    , fUsed(0) {
+    #if LAZY_CACHE_STATS
+    fCacheHits = 0;
+    fCacheMisses = 0;
+    #endif  // LAZY_CACHE_STATS
+}
+SkDiscardableMemoryPool::~SkDiscardableMemoryPool() {
+    // SkPoolDiscardableMemory objects that belong to this pool are
+    // always deleted before deleting this pool since each one has a
+    // ref to the pool.
+    SkASSERT(fList.isEmpty());
+}
+
+void SkDiscardableMemoryPool::dumpDownTo(size_t budget) {
+    // assert((NULL = fMutex) || fMutex->isLocked());
+    // TODO(halcanary) implement bool fMutex::isLocked().
+    // WARNING: only call this function after aquiring lock.
+    if (fUsed <= budget) {
+        return;
+    }
+    typedef SkTInternalLList<SkPoolDiscardableMemory>::Iter Iter;
+    Iter iter;
+    SkPoolDiscardableMemory* cur = iter.init(fList, Iter::kTail_IterStart);
+    while ((fUsed > budget) && (NULL != cur)) {
+        if (!cur->fLocked) {
+            SkPoolDiscardableMemory* dm = cur;
+            SkASSERT(dm->fPointer != NULL);
+            sk_free(dm->fPointer);
+            dm->fPointer = NULL;
+            SkASSERT(fUsed >= dm->fBytes);
+            fUsed -= dm->fBytes;
+            cur = iter.prev();
+            // Purged DMs are taken out of the list.  This saves times
+            // looking them up.  Purged DMs are NOT deleted.
+            fList.remove(dm);
+        } else {
+            cur = iter.prev();
+        }
+    }
+}
+
+SkDiscardableMemory* SkDiscardableMemoryPool::create(size_t bytes) {
+    void* addr = sk_malloc_flags(bytes, 0);
+    if (NULL == addr) {
+        return NULL;
+    }
+    SkPoolDiscardableMemory* dm = SkNEW_ARGS(SkPoolDiscardableMemory,
+                                             (this, addr, bytes));
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
+    fList.addToHead(dm);
+    fUsed += bytes;
+    this->dumpDownTo(fBudget);
+    return dm;
+}
+
+void SkDiscardableMemoryPool::free(SkPoolDiscardableMemory* dm) {
+    // This is called by dm's destructor.
+    if (dm->fPointer != NULL) {
+        SkAutoMutexAcquire autoMutexAcquire(fMutex);
+        sk_free(dm->fPointer);
+        dm->fPointer = NULL;
+        SkASSERT(fUsed >= dm->fBytes);
+        fUsed -= dm->fBytes;
+        fList.remove(dm);
+    } else {
+        SkASSERT(!fList.isInList(dm));
+    }
+}
+
+bool SkDiscardableMemoryPool::lock(SkPoolDiscardableMemory* dm) {
+    SkASSERT(dm != NULL);
+    if (NULL == dm->fPointer) {
+        #if LAZY_CACHE_STATS
+        SkAutoMutexAcquire autoMutexAcquire(fMutex);
+        ++fCacheMisses;
+        #endif  // LAZY_CACHE_STATS
+        return false;
+    }
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
+    if (NULL == dm->fPointer) {
+        // May have been purged while waiting for lock.
+        #if LAZY_CACHE_STATS
+        ++fCacheMisses;
+        #endif  // LAZY_CACHE_STATS
+        return false;
+    }
+    dm->fLocked = true;
+    fList.remove(dm);
+    fList.addToHead(dm);
+    #if LAZY_CACHE_STATS
+    ++fCacheHits;
+    #endif  // LAZY_CACHE_STATS
+    return true;
+}
+
+void SkDiscardableMemoryPool::unlock(SkPoolDiscardableMemory* dm) {
+    SkASSERT(dm != NULL);
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
+    dm->fLocked = false;
+    this->dumpDownTo(fBudget);
+}
+
+size_t SkDiscardableMemoryPool::getRAMUsed() {
+    return fUsed;
+}
+void SkDiscardableMemoryPool::setRAMBudget(size_t budget) {
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
+    fBudget = budget;
+    this->dumpDownTo(fBudget);
+}
+void SkDiscardableMemoryPool::dumpPool() {
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
+    this->dumpDownTo(0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+SK_DECLARE_STATIC_MUTEX(gMutex);
+static void create_pool(SkDiscardableMemoryPool** pool) {
+    SkASSERT(NULL == *pool);
+    *pool = SkNEW_ARGS(SkDiscardableMemoryPool,
+                       (SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE,
+                        &gMutex));
+}
+SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() {
+    static SkDiscardableMemoryPool* gPool(NULL);
+    SK_DECLARE_STATIC_ONCE(create_pool_once);
+    SkOnce(&create_pool_once, create_pool, &gPool);
+    SkASSERT(NULL != gPool);
+    return gPool;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/lazy/SkDiscardableMemoryPool.h b/src/lazy/SkDiscardableMemoryPool.h
new file mode 100644
index 0000000..0939260
--- /dev/null
+++ b/src/lazy/SkDiscardableMemoryPool.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDiscardableMemoryPool_DEFINED
+#define SkDiscardableMemoryPool_DEFINED
+
+#include "SkDiscardableMemory.h"
+#include "SkTInternalLList.h"
+#include "SkThread.h"
+
+#ifdef SK_DEBUG
+    #define LAZY_CACHE_STATS 1
+#elif !defined(LAZY_CACHE_STATS)
+    #define LAZY_CACHE_STATS 0
+#endif
+
+/**
+ *  This non-global pool can be used for unit tests to verify that the
+ *  pool works.
+ */
+class SkPoolDiscardableMemory;
+class SkDiscardableMemoryPool : public SkDiscardableMemory::Factory {
+public:
+    /**
+     *  Without mutex, will be not be thread safe.
+     */
+    SkDiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
+    ~SkDiscardableMemoryPool();
+    SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
+    size_t getRAMUsed();
+    void setRAMBudget(size_t budget);
+    /** purges all unlocked DMs */
+    void dumpPool();
+    friend class SkPoolDiscardableMemory;
+    #if LAZY_CACHE_STATS
+    int          fCacheHits;
+    int          fCacheMisses;
+    #endif  // LAZY_CACHE_STATS
+
+private:
+    SkBaseMutex* fMutex;
+    size_t       fBudget;
+    size_t       fUsed;
+    SkTInternalLList<SkPoolDiscardableMemory> fList;
+    /** Function called to free memory if needed */
+    void dumpDownTo(size_t budget);
+    /** called by SkDiscardableMemoryPool upon destruction */
+    void free(SkPoolDiscardableMemory* dm);
+    /** called by SkDiscardableMemoryPool::lock() */
+    bool lock(SkPoolDiscardableMemory* dm);
+    /** called by SkDiscardableMemoryPool::unlock() */
+    void unlock(SkPoolDiscardableMemory* dm);
+    typedef SkDiscardableMemory::Factory INHERITED;
+};
+
+/**
+ *  Returns (and creates if needed) a threadsafe global
+ *  SkDiscardableMemoryPool.
+ */
+SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool();
+
+#if !defined(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE)
+#define SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE (128 * 1024 * 1024)
+#endif
+
+#endif  // SkDiscardableMemoryPool_DEFINED
+
diff --git a/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp
index 4e19c7f..e614db3 100644
--- a/src/lazy/SkDiscardablePixelRef.cpp
+++ b/src/lazy/SkDiscardablePixelRef.cpp
@@ -11,8 +11,10 @@
 SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator,
                                              const SkImageInfo& info,
                                              size_t size,
-                                             size_t rowBytes)
+                                             size_t rowBytes,
+                                             SkDiscardableMemory::Factory* fact)
     : fGenerator(generator)
+    , fDMFactory(fact)
     , fInfo(info)
     , fSize(size)
     , fRowBytes(rowBytes)
@@ -23,9 +25,12 @@
     // The SkImageGenerator contract requires fGenerator to always
     // decode the same image on each call to getPixels().
     this->setImmutable();
+    SkSafeRef(fDMFactory);
 }
 
 SkDiscardablePixelRef::~SkDiscardablePixelRef() {
+    SkDELETE(fDiscardableMemory);
+    SkSafeUnref(fDMFactory);
     SkDELETE(fGenerator);
 }
 
@@ -34,9 +39,14 @@
         if (fDiscardableMemory->lock()) {
             return fDiscardableMemory->data();
         }
+        SkDELETE(fDiscardableMemory);
         fDiscardableMemory = NULL;
     }
-    fDiscardableMemory = SkDiscardableMemory::Create(fSize);
+    if (fDMFactory != NULL) {
+        fDiscardableMemory = fDMFactory->create(fSize);
+    } else {
+        fDiscardableMemory = SkDiscardableMemory::Create(fSize);
+    }
     if (NULL == fDiscardableMemory) {
         return NULL;  // Memory allocation failed.
     }
@@ -53,19 +63,22 @@
 }
 
 bool SkDiscardablePixelRef::Install(SkImageGenerator* generator,
-                                    SkBitmap* dst) {
+                                    SkBitmap* dst,
+                                    SkDiscardableMemory::Factory* factory) {
     SkImageInfo info;
     SkASSERT(generator != NULL);
     if ((NULL == generator)
         || (!generator->getInfo(&info))
-        || (!dst->setConfig(info, 0))) {
+        || (!dst->setConfig(info, 0))
+        || (0 == dst->getSize())) {  // dst->getSize=0 Probably a bad config
         SkDELETE(generator);
         return false;
     }
     SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef,
                                                    (generator, info,
                                                     dst->getSize(),
-                                                    dst->rowBytes())));
+                                                    dst->rowBytes(),
+                                                    factory)));
     dst->setPixelRef(ref);
     return true;
 }
diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
index bbe19b8..78dcd66 100644
--- a/src/lazy/SkDiscardablePixelRef.h
+++ b/src/lazy/SkDiscardablePixelRef.h
@@ -8,16 +8,18 @@
 #ifndef SkDiscardablePixelRef_DEFINED
 #define SkDiscardablePixelRef_DEFINED
 
+#include "SkDiscardableMemory.h"
 #include "SkPixelRef.h"
 #include "SkImageGenerator.h"
 #include "SkImageInfo.h"
 
-class SkDiscardableMemory;
-
 /**
  * An interface that allows a purgable PixelRef to re-decode an image.
  */
 
+typedef SkDiscardableMemory* (*SkDiscardableMemoryFactory)(size_t bytes);
+
+
 class SkDiscardablePixelRef : public SkPixelRef {
 public:
     /**
@@ -30,8 +32,19 @@
      *  installed into destination is destroyed, it will call
      *  SkDELETE() on the generator.  Therefore, generator should be
      *  allocated with SkNEW() or SkNEW_ARGS().
+     *
+     *  @param destination Upon success, this bitmap will be
+     *  configured and have a pixelref installed.
+     *
+     *  @param factory If not NULL, this object will be used as a
+     *  source of discardable memory when decoding.  If NULL, then
+     *  SkDiscardableMemory::Create() will be called.
+     *
+     *  @return true iff successful.
      */
-    static bool Install(SkImageGenerator* generator, SkBitmap* destination);
+    static bool Install(SkImageGenerator* generator,
+                        SkBitmap* destination,
+                        SkDiscardableMemory::Factory* factory = NULL);
 
     SK_DECLARE_UNFLATTENABLE_OBJECT()
 
@@ -47,6 +60,7 @@
 
 private:
     SkImageGenerator* const fGenerator;
+    SkDiscardableMemory::Factory* const fDMFactory;
     const SkImageInfo fInfo;
     const size_t fSize;  // size of memory to be allocated
     const size_t fRowBytes;
@@ -59,6 +73,7 @@
     SkDiscardablePixelRef(SkImageGenerator* generator,
                           const SkImageInfo& info,
                           size_t size,
-                          size_t rowBytes);
+                          size_t rowBytes,
+                          SkDiscardableMemory::Factory* factory);
 };
 #endif  // SkDiscardablePixelRef_DEFINED
diff --git a/src/lazy/SkLazyPixelRef.cpp b/src/lazy/SkLazyPixelRef.cpp
deleted file mode 100644
index c2ca041..0000000
--- a/src/lazy/SkLazyPixelRef.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Sk64.h"
-#include "SkLazyPixelRef.h"
-#include "SkColorTable.h"
-#include "SkData.h"
-#include "SkImageCache.h"
-#include "SkImagePriv.h"
-#include "SkScaledImageCache.h"
-
-#if LAZY_CACHE_STATS
-#include "SkThread.h"
-
-int32_t SkLazyPixelRef::gCacheHits;
-int32_t SkLazyPixelRef::gCacheMisses;
-#endif
-
-SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, SkImageCache* cache)
-    // Pass NULL for the Mutex so that the default (ring buffer) will be used.
-    : INHERITED(NULL)
-    , fErrorInDecoding(false)
-    , fDecodeProc(proc)
-    , fImageCache(cache)
-    , fRowBytes(0) {
-    SkASSERT(fDecodeProc != NULL);
-    if (NULL == data) {
-        fData = SkData::NewEmpty();
-        fErrorInDecoding = true;
-    } else {
-        fData = data;
-        fData->ref();
-        fErrorInDecoding = data->size() == 0;
-    }
-    if (fImageCache != NULL) {
-        fImageCache->ref();
-        fCacheId = SkImageCache::UNINITIALIZED_ID;
-    } else {
-        fScaledCacheId = NULL;
-    }
-
-    // mark as uninitialized -- all fields are -1
-    memset(&fLazilyCachedInfo, 0xFF, sizeof(fLazilyCachedInfo));
-
-    // Since this pixel ref bases its data on encoded data, it should never change.
-    this->setImmutable();
-}
-
-SkLazyPixelRef::~SkLazyPixelRef() {
-    SkASSERT(fData != NULL);
-    fData->unref();
-    if (NULL == fImageCache) {
-        if (fScaledCacheId != NULL) {
-            SkScaledImageCache::Unlock(fScaledCacheId);
-            // TODO(halcanary): SkScaledImageCache needs a
-            // throwAwayCache(id) method.
-        }
-        return;
-    }
-    SkASSERT(fImageCache);
-    if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
-        fImageCache->throwAwayCache(fCacheId);
-    }
-    fImageCache->unref();
-}
-
-static size_t ComputeMinRowBytesAndSize(const SkImageInfo& info, size_t* rowBytes) {
-    *rowBytes = SkImageMinRowBytes(info);
-
-    Sk64 safeSize;
-    safeSize.setZero();
-    if (info.fHeight > 0) {
-        safeSize.setMul(info.fHeight, SkToS32(*rowBytes));
-    }
-    SkASSERT(!safeSize.isNeg());
-    return safeSize.is32() ? safeSize.get32() : 0;
-}
-
-const SkImageInfo* SkLazyPixelRef::getCachedInfo() {
-    if (fLazilyCachedInfo.fWidth < 0) {
-        SkImageInfo info;
-        fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
-        if (fErrorInDecoding) {
-            return NULL;
-        }
-        fLazilyCachedInfo = info;
-    }
-    return &fLazilyCachedInfo;
-}
-
-/**
-   Returns bitmap->getPixels() on success; NULL on failure */
-static void* decode_into_bitmap(SkImageInfo* info,
-                                SkBitmapFactory::DecodeProc decodeProc,
-                                size_t* rowBytes,
-                                SkData* data,
-                                SkBitmap* bm) {
-    SkASSERT(info && decodeProc && rowBytes && data && bm);
-    if (!(bm->setConfig(SkImageInfoToBitmapConfig(*info), info->fWidth,
-                        info->fHeight, *rowBytes, info->fAlphaType)
-          && bm->allocPixels(NULL, NULL))) {
-        // Use the default allocator.  It may be necessary for the
-        // SkLazyPixelRef to have a allocator field which is passed
-        // into allocPixels().
-        return NULL;
-    }
-    SkBitmapFactory::Target target;
-    target.fAddr = bm->getPixels();
-    target.fRowBytes = bm->rowBytes();
-    *rowBytes = target.fRowBytes;
-    if (!decodeProc(data->data(), data->size(), info, &target)) {
-        return NULL;
-    }
-    return target.fAddr;
-}
-
-void* SkLazyPixelRef::lockScaledImageCachePixels() {
-    SkASSERT(!fErrorInDecoding);
-    SkASSERT(NULL == fImageCache);
-    SkBitmap bitmap;
-    const SkImageInfo* info = this->getCachedInfo();
-    if (info == NULL) {
-        return NULL;
-    }
-    // If this is the first time though, this is guaranteed to fail.
-    // Maybe we should have a flag that says "don't even bother looking"
-    fScaledCacheId = SkScaledImageCache::FindAndLock(this->getGenerationID(),
-                                                     info->fWidth,
-                                                     info->fHeight,
-                                                     &bitmap);
-    if (fScaledCacheId != NULL) {
-        SkAutoLockPixels autoLockPixels(bitmap);
-        void* pixels = bitmap.getPixels();
-        SkASSERT(NULL != pixels);
-        // At this point, the autoLockPixels will unlockPixels()
-        // to remove bitmap's lock on the pixels.  We will then
-        // destroy bitmap.  The *only* guarantee that this pointer
-        // remains valid is the guarantee made by
-        // SkScaledImageCache that it will not destroy the *other*
-        // bitmap (SkScaledImageCache::Rec.fBitmap) that holds a
-        // reference to the concrete PixelRef while this record is
-        // locked.
-        return pixels;
-    } else {
-        // Cache has been purged, must re-decode.
-        void* pixels = decode_into_bitmap(const_cast<SkImageInfo*>(info),
-                                          fDecodeProc, &fRowBytes, fData,
-                                          &bitmap);
-        if (NULL == pixels) {
-            fErrorInDecoding = true;
-            return NULL;
-        }
-        fScaledCacheId = SkScaledImageCache::AddAndLock(this->getGenerationID(),
-                                                        info->fWidth,
-                                                        info->fHeight,
-                                                        bitmap);
-        SkASSERT(fScaledCacheId != NULL);
-        return pixels;
-    }
-}
-
-void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
-    if (fErrorInDecoding) {
-        return NULL;
-    }
-    if (NULL == fImageCache) {
-        return this->lockScaledImageCachePixels();
-    } else {
-        return this->lockImageCachePixels();
-    }
-}
-
-void* SkLazyPixelRef::lockImageCachePixels() {
-    SkASSERT(fImageCache != NULL);
-    SkASSERT(!fErrorInDecoding);
-    SkBitmapFactory::Target target;
-    // Check to see if the pixels still exist in the cache.
-    if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
-        target.fAddr = NULL;
-    } else {
-        SkImageCache::DataStatus status;
-        target.fAddr = fImageCache->pinCache(fCacheId, &status);
-        if (target.fAddr == NULL) {
-            fCacheId = SkImageCache::UNINITIALIZED_ID;
-        } else {
-            if (SkImageCache::kRetained_DataStatus == status) {
-#if LAZY_CACHE_STATS
-                sk_atomic_inc(&gCacheHits);
-#endif
-                return target.fAddr;
-            }
-            SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
-        }
-        // Cache miss. Either pinCache returned NULL or it returned a memory address without the old
-        // data
-#if LAZY_CACHE_STATS
-        sk_atomic_inc(&gCacheMisses);
-#endif
-    }
-
-    SkASSERT(fData != NULL && fData->size() > 0);
-    if (NULL == target.fAddr) {
-        const SkImageInfo* info = this->getCachedInfo();
-        if (NULL == info) {
-            SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
-            return NULL;
-        }
-        size_t bytes = ComputeMinRowBytesAndSize(*info, &target.fRowBytes);
-        target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
-        if (NULL == target.fAddr) {
-            // Space could not be allocated.
-            // Just like the last assert, fCacheId must be UNINITIALIZED_ID.
-            SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
-            return NULL;
-        }
-    } else {
-        // pinCache returned purged memory to which target.fAddr already points. Set
-        // target.fRowBytes properly.
-        target.fRowBytes = fRowBytes;
-        // Assume that the size is correct, since it was determined by this same function
-        // previously.
-    }
-    SkASSERT(target.fAddr != NULL);
-    SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
-    fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), NULL, &target);
-    if (fErrorInDecoding) {
-        fImageCache->throwAwayCache(fCacheId);
-        fCacheId = SkImageCache::UNINITIALIZED_ID;
-        return NULL;
-    }
-    // Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
-    fRowBytes = target.fRowBytes;
-    return target.fAddr;
-}
-
-void SkLazyPixelRef::onUnlockPixels() {
-    if (fErrorInDecoding) {
-        return;
-    }
-    if (NULL == fImageCache) {
-        // onUnlockPixels() should never be called a second time from
-        // PixelRef::Unlock() without calling onLockPixels() first.
-        SkASSERT(NULL != fScaledCacheId);
-        if (NULL != fScaledCacheId) {
-            SkScaledImageCache::Unlock(fScaledCacheId);
-            fScaledCacheId = NULL;
-        }
-    } else {  // use fImageCache
-        SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
-        if (SkImageCache::UNINITIALIZED_ID != fCacheId) {
-            fImageCache->releaseCache(fCacheId);
-        }
-    }
-}
-
-SkData* SkLazyPixelRef::onRefEncodedData() {
-    fData->ref();
-    return fData;
-}
-
-static bool init_from_info(SkBitmap* bm, const SkImageInfo& info,
-                           size_t rowBytes) {
-    SkBitmap::Config config = SkImageInfoToBitmapConfig(info);
-    if (SkBitmap::kNo_Config == config) {
-        return false;
-    }
-
-    return bm->setConfig(config, info.fWidth, info.fHeight, rowBytes, info.fAlphaType)
-           &&
-           bm->allocPixels();
-}
-
-bool SkLazyPixelRef::onImplementsDecodeInto() {
-    return true;
-}
-
-bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
-    SkASSERT(fData != NULL && fData->size() > 0);
-    if (fErrorInDecoding) {
-        return false;
-    }
-
-    SkImageInfo 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/src/lazy/SkLazyPixelRef.h b/src/lazy/SkLazyPixelRef.h
deleted file mode 100644
index f8a16d1..0000000
--- a/src/lazy/SkLazyPixelRef.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkLazyPixelRef_DEFINED
-#define SkLazyPixelRef_DEFINED
-
-#include "SkBitmapFactory.h"
-#include "SkImage.h"
-#include "SkImageCache.h"
-#include "SkPixelRef.h"
-#include "SkFlattenable.h"
-#include "SkScaledImageCache.h"
-
-class SkColorTable;
-class SkData;
-class SkImageCache;
-
-#ifdef SK_DEBUG
-    #define LAZY_CACHE_STATS 1
-#elif !defined(LAZY_CACHE_STATS)
-    #define LAZY_CACHE_STATS 0
-#endif
-
-/**
- *  PixelRef which defers decoding until SkBitmap::lockPixels() is called.
- */
-class SkLazyPixelRef : public SkPixelRef {
-
-public:
-    /**
-     *  Create a new SkLazyPixelRef.
-     *  @param SkData Encoded data representing the pixels.
-     *  @param DecodeProc Called to decode the pixels when needed. Must be non-NULL.
-     *  @param SkImageCache Object that handles allocating and freeing
-     *         the pixel memory, as needed.  If NULL, use the global
-     *         SkScaledImageCache.
-     */
-    SkLazyPixelRef(SkData*, SkBitmapFactory::DecodeProc, SkImageCache*);
-
-    virtual ~SkLazyPixelRef();
-
-#ifdef SK_DEBUG
-    intptr_t getCacheId() const { return fCacheId; }
-#endif
-
-#if LAZY_CACHE_STATS
-    static int32_t GetCacheHits() { return gCacheHits; }
-    static int32_t GetCacheMisses() { return gCacheMisses; }
-    static void ResetCacheStats() { gCacheHits = gCacheMisses = 0; }
-#endif
-
-    // No need to flatten this object. When flattening an SkBitmap, SkOrderedWriteBuffer will check
-    // the encoded data and write that instead.
-    // Future implementations of SkFlattenableWriteBuffer will need to special case for
-    // onRefEncodedData as well.
-    SK_DECLARE_UNFLATTENABLE_OBJECT()
-
-protected:
-    virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
-    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;
-    SkData*                     fData;
-    SkBitmapFactory::DecodeProc fDecodeProc;
-    SkImageCache*               fImageCache;
-    union {
-        SkImageCache::ID        fCacheId;
-        SkScaledImageCache::ID* fScaledCacheId;
-    };
-    size_t                      fRowBytes;
-    SkImageInfo                 fLazilyCachedInfo;
-
-#if LAZY_CACHE_STATS
-    static int32_t              gCacheHits;
-    static int32_t              gCacheMisses;
-#endif
-
-    // lazily initialized our cached info. Returns NULL on failure.
-    const SkImage::Info* getCachedInfo();
-    void* lockScaledImageCachePixels();
-    void* lockImageCachePixels();
-
-
-    typedef SkPixelRef INHERITED;
-};
-
-#endif  // SkLazyPixelRef_DEFINED
diff --git a/src/lazy/SkLruImageCache.cpp b/src/lazy/SkLruImageCache.cpp
deleted file mode 100644
index 26f7ef5..0000000
--- a/src/lazy/SkLruImageCache.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkLruImageCache.h"
-
-static intptr_t NextGenerationID() {
-    static intptr_t gNextID;
-    do {
-        gNextID++;
-    } while (SkImageCache::UNINITIALIZED_ID == gNextID);
-    return gNextID;
-}
-
-class CachedPixels : public SkNoncopyable {
-
-public:
-    CachedPixels(size_t length)
-        : fLength(length)
-        , fID(NextGenerationID())
-        , fLocked(false) {
-        fAddr = sk_malloc_throw(length);
-    }
-
-    ~CachedPixels() {
-        sk_free(fAddr);
-    }
-
-    void* getData() { return fAddr; }
-
-    intptr_t getID() const { return fID; }
-
-    size_t getLength() const { return fLength; }
-
-    void lock() { SkASSERT(!fLocked); fLocked = true; }
-
-    void unlock() { SkASSERT(fLocked); fLocked = false; }
-
-    bool isLocked() const { return fLocked; }
-
-private:
-    void*          fAddr;
-    size_t         fLength;
-    const intptr_t fID;
-    bool           fLocked;
-    SK_DECLARE_INTERNAL_LLIST_INTERFACE(CachedPixels);
-};
-
-////////////////////////////////////////////////////////////////////////////////////
-
-SkLruImageCache::SkLruImageCache(size_t budget)
-    : fRamBudget(budget)
-    , fRamUsed(0) {}
-
-SkLruImageCache::~SkLruImageCache() {
-    // Don't worry about updating pointers. All will be deleted.
-    Iter iter;
-    CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
-    while (pixels != NULL) {
-        CachedPixels* prev = iter.prev();
-        SkASSERT(!pixels->isLocked());
-#ifdef SK_DEBUG
-        fRamUsed -= pixels->getLength();
-#endif
-        SkDELETE(pixels);
-        pixels = prev;
-    }
-#ifdef SK_DEBUG
-    SkASSERT(fRamUsed == 0);
-#endif
-}
-
-#ifdef SK_DEBUG
-SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const {
-    if (SkImageCache::UNINITIALIZED_ID == ID) {
-        return SkImageCache::kFreed_MemoryStatus;
-    }
-    SkAutoMutexAcquire ac(&fMutex);
-    CachedPixels* pixels = this->findByID(ID);
-    if (NULL == pixels) {
-        return SkImageCache::kFreed_MemoryStatus;
-    }
-    if (pixels->isLocked()) {
-        return SkImageCache::kPinned_MemoryStatus;
-    }
-    return SkImageCache::kUnpinned_MemoryStatus;
-}
-
-void SkLruImageCache::purgeAllUnpinnedCaches() {
-    SkAutoMutexAcquire ac(&fMutex);
-    this->purgeTilAtOrBelow(0);
-}
-#endif
-
-size_t SkLruImageCache::setImageCacheLimit(size_t newLimit) {
-    size_t oldLimit = fRamBudget;
-    SkAutoMutexAcquire ac(&fMutex);
-    fRamBudget = newLimit;
-    this->purgeIfNeeded();
-    return oldLimit;
-}
-
-void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
-    SkAutoMutexAcquire ac(&fMutex);
-    CachedPixels* pixels = SkNEW_ARGS(CachedPixels, (bytes));
-    if (ID != NULL) {
-        *ID = pixels->getID();
-    }
-    pixels->lock();
-    fRamUsed += bytes;
-    fLRU.addToHead(pixels);
-    this->purgeIfNeeded();
-    return pixels->getData();
-}
-
-void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&fMutex);
-    CachedPixels* pixels = this->findByID(ID);
-    if (NULL == pixels) {
-        return NULL;
-    }
-    if (pixels != fLRU.head()) {
-        fLRU.remove(pixels);
-        fLRU.addToHead(pixels);
-    }
-    SkASSERT(status != NULL);
-    // This cache will never return pinned memory whose data has been overwritten.
-    *status = SkImageCache::kRetained_DataStatus;
-    pixels->lock();
-    return pixels->getData();
-}
-
-void SkLruImageCache::releaseCache(intptr_t ID) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&fMutex);
-    CachedPixels* pixels = this->findByID(ID);
-    SkASSERT(pixels != NULL);
-    pixels->unlock();
-    this->purgeIfNeeded();
-}
-
-void SkLruImageCache::throwAwayCache(intptr_t ID) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&fMutex);
-    CachedPixels* pixels = this->findByID(ID);
-    if (pixels != NULL) {
-        if (pixels->isLocked()) {
-            pixels->unlock();
-        }
-        this->removePixels(pixels);
-    }
-}
-
-void SkLruImageCache::removePixels(CachedPixels* pixels) {
-    // Mutex is already locked.
-    SkASSERT(!pixels->isLocked());
-    const size_t size = pixels->getLength();
-    SkASSERT(size <= fRamUsed);
-    fLRU.remove(pixels);
-    SkDELETE(pixels);
-    fRamUsed -= size;
-}
-
-CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
-    // Mutex is already locked.
-    Iter iter;
-    // Start from the head, most recently used.
-    CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
-    while (pixels != NULL) {
-        if (pixels->getID() == ID) {
-            return pixels;
-        }
-        pixels = iter.next();
-    }
-    return NULL;
-}
-
-void SkLruImageCache::purgeIfNeeded() {
-    // Mutex is already locked.
-    if (fRamBudget > 0) {
-        this->purgeTilAtOrBelow(fRamBudget);
-    }
-}
-
-void SkLruImageCache::purgeTilAtOrBelow(size_t limit) {
-    // Mutex is already locked.
-    if (fRamUsed > limit) {
-        Iter iter;
-        // Start from the tail, least recently used.
-        CachedPixels* pixels = iter.init(fLRU, Iter::kTail_IterStart);
-        while (pixels != NULL && fRamUsed > limit) {
-            CachedPixels* prev = iter.prev();
-            if (!pixels->isLocked()) {
-                this->removePixels(pixels);
-            }
-            pixels = prev;
-        }
-    }
-}
diff --git a/src/lazy/SkPurgeableImageCache.cpp b/src/lazy/SkPurgeableImageCache.cpp
deleted file mode 100644
index 0f2c5e3..0000000
--- a/src/lazy/SkPurgeableImageCache.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkThread.h"
-#include "SkPurgeableImageCache.h"
-#include "SkPurgeableMemoryBlock.h"
-
-#ifdef SK_DEBUG
-    #include "SkTSearch.h"
-#endif
-
-SK_DECLARE_STATIC_MUTEX(gPurgeableImageMutex);
-
-SkImageCache* SkPurgeableImageCache::Create() {
-    if (!SkPurgeableMemoryBlock::IsSupported()) {
-        return NULL;
-    }
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-    static SkPurgeableImageCache gCache;
-    gCache.ref();
-    return &gCache;
-}
-
-SkPurgeableImageCache::SkPurgeableImageCache() {}
-
-#ifdef SK_DEBUG
-SkPurgeableImageCache::~SkPurgeableImageCache() {
-    SkASSERT(fRecs.count() == 0);
-}
-#endif
-
-
-void* SkPurgeableImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-
-    SkPurgeableMemoryBlock* block = SkPurgeableMemoryBlock::Create(bytes);
-    if (NULL == block) {
-        return NULL;
-    }
-
-    SkPurgeableMemoryBlock::PinResult pinResult;
-    void* data = block->pin(&pinResult);
-    if (NULL == data) {
-        SkDELETE(block);
-        return NULL;
-    }
-
-    SkASSERT(ID != NULL);
-    *ID = reinterpret_cast<intptr_t>(block);
-#ifdef SK_DEBUG
-    // Insert into the array of all recs:
-    int index = this->findRec(*ID);
-    SkASSERT(index < 0);
-    fRecs.insert(~index, 1, ID);
-#endif
-    return data;
-}
-
-void* SkPurgeableImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-
-    SkASSERT(this->findRec(ID) >= 0);
-    SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
-    SkPurgeableMemoryBlock::PinResult pinResult;
-    void* data = block->pin(&pinResult);
-    if (NULL == data) {
-        this->removeRec(ID);
-        return NULL;
-    }
-
-    switch (pinResult) {
-        case SkPurgeableMemoryBlock::kRetained_PinResult:
-            *status = SkImageCache::kRetained_DataStatus;
-            break;
-
-        case SkPurgeableMemoryBlock::kUninitialized_PinResult:
-            *status = SkImageCache::kUninitialized_DataStatus;
-            break;
-
-        default:
-            // Invalid value. Treat as a failure to pin.
-            SkASSERT(false);
-            this->removeRec(ID);
-            return NULL;
-    }
-
-    return data;
-}
-
-void SkPurgeableImageCache::releaseCache(intptr_t ID) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-
-    SkASSERT(this->findRec(ID) >= 0);
-    SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
-    block->unpin();
-}
-
-void SkPurgeableImageCache::throwAwayCache(intptr_t ID) {
-    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-
-    this->removeRec(ID);
-}
-
-#ifdef SK_DEBUG
-SkImageCache::MemoryStatus SkPurgeableImageCache::getMemoryStatus(intptr_t ID) const {
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-    if (SkImageCache::UNINITIALIZED_ID == ID || this->findRec(ID) < 0) {
-        return SkImageCache::kFreed_MemoryStatus;
-    }
-
-    SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
-    if (block->isPinned()) {
-        return SkImageCache::kPinned_MemoryStatus;
-    }
-    return SkImageCache::kUnpinned_MemoryStatus;
-}
-
-void SkPurgeableImageCache::purgeAllUnpinnedCaches() {
-    SkAutoMutexAcquire ac(&gPurgeableImageMutex);
-    if (SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks()) {
-        SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks();
-    } else {
-        // Go through the blocks, and purge them individually.
-        // Rather than deleting the blocks, which would interfere with further calls, purge them
-        // and keep them around.
-        for (int i = 0; i < fRecs.count(); i++) {
-            SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(fRecs[i]);
-            if (!block->isPinned()) {
-                if (!block->purge()) {
-                    // FIXME: This should be more meaningful (which one, etc...)
-                    SkDebugf("Failed to purge\n");
-                }
-            }
-        }
-    }
-}
-
-int SkPurgeableImageCache::findRec(intptr_t rec) const {
-    return SkTSearch(fRecs.begin(), fRecs.count(), rec, sizeof(intptr_t));
-}
-#endif
-
-void SkPurgeableImageCache::removeRec(intptr_t ID) {
-#ifdef SK_DEBUG
-    int index = this->findRec(ID);
-    SkASSERT(index >= 0);
-    fRecs.remove(index);
-#endif
-    SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
-    SkASSERT(!block->isPinned());
-    SkDELETE(block);
-}
diff --git a/src/ports/SkDiscardableMemory_none.cpp b/src/ports/SkDiscardableMemory_none.cpp
index 700713b..51c3164 100644
--- a/src/ports/SkDiscardableMemory_none.cpp
+++ b/src/ports/SkDiscardableMemory_none.cpp
@@ -5,57 +5,9 @@
  * found in the LICENSE file.
  */
 
-#include "SkDiscardableMemory.h"
+#include "SkDiscardableMemoryPool.h"
 #include "SkTypes.h"
 
-namespace {
-////////////////////////////////////////////////////////////////////////////////
-/**
- *  Always successful, never purges.  Useful for testing.
- */
-class SkMockDiscardableMemory : public SkDiscardableMemory {
-public:
-    SkMockDiscardableMemory(void*);
-    virtual ~SkMockDiscardableMemory();
-    virtual bool lock() SK_OVERRIDE;
-    virtual void* data() SK_OVERRIDE;
-    virtual void unlock() SK_OVERRIDE;
-private:
-    bool fLocked;
-    void* fPointer;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-SkMockDiscardableMemory::SkMockDiscardableMemory(void* ptr)
-    : fLocked(true)
-    , fPointer(ptr) {  // Takes ownership of ptr.
-    SkASSERT(fPointer != NULL);
-}
-
-SkMockDiscardableMemory::~SkMockDiscardableMemory() {
-    SkASSERT(!fLocked);
-    sk_free(fPointer);
-}
-
-bool SkMockDiscardableMemory::lock() {
-    SkASSERT(!fLocked);
-    return fLocked = true;
-}
-
-void* SkMockDiscardableMemory::data() {
-    SkASSERT(fLocked);
-    return fLocked ? fPointer : NULL;
-}
-
-void SkMockDiscardableMemory::unlock() {
-    SkASSERT(fLocked);
-    fLocked = false;
-}
-////////////////////////////////////////////////////////////////////////////////
-}  // namespace
-
 SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
-    void* ptr = sk_malloc_throw(bytes);
-    return (ptr != NULL) ? SkNEW_ARGS(SkMockDiscardableMemory, (ptr)) : NULL;
+    return SkGetGlobalDiscardableMemoryPool()->create(bytes);
 }