Create SkLazyPixelRef which performs lazy decoding.

The new pixel ref behaves similarly to SkImageRef, with some key differences:
It does not depend on the images project.
It requires an SkImageCache, which handles allocation and caching of the pixel
memory.
It takes a function signature for decoding which decodes into already allocated
pixel memory rather than into an SkBitmap.

Add two implementations of SkImageCache: SkLruImageCache and SkAshmemImageCache.

Replace SkSerializationHelpers::DecodeBitmap with SkPicture::InstallPixelRefProc,
and update sites that referenced it.

SkBitmapFactory now sets the pixel ref to a new object of the new
class SkLazyPixelRef, provided it has an SkImageCache for caching.

Provide an option to do lazy decodes in render_pictures and bench_pictures.

SkPicture:
Eliminate the default parameters in the constructor.
If a proc for decoding bitmaps is installed, use it to decode any encoded
data in subpictures.
When parsing deserializing subpictures, check for success.
When serializing subpictures, pass the picture's bitmap encoder to the
subpicture's call to serialize.

Update BitmapFactoryTest to test its new behavior.

BUG=https://code.google.com/p/skia/issues/detail?id=1008
BUG=https://code.google.com/p/skia/issues/detail?id=1009

Review URL: https://codereview.appspot.com/7060052

git-svn-id: http://skia.googlecode.com/svn/trunk@7835 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
index fba7a1e..8f1dc55 100644
--- a/debugger/QT/SkDebuggerGUI.cpp
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -144,8 +144,7 @@
 class SkTimedPicturePlayback : public SkPicturePlayback {
 public:
     SkTimedPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
-                           SkSerializationHelpers::DecodeBitmap decoder,
-                           const SkTDArray<size_t>& offsets,
+                           SkPicture::InstallPixelRefProc proc, const SkTDArray<size_t>& offsets,
                            const SkTDArray<bool>& deletedCommands)
         : INHERITED(stream, info, isValid, decoder)
         , fOffsets(offsets)
@@ -251,9 +250,7 @@
 // Wrap SkPicture to allow installation of an SkTimedPicturePlayback object
 class SkTimedPicture : public SkPicture {
 public:
-    explicit SkTimedPicture(SkStream* stream,
-                            bool* success,
-                            SkSerializationHelpers::DecodeBitmap decoder,
+    explicit SkTimedPicture(SkStream* stream, bool* success, SkPicture::InstallPixelRefProc,
                             const SkTDArray<size_t>& offsets,
                             const SkTDArray<bool>& deletedCommands) {
         if (success) {
@@ -362,7 +359,7 @@
     }
 
     bool success = false;
-    SkTimedPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream,
+    SkTimedPicture picture(&inputStream, &success, &SkImageDecoder::DecodeMemory,
                            fOffsets, fSkipCommands);
     if (!success) {
         return;
@@ -928,7 +925,7 @@
 class SkOffsetPicturePlayback : public SkPicturePlayback {
 public:
     SkOffsetPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
-                            SkSerializationHelpers::DecodeBitmap decoder)
+                            SkPicture::InstallPixelRefProc proc)
         : INHERITED(stream, info, isValid, decoder) {
     }
 
@@ -949,9 +946,7 @@
 // Picture to wrap an SkOffsetPicturePlayback.
 class SkOffsetPicture : public SkPicture {
 public:
-    SkOffsetPicture(SkStream* stream,
-                    bool* success,
-                    SkSerializationHelpers::DecodeBitmap decoder) {
+    SkOffsetPicture(SkStream* stream, bool* success, SkPicture::InstallPixelRefProc proc) {
         if (success) {
             *success = false;
         }
@@ -1008,7 +1003,8 @@
 
     bool success = false;
 
-    SkOffsetPicture* picture = SkNEW_ARGS(SkOffsetPicture, (stream, &success, &SkImageDecoder::DecodeStream));
+    SkOffsetPicture* picture = SkNEW_ARGS(SkOffsetPicture,
+                                          (stream, &success, &SkImageDecoder::DecodeMemory));
 
     if (!success) {
         QMessageBox::critical(this, "Error loading file", "Couldn't read file, sorry.");
diff --git a/gm/factory.cpp b/gm/factory.cpp
index 8893158..b55532e 100644
--- a/gm/factory.cpp
+++ b/gm/factory.cpp
@@ -9,6 +9,8 @@
 #include "SkBitmapFactory.h"
 #include "SkCanvas.h"
 #include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkLruImageCache.h"
 #include "SkStream.h"
 
 namespace skiagm {
@@ -37,7 +39,12 @@
             void* buffer = sk_malloc_throw(length);
             stream.read(buffer, length);
             SkAutoDataUnref data(SkData::NewFromMalloc(buffer, length));
-            SkBitmapFactory::DecodeBitmap(&fBitmap, data);
+            SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
+            // Create a cache which will boot the pixels out anytime the
+            // bitmap is unlocked.
+            SkAutoTUnref<SkLruImageCache> cache(SkNEW_ARGS(SkLruImageCache, (0)));
+            factory.setImageCache(cache);
+            factory.installPixelRef(data, &fBitmap);
         }
     }
 
diff --git a/gyp/animator.gyp b/gyp/animator.gyp
index 942e849..a462ca4 100644
--- a/gyp/animator.gyp
+++ b/gyp/animator.gyp
@@ -10,14 +10,15 @@
       'type': 'static_library',
       'standalone_static_library': 1,
       'include_dirs': [
+        '../include/animator',
         '../include/config',
         '../include/core',
         '../include/effects',
-        '../include/animator',
+        '../include/images',
+        '../include/lazy',
+        '../include/utils',
         '../include/views',
         '../include/xml',
-        '../include/utils',
-        '../include/images',
         '../src/utils',
       ],
       'sources': [
diff --git a/gyp/core.gyp b/gyp/core.gyp
index 7e7640c..76994e1 100644
--- a/gyp/core.gyp
+++ b/gyp/core.gyp
@@ -15,6 +15,7 @@
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/lazy',
         '../include/pipe',
         '../include/ports',
         '../include/utils',
@@ -106,6 +107,7 @@
           'config',
           '../include/config',
           '../include/core',
+          '../include/lazy',
           '../include/pipe',
           'ext',
         ],
diff --git a/gyp/core.gypi b/gyp/core.gypi
index d5f76a3..9831bbb 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -281,6 +281,16 @@
         '<(skia_include_path)/core/SkWeakRefCnt.h',
         '<(skia_include_path)/core/SkWriter32.h',
         '<(skia_include_path)/core/SkXfermode.h',
+
+        # Lazy decoding:
+        '<(skia_include_path)/lazy/SkBitmapFactory.h',
+        '<(skia_include_path)/lazy/SkImageCache.h',
+        '<(skia_include_path)/lazy/SkLruImageCache.h',
+
+        '<(skia_src_path)/lazy/SkBitmapFactory.cpp',
+        '<(skia_src_path)/lazy/SkLazyPixelRef.h',
+        '<(skia_src_path)/lazy/SkLazyPixelRef.cpp',
+        '<(skia_src_path)/lazy/SkLruImageCache.cpp',
     ],
 }
 
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 8c4690e..2f007ff 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -16,9 +16,11 @@
         '../include/config',
         '../include/core',
         '../include/images',
+        '../include/lazy',
+        # for access to SkImagePriv.h
+        '../src/image/',
       ],
       'sources': [
-        '../include/images/SkBitmapFactory.h',
         '../include/images/SkImageDecoder.h',
         '../include/images/SkImageEncoder.h',
         '../include/images/SkImageRef.h',
@@ -29,7 +31,6 @@
 
         '../src/images/bmpdecoderhelper.cpp',
         '../src/images/bmpdecoderhelper.h',
-        '../src/images/SkBitmapFactory.cpp',
         '../src/images/SkFDStream.cpp',
         '../src/images/SkImageDecoder.cpp',
         '../src/images/SkImageDecoder_Factory.cpp',
@@ -136,6 +137,7 @@
       'direct_dependent_settings': {
         'include_dirs': [
           '../include/images',
+          '../include/lazy',
         ],
       },
     },
diff --git a/gyp/ports.gyp b/gyp/ports.gyp
index 219bf3e..814441a 100644
--- a/gyp/ports.gyp
+++ b/gyp/ports.gyp
@@ -155,12 +155,15 @@
             '../src/ports/SkDebug_stdio.cpp',
           ],
           'sources': [
+            '../include/ports/SkAshmemImageCache.h',
+
             '../src/ports/SkDebug_android.cpp',
             '../src/ports/SkThread_pthread.cpp',
             '../src/ports/SkFontHost_android.cpp',
             '../src/ports/SkFontHost_FreeType.cpp',
             '../src/ports/SkFontHost_FreeType_common.cpp',
             '../src/ports/FontHostConfiguration_android.cpp',
+            '../src/ports/SkAshmemImageCache.cpp',
           ],
           'dependencies': [
              'freetype.gyp:freetype',
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 247148b..6362352 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -10,6 +10,7 @@
       'include_dirs' : [
         '../src/core',
         '../src/effects',
+        '../src/lazy',
         '../src/pdf',
         '../src/pipe/utils',
         '../src/utils',
diff --git a/gyp/views.gyp b/gyp/views.gyp
index b0697a0..aca3bb3 100644
--- a/gyp/views.gyp
+++ b/gyp/views.gyp
@@ -14,12 +14,13 @@
       'include_dirs': [
         '../include/config',
         '../include/core',
-        '../include/views',
-        '../include/xml',
-        '../include/utils',
-        '../include/images',
         '../include/effects',
+        '../include/images',
+        '../include/lazy',
+        '../include/utils',
+        '../include/views',
         '../include/views/unix',
+        '../include/xml',
       ],
       'dependencies': [
         'angle.gyp:*',
diff --git a/gyp/views_animated.gyp b/gyp/views_animated.gyp
index 1767fe1..29e981d 100644
--- a/gyp/views_animated.gyp
+++ b/gyp/views_animated.gyp
@@ -6,16 +6,17 @@
       'target_name': 'views_animated',
       'type': 'static_library',
       'include_dirs': [
+        '../include/animator',
         '../include/config',
         '../include/core',
-        '../include/views',
-        '../include/xml',
-        '../include/utils',
-        '../include/images',
-        '../include/animator',
         '../include/effects',
-        '../include/views/unix',
+        '../include/images',
+        '../include/lazy',
+        '../include/utils',
+        '../include/views',
         '../include/views/animated',
+        '../include/views/unix',
+        '../include/xml',
       ],
       'sources': [
         '../include/views/animated/SkBorderView.h',
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 44e4f86..c8b1676 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -10,11 +10,11 @@
 #ifndef SkPicture_DEFINED
 #define SkPicture_DEFINED
 
+#include "SkBitmap.h"
 #include "SkRefCnt.h"
 #include "SkSerializationHelpers.h"
 
 class SkBBoxHierarchy;
-class SkBitmap;
 class SkCanvas;
 class SkPicturePlayback;
 class SkPictureRecord;
@@ -39,13 +39,37 @@
         this call, those elements will not appear in this picture.
     */
     SkPicture(const SkPicture& src);
+
     /**
-     *  Recreate a picture that was serialized into a stream. *success is set to
-     *  true if the picture was deserialized successfully and false otherwise.
-     *  decoder is used to decode any SkBitmaps that were encoded into the stream.
+     *  Recreate a picture that was serialized into a stream.
+     *  On failure, silently creates an empty picture.
+     *  @param SkStream Serialized picture data.
      */
-    explicit SkPicture(SkStream*, bool* success = NULL,
-                       SkSerializationHelpers::DecodeBitmap decoder = NULL);
+    explicit SkPicture(SkStream*);
+
+    /**
+     *  Function signature defining a function that sets up an SkBitmap from encoded data. On
+     *  success, the SkBitmap should have its Config, width, height, rowBytes and pixelref set.
+     *  If the installed pixelref has decoded the data into pixels, then the src buffer need not be
+     *  copied. If the pixelref defers the actual decode until its lockPixels() is called, then it
+     *  must make a copy of the src buffer.
+     *  @param src Encoded data.
+     *  @param length Size of the encoded data, in bytes.
+     *  @param dst SkBitmap to install the pixel ref on.
+     *  @param bool Whether or not a pixel ref was successfully installed.
+     */
+    typedef bool (*InstallPixelRefProc)(const void* src, size_t length, SkBitmap* dst);
+
+    /**
+     *  Recreate a picture that was serialized into a stream.
+     *  @param SkStream Serialized picture data.
+     *  @param success Output parameter. If non-NULL, will be set to true if the picture was
+     *                 deserialized successfully and false otherwise.
+     *  @param proc Function pointer for installing pixelrefs on SkBitmaps representing the
+     *              encoded bitmap data from the stream.
+     */
+    SkPicture(SkStream*, bool* success, InstallPixelRefProc proc);
+
     virtual ~SkPicture();
 
     /**
@@ -176,6 +200,7 @@
     virtual SkBBoxHierarchy* createBBoxHierarchy() const;
 
 private:
+    void initFromStream(SkStream*, bool* success, InstallPixelRefProc);
 
     friend class SkFlatPicture;
     friend class SkPicturePlayback;
diff --git a/include/core/SkSerializationHelpers.h b/include/core/SkSerializationHelpers.h
index 8bb2a41..d87167b 100644
--- a/include/core/SkSerializationHelpers.h
+++ b/include/core/SkSerializationHelpers.h
@@ -21,15 +21,6 @@
      *  another method of storing SkBitmaps.
      */
     typedef bool (*EncodeBitmap)(SkWStream*, const SkBitmap&);
-
-    /**
-     *  Function to decode an SkBitmap from an SkStream. A function with this signature can be
-     *  passed to the SkStream constructor for SkPicture and SkOrderedReadBuffer to decode SkBitmaps
-     *  which were previously encoded. The function should return true if it succeeds. Otherwise it
-     *  should return false so that SkOrderedReadBuffer can skip the data and provide a dummy
-     *  SkBitmap.
-     */
-    typedef bool (*DecodeBitmap)(SkStream*, SkBitmap*);
 }
 
 #endif // SkSerializationHelpers_DEFINED
diff --git a/include/images/SkBitmapFactory.h b/include/images/SkBitmapFactory.h
deleted file mode 100644
index 6779dc2..0000000
--- a/include/images/SkBitmapFactory.h
+++ /dev/null
@@ -1,46 +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 SkBitmapFactory_DEFINED
-#define SkBitmapFactory_DEFINED
-
-class SkBitmap;
-class SkData;
-
-/**
- *  General purpose factory for decoding bitmaps.
- *
- *  Currently only provides a way to decode a bitmap or its dimensions from an SkData. Future plans
- *  include options to provide a bitmap which caches the pixel data.
- */
-class SkBitmapFactory {
-
-public:
-    enum Constraints {
-        /**
-         *  Only decode the bounds of the bitmap. No pixels will be allocated.
-         */
-        kDecodeBoundsOnly_Constraint,
-
-        /**
-         *  Decode the bounds and pixels of the bitmap.
-         */
-        kDecodePixels_Constraint,
-    };
-
-    /**
-     *  Decodes an SkData into an SkBitmap.
-     *  @param SkBitmap Already created bitmap to encode into.
-     *  @param SkData Encoded SkBitmap data.
-     *  @param constraint Specifications for how to do the decoding.
-     *  @return True on success. If false, passed in SkBitmap is unmodified.
-     */
-    static bool DecodeBitmap(SkBitmap*, const SkData*,
-                             Constraints constraint = kDecodePixels_Constraint);
-};
-
-#endif // SkBitmapFactory_DEFINED
diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
index a8ec1c6..6d1bb72 100644
--- a/include/images/SkImageDecoder.h
+++ b/include/images/SkImageDecoder.h
@@ -11,6 +11,8 @@
 #define SkImageDecoder_DEFINED
 
 #include "SkBitmap.h"
+#include "SkBitmapFactory.h"
+#include "SkImage.h"
 #include "SkRefCnt.h"
 
 class SkStream;
@@ -223,6 +225,34 @@
         return DecodeMemory(buffer, size, bitmap, SkBitmap::kNo_Config,
                             kDecodePixels_Mode, NULL);
     }
+
+    /**
+     *  Decode memory.
+     *  @param info Output parameter. Returns info about the encoded image.
+     *  @param target Contains the address of pixel memory to decode into
+     *         (which must be large enough to hold the width in info) and
+     *         the row bytes to use. If NULL, returns info and does not
+     *         decode pixels.
+     *  @return bool Whether the function succeeded.
+     *
+     *  Sample usage:
+     *  <code>
+     *      // Determine the image's info: width/height/config
+     *      SkImage::Info info;
+     *      bool success = DecodeMemoryToTarget(src, size, &info, NULL);
+     *      if (!success) return;
+     *      // Allocate space for the result:
+     *      SkBitmapFactory::Target target;
+     *      target.fAddr = malloc/other allocation
+     *      target.fRowBytes = ...
+     *      // Now decode the actual pixels into target. &info is optional,
+     *      // and could be NULL
+     *      success = DecodeMemoryToTarget(src, size, &info, &target);
+     *  </code>
+     */
+    static bool DecodeMemoryToTarget(const void* buffer, size_t size, SkImage::Info* info,
+                                     const SkBitmapFactory::Target* target);
+
     /** Decode the image stored in the specified SkStream, and store the result
         in bitmap. Return true for success or false on failure.
 
diff --git a/include/lazy/SkBitmapFactory.h b/include/lazy/SkBitmapFactory.h
new file mode 100644
index 0000000..bebd3a7
--- /dev/null
+++ b/include/lazy/SkBitmapFactory.h
@@ -0,0 +1,86 @@
+/*
+ * 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 SkBitmapFactory_DEFINED
+#define SkBitmapFactory_DEFINED
+
+#include "SkImage.h"
+#include "SkTypes.h"
+
+class SkBitmap;
+class SkData;
+class SkImageCache;
+
+/**
+ *  Factory for creating a bitmap from encoded data.
+ */
+class SkBitmapFactory {
+
+public:
+    /**
+     *  Struct containing information about a pixel destination.
+     */
+    struct Target {
+        /**
+         *  Pre-allocated memory.
+         */
+        void*  fAddr;
+
+        /**
+         *  Rowbytes of the allocated memory.
+         */
+        size_t fRowBytes;
+    };
+
+    /**
+     *  Signature for a function to decode an image from encoded data.
+     */
+    typedef bool (*DecodeProc)(const void* data, size_t length, SkImage::Info*, const Target*);
+
+    /**
+     *  Create a bitmap factory which uses DecodeProc for decoding.
+     *  @param DecodeProc Must not be NULL.
+     */
+    SkBitmapFactory(DecodeProc);
+
+    ~SkBitmapFactory();
+
+    /**
+     *  Set an image cache to use on pixelrefs provided by installPixelRef. Mutually exclusive
+     *  with fCacheSelector.
+     */
+    void setImageCache(SkImageCache* cache);
+
+    /**
+     *  Sets up an SkBitmap from encoded data. On success, the SkBitmap will have its Config,
+     *  width, height, rowBytes and pixelref set. If fImageCache is non-NULL, or if fCacheSelector
+     *  is set and returns non-NULL, the pixelref will lazily decode, and that SkImageCache will
+     *  handle the pixel memory. Otherwise installPixelRef will do an immediate decode.
+     *  @param SkData Encoded data.
+     *  @param SkBitmap to install the pixel ref on.
+     *  @return bool Whether or not a pixel ref was successfully installed.
+     */
+    bool installPixelRef(SkData*, SkBitmap*);
+
+    /**
+     *  A function for selecting an SkImageCache to use based on an SkImage::Info.
+     */
+    typedef SkImageCache* (*CacheSelector)(const SkImage::Info&);
+
+    /**
+     *  Set the function to be used to select which SkImageCache to use. Mutually exclusive with
+     *  fImageCache.
+     */
+    void setCacheSelector(CacheSelector);
+
+private:
+    DecodeProc    fDecodeProc;
+    SkImageCache* fImageCache;
+    CacheSelector fCacheSelector;
+};
+
+#endif // SkBitmapFactory_DEFINED
diff --git a/include/lazy/SkImageCache.h b/include/lazy/SkImageCache.h
new file mode 100644
index 0000000..045ce2c
--- /dev/null
+++ b/include/lazy/SkImageCache.h
@@ -0,0 +1,76 @@
+/*
+ * 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 SkImageCache_DEFINED
+#define SkImageCache_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkTypes.h"
+
+/**
+ *  Interface for a cache that manages pixel memory.
+ */
+class SkImageCache : public SkRefCnt {
+
+public:
+    /**
+     *  Allocate memory whose lifetime is managed by the cache. On success, MUST be balanced with a
+     *  call to releaseCache and a call to throwAwayCache.
+     *  @param bytes Number of bytes needed.
+     *  @param ID Output parameter which must not be NULL. On success, ID will be set to a value
+     *         associated with that memory which can be used as a parameter to the other functions
+     *         in SkImageCache. On failure, ID is unchanged.
+     *  @return Pointer to the newly allocated memory, or NULL. This memory is safe to use until
+     *          releaseCache is called with ID.
+     */
+    virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) = 0;
+
+    /**
+     *  Re-request the memory associated with ID.
+     *  @param ID Unique ID for the memory block.
+     *  @return Pointer: If non-NULL, points to the previously allocated memory, in which case
+     *          this call must be balanced with a call to releaseCache. If NULL, the memory
+     *          has been reclaimed, so allocAndPinCache must be called again, and ID is no
+     *          longer valid (thus throwAwayCache need not be called).
+     */
+    virtual void* pinCache(intptr_t ID) = 0;
+
+    /**
+     *  Inform the cache that it is safe to free the block of memory corresponding to ID. After
+     *  calling this function, the pointer returnted by allocAndPinCache or pinCache must not be
+     *  used again. In order to access the same memory after this, pinCache must be called.
+     *  @param ID Unique ID for the memory block which is now safe to age out of the cache.
+     */
+    virtual void releaseCache(intptr_t ID) = 0;
+
+    /**
+     *  Inform the cache that the block of memory associated with ID will not be asked for again.
+     *  After this call, ID is no longer valid. Must not be called while the associated memory is
+     *  pinned. Must be called to balance a successful allocAndPinCache, unless a later pinCache
+     *  returns NULL.
+     */
+    virtual void throwAwayCache(intptr_t ID) = 0;
+
+    /**
+     *  ID which does not correspond to any valid cache.
+     */
+    static const intptr_t UNINITIALIZED_ID = 0;
+
+#ifdef SK_DEBUG
+    enum CacheStatus {
+        kPinned_CacheStatus,
+        kUnpinned_CacheStatus,
+        kThrownAway_CacheStatus,
+    };
+
+    /**
+     *  Debug only function to get the status of a particular block of memory.
+     */
+    virtual CacheStatus getCacheStatus(intptr_t ID) const = 0;
+#endif
+};
+#endif // SkImageCache_DEFINED
diff --git a/include/lazy/SkLruImageCache.h b/include/lazy/SkLruImageCache.h
new file mode 100644
index 0000000..4444600
--- /dev/null
+++ b/include/lazy/SkLruImageCache.h
@@ -0,0 +1,74 @@
+/*
+ * 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 SkLruImageCache_DEFINED
+#define SkLruImageCache_DEFINED
+
+#include "SkImageCache.h"
+#include "SkThread.h"
+#include "SkTInternalLList.h"
+
+class CachedPixels;
+
+/**
+ *  SkImageCache implementation that uses an LRU cache to age out old images.
+ */
+class SkLruImageCache : public SkImageCache {
+
+public:
+    SkLruImageCache(size_t budget);
+
+    virtual ~SkLruImageCache();
+
+#ifdef SK_DEBUG
+    CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
+#endif
+
+    void setBudget(size_t newBudget);
+
+    virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
+    virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
+    virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
+    virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
+
+private:
+    // Linked list of recently used. Head is the most recently used, and tail is the least.
+    SkTInternalLList<CachedPixels> fLRU;
+    typedef SkTInternalLList<CachedPixels>::Iter Iter;
+
+#ifdef SK_DEBUG
+    // fMutex is mutable so that getCacheStatus can be const
+    mutable
+#endif
+    SkMutex fMutex;
+    size_t  fRamBudget;
+    size_t  fRamUsed;
+
+    /**
+     *  Find the CachedPixels represented by ID, or NULL if not in the cache. Mutex must be locked
+     *  before calling.
+     */
+    CachedPixels* findByID(intptr_t ID) const;
+
+    /**
+     *  If over budget, throw away pixels which are not currently in use until below budget or there
+     *  are no more pixels eligible to be thrown away. Mutex must be locked before calling.
+     */
+    void purgeIfNeeded();
+
+    /**
+     *  Purge until below limit. Mutex must be locked before calling.
+     */
+    void purgeTilAtOrBelow(size_t limit);
+
+    /**
+     *  Remove a set of CachedPixels. Mutex must be locked before calling.
+     */
+    void removePixels(CachedPixels*);
+};
+
+#endif // SkLruImageCache_DEFINED
diff --git a/include/ports/SkAshmemImageCache.h b/include/ports/SkAshmemImageCache.h
new file mode 100644
index 0000000..d89c3df
--- /dev/null
+++ b/include/ports/SkAshmemImageCache.h
@@ -0,0 +1,73 @@
+/*
+ * 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 SkAshmemImageCache_DEFINED
+#define SkAshmemImageCache_DEFINED
+
+#include "SkImageCache.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+
+class SkAshmemImageCache : public SkImageCache {
+
+public:
+    /**
+     *  Get a pointer to the single global instance of SkAshmemImageCache.
+     */
+    static SkAshmemImageCache* GetAshmemImageCache();
+
+    virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
+    virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
+    virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
+    virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
+
+#ifdef SK_DEBUG
+    SkImageCache::CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
+
+    virtual ~SkAshmemImageCache();
+#endif
+
+private:
+    struct AshmemRec {
+        int    fFD;
+        void*  fAddr;
+        size_t fSize;
+#ifdef SK_DEBUG
+        bool   fPinned;
+
+        static int Compare(const AshmemRec*, const AshmemRec*);
+#endif
+    };
+
+    /**
+     *  Constructor is private. The correct way to get this cache is through
+     *  GetAshmemImageCache, so that all callers can get the single global.
+     */
+    SkAshmemImageCache();
+
+#ifdef SK_DEBUG
+    // Stores a list of AshmemRecs to track deletion.
+    SkTDArray<AshmemRec*> fRecs;
+
+    /**
+     *  Debug only function to add an AshmemRec to the list.
+     */
+    void appendRec(AshmemRec*);
+
+    /**
+     *  Return the index of AshmemRec.
+     */
+    int findRec(const AshmemRec*) const;
+#endif
+
+    /**
+     *  Deletes AshmemRec. In debug, also removes from the list.
+     */
+    void removeRec(AshmemRec*);
+};
+#endif // SkAshmemImageCache_DEFINED
+
diff --git a/samplecode/SamplePictFile.cpp b/samplecode/SamplePictFile.cpp
index 65279cb..c82d03e 100644
--- a/samplecode/SamplePictFile.cpp
+++ b/samplecode/SamplePictFile.cpp
@@ -48,8 +48,7 @@
         } else {
             SkFILEStream stream(path);
             if (stream.isValid()) {
-                pic = SkNEW_ARGS(SkPicture,
-                                 (&stream, NULL, &SkImageDecoder::DecodeStream));
+                pic = SkNEW_ARGS(SkPicture, (&stream, NULL, &SkImageDecoder::DecodeMemory));
             }
 
             if (false) { // re-record
diff --git a/src/core/SkOrderedReadBuffer.cpp b/src/core/SkOrderedReadBuffer.cpp
index daca74c..990de45 100644
--- a/src/core/SkOrderedReadBuffer.cpp
+++ b/src/core/SkOrderedReadBuffer.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkOrderedReadBuffer.h"
 #include "SkStream.h"
 #include "SkTypeface.h"
@@ -167,19 +168,18 @@
 }
 
 void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
-    size_t length = this->readUInt();
+    const size_t length = this->readUInt();
     if (length > 0) {
         // Bitmap was encoded.
-        SkMemoryStream stream(const_cast<void*>(this->skip(length)), length, false);
-        if (fBitmapDecoder != NULL && fBitmapDecoder(&stream, bitmap)) {
-            // Skip the width and height, which were written in case of failure.
-            fReader.skip(2 * sizeof(int));
+        const void* data = this->skip(length);
+        const int width = this->readInt();
+        const int height = this->readInt();
+        if (fBitmapDecoder != NULL && fBitmapDecoder(data, length, bitmap)) {
+            SkASSERT(bitmap->width() == width && bitmap->height() == height);
         } else {
             // This bitmap was encoded when written, but we are unable to decode, possibly due to
             // not having a decoder. Use a placeholder bitmap.
             SkDebugf("Could not decode bitmap. Resulting bitmap will be red.\n");
-            int width = this->readInt();
-            int height = this->readInt();
             bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
             bitmap->allocPixels();
             bitmap->eraseColor(SK_ColorRED);
diff --git a/src/core/SkOrderedReadBuffer.h b/src/core/SkOrderedReadBuffer.h
index b3fde17..b3635fb 100644
--- a/src/core/SkOrderedReadBuffer.h
+++ b/src/core/SkOrderedReadBuffer.h
@@ -10,13 +10,15 @@
 #define SkOrderedReadBuffer_DEFINED
 
 #include "SkRefCnt.h"
-#include "SkBitmap.h"
 #include "SkBitmapHeap.h"
 #include "SkFlattenableBuffers.h"
 #include "SkPath.h"
+#include "SkPicture.h"
 #include "SkReader32.h"
 #include "SkSerializationHelpers.h"
 
+class SkBitmap;
+
 class SkOrderedReadBuffer : public SkFlattenableReadBuffer {
 public:
     SkOrderedReadBuffer();
@@ -99,11 +101,11 @@
     }
 
     /**
-     *  Provide a function to decode an SkBitmap from an SkStream. Only used if the writer encoded
-     *  the SkBitmap. If the proper decoder cannot be used, a red bitmap with the appropriate size
-     *  will be used.
+     *  Provide a function to decode an SkBitmap from encoded data. Only used if the writer
+     *  encoded the SkBitmap. If the proper decoder cannot be used, a red bitmap with the
+     *  appropriate size will be used.
      */
-    void setBitmapDecoder(SkSerializationHelpers::DecodeBitmap bitmapDecoder) {
+    void setBitmapDecoder(SkPicture::InstallPixelRefProc bitmapDecoder) {
         fBitmapDecoder = bitmapDecoder;
     }
 
@@ -119,7 +121,7 @@
     SkFlattenable::Factory* fFactoryArray;
     int                     fFactoryCount;
 
-    SkSerializationHelpers::DecodeBitmap fBitmapDecoder;
+    SkPicture::InstallPixelRefProc fBitmapDecoder;
 
     typedef SkFlattenableReadBuffer INHERITED;
 };
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index 58fc0c3..f86634a 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -121,7 +121,7 @@
     fWidth = fHeight = 0;
 }
 
-SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
+SkPicture::SkPicture(const SkPicture& src) {
     fWidth = src.fWidth;
     fHeight = src.fHeight;
     fRecord = NULL;
@@ -264,7 +264,15 @@
 
 #include "SkStream.h"
 
-SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
+SkPicture::SkPicture(SkStream* stream) {
+    this->initFromStream(stream, NULL, NULL);
+}
+
+SkPicture::SkPicture(SkStream* stream, bool* success, InstallPixelRefProc proc) {
+    this->initFromStream(stream, success, proc);
+}
+
+void SkPicture::initFromStream(SkStream* stream, bool* success, InstallPixelRefProc proc) {
     if (success) {
         *success = false;
     }
@@ -283,7 +291,7 @@
 
     if (stream->readBool()) {
         bool isValid = false;
-        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, decoder));
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, proc));
         if (!isValid) {
             SkDELETE(fPlayback);
             fPlayback = NULL;
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index b81d2be..722a2c6 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -416,7 +416,7 @@
     if (fPictureCount > 0) {
         writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
         for (int i = 0; i < fPictureCount; i++) {
-            fPictureRefs[i]->serialize(stream);
+            fPictureRefs[i]->serialize(stream, encoder);
         }
     }
 
@@ -473,9 +473,8 @@
     return rbMask;
 }
 
-bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info,
-                                       uint32_t tag, size_t size,
-                                       SkSerializationHelpers::DecodeBitmap decoder) {
+bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info, uint32_t tag,
+                                       size_t size, SkPicture::InstallPixelRefProc proc) {
     /*
      *  By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
      *  its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
@@ -515,8 +514,23 @@
         case PICT_PICTURE_TAG: {
             fPictureCount = size;
             fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
-            for (int i = 0; i < fPictureCount; i++) {
-                fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
+            bool success = true;
+            int i = 0;
+            for ( ; i < fPictureCount; i++) {
+                fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream, &success, proc));
+                if (!success) {
+                    break;
+                }
+            }
+            if (!success) {
+                // Delete all of the pictures that were already created (up through i):
+                for (int j = 0; j <= i; j++) {
+                    fPictureRefs[j]->unref();
+                }
+                // Delete the array
+                SkDELETE_ARRAY(fPictureRefs);
+                fPictureCount = 0;
+                return false;
             }
         } break;
         case PICT_BUFFER_SIZE_TAG: {
@@ -528,7 +542,7 @@
 
             fFactoryPlayback->setupBuffer(buffer);
             fTFPlayback.setupBuffer(buffer);
-            buffer.setBitmapDecoder(decoder);
+            buffer.setBitmapDecoder(proc);
 
             while (!buffer.eof()) {
                 tag = buffer.readUInt();
@@ -581,8 +595,8 @@
     return true;    // success
 }
 
-SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info,
-                                     bool* isValid, SkSerializationHelpers::DecodeBitmap decoder) {
+SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
+                                     SkPicture::InstallPixelRefProc proc) {
     this->init();
 
     *isValid = false;   // wait until we're done parsing to mark as true
@@ -593,7 +607,7 @@
         }
 
         uint32_t size = stream->readU32();
-        if (!this->parseStreamTag(stream, info, tag, size, decoder)) {
+        if (!this->parseStreamTag(stream, info, tag, size, proc)) {
             return; // we're invalid
         }
     }
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index cd3a6a5..fee7d78 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -63,8 +63,7 @@
     SkPicturePlayback();
     SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInfo* deepCopyInfo = NULL);
     explicit SkPicturePlayback(const SkPictureRecord& record, bool deepCopy = false);
-    SkPicturePlayback(SkStream*, const SkPictInfo&, bool* isValid,
-                      SkSerializationHelpers::DecodeBitmap decoder);
+    SkPicturePlayback(SkStream*, const SkPictInfo&, bool* isValid, SkPicture::InstallPixelRefProc);
 
     virtual ~SkPicturePlayback();
 
@@ -192,7 +191,7 @@
 
 private:    // these help us with reading/writing
     bool parseStreamTag(SkStream*, const SkPictInfo&, uint32_t tag, size_t size,
-                        SkSerializationHelpers::DecodeBitmap decoder);
+                        SkPicture::InstallPixelRefProc);
     bool parseBufferTag(SkOrderedReadBuffer&, uint32_t tag, size_t size);
     void flattenToBuffer(SkOrderedWriteBuffer&) const;
 
diff --git a/src/images/SkBitmapFactory.cpp b/src/images/SkBitmapFactory.cpp
deleted file mode 100644
index 4e5d90b..0000000
--- a/src/images/SkBitmapFactory.cpp
+++ /dev/null
@@ -1,41 +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 "SkImageDecoder.h"
-#include "SkStream.h"
-#include "SkTemplates.h"
-
-bool SkBitmapFactory::DecodeBitmap(SkBitmap* dst, const SkData* data, Constraints constraint) {
-    if (NULL == data || data->size() == 0 || dst == NULL) {
-        return false;
-    }
-
-    SkMemoryStream stream(data->data(), data->size());
-    SkAutoTDelete<SkImageDecoder> decoder (SkImageDecoder::Factory(&stream));
-    if (decoder.get() == NULL) {
-        return false;
-    }
-
-    SkBitmap tmp;
-    SkImageDecoder::Mode mode;
-    if (kDecodeBoundsOnly_Constraint == constraint) {
-        mode = SkImageDecoder::kDecodeBounds_Mode;
-    } else {
-        mode = SkImageDecoder::kDecodePixels_Mode;
-    }
-
-    if (decoder->decode(&stream, &tmp, mode)) {
-        tmp.swap(*dst);
-        return true;
-    } else {
-        return false;
-    }
-}
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index a0d7ba9..7b42676 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -9,6 +8,7 @@
 
 #include "SkImageDecoder.h"
 #include "SkBitmap.h"
+#include "SkImagePriv.h"
 #include "SkPixelRef.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
@@ -174,6 +174,69 @@
     return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
 }
 
+class TargetAllocator : public SkBitmap::Allocator {
+
+public:
+    TargetAllocator(void* target)
+        : fTarget(target) {}
+
+    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
+        // SkColorTable is not supported by Info/Target model.
+        SkASSERT(NULL == ct);
+        bm->setPixels(fTarget);
+        return true;
+    }
+
+private:
+    void* fTarget;
+};
+
+bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
+                                          SkImage::Info* info,
+                                          const SkBitmapFactory::Target* target) {
+    if (NULL == info) {
+        return false;
+    }
+    // FIXME: Just to get this working, implement in terms of existing
+    // ImageDecoder calls.
+    SkBitmap bm;
+    SkMemoryStream stream(buffer, size);
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
+    if (decoder.get() != NULL && decoder->decode(&stream, &bm, kDecodeBounds_Mode)) {
+        // Now set info properly
+        if (!SkBitmapToImageInfo(bm, info)) {
+            return false;
+        }
+
+        // SkBitmapToImageInfo will return false if Index8 is used. kIndex8
+        // is not supported by the Info/Target model, since kIndex8 requires
+        // an SkColorTable, which this model does not keep track of.
+        SkASSERT(bm.config() != SkBitmap::kIndex8_Config);
+
+        if (NULL == target) {
+            return true;
+        }
+
+        if (target->fRowBytes != (uint32_t) bm.rowBytes()) {
+            if (target->fRowBytes < SkImageMinRowBytes(*info)) {
+                SkASSERT(!"Desired row bytes is too small");
+                return false;
+            }
+            bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
+        }
+
+        TargetAllocator allocator(target->fAddr);
+        decoder->setAllocator(&allocator);
+        stream.rewind();
+        bool success = decoder->decode(&stream, &bm, kDecodePixels_Mode);
+        // Remove the allocator, since it's on the stack.
+        decoder->setAllocator(NULL);
+        return success;
+    }
+    return false;
+}
+
+
 bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
                           SkBitmap::Config pref, Mode mode, Format* format) {
     SkASSERT(stream);
diff --git a/src/lazy/SkBitmapFactory.cpp b/src/lazy/SkBitmapFactory.cpp
new file mode 100644
index 0000000..d67e019
--- /dev/null
+++ b/src/lazy/SkBitmapFactory.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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);
+}
+
+void SkBitmapFactory::setImageCache(SkImageCache *cache) {
+    SkRefCnt_SafeAssign(fImageCache, cache);
+    if (cache != NULL) {
+        fCacheSelector = NULL;
+    }
+}
+
+void SkBitmapFactory::setCacheSelector(CacheSelector selector) {
+    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;
+    }
+
+    SkImage::Info info;
+    if (!fDecodeProc(data->data(), data->size(), &info, NULL)) {
+        return false;
+    }
+
+    bool isOpaque = false;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    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);
+    dst->setIsOpaque(isOpaque);
+
+    // fImageCache and fCacheSelector are mutually exclusive.
+    SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
+
+    SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector(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/SkLazyPixelRef.cpp b/src/lazy/SkLazyPixelRef.cpp
new file mode 100644
index 0000000..c0d5d90
--- /dev/null
+++ b/src/lazy/SkLazyPixelRef.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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"
+
+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)
+    , fDecodeProc(proc)
+    , fImageCache(cache)
+    , fCacheId(SkImageCache::UNINITIALIZED_ID) {
+    SkASSERT(fDecodeProc != NULL);
+    if (NULL == data) {
+        fData = SkData::NewEmpty();
+        fErrorInDecoding = true;
+    } else {
+        fData = data;
+        fData->ref();
+        fErrorInDecoding = data->size() == 0;
+    }
+    SkASSERT(cache != NULL);
+    cache->ref();
+    // Since this pixel ref bases its data on encoded data, it should never change.
+    this->setImmutable();
+}
+
+SkLazyPixelRef::~SkLazyPixelRef() {
+    SkASSERT(fData != NULL);
+    fData->unref();
+    SkASSERT(fImageCache);
+    if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
+        fImageCache->throwAwayCache(fCacheId);
+    }
+    fImageCache->unref();
+}
+
+static size_t ComputeMinRowBytesAndSize(const SkImage::Info& info, size_t* rowBytes) {
+    *rowBytes = SkImageMinRowBytes(info);
+
+    Sk64 safeSize;
+    safeSize.setZero();
+    if (info.fHeight > 0) {
+        safeSize.setMul(info.fHeight, *rowBytes);
+    }
+    SkASSERT(!safeSize.isNeg());
+    return safeSize.is32() ? safeSize.get32() : 0;
+}
+
+void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
+    if (fErrorInDecoding) {
+        return NULL;
+    }
+    SkBitmapFactory::Target target;
+    // Check to see if the pixels still exist in the cache.
+    target.fAddr = SkImageCache::UNINITIALIZED_ID == fCacheId ?
+                   NULL : fImageCache->pinCache(fCacheId);
+    if (NULL == target.fAddr) {
+        SkImage::Info info;
+        SkASSERT(fData != NULL && fData->size() > 0);
+        // FIXME: As an optimization, only do this part once.
+        fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
+        if (fErrorInDecoding) {
+            fCacheId = SkImageCache::UNINITIALIZED_ID;
+            return NULL;
+        }
+        // Allocate the memory.
+        size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
+
+        target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
+        if (NULL == target.fAddr) {
+            // Space could not be allocated.
+            fCacheId = SkImageCache::UNINITIALIZED_ID;
+            return NULL;
+        }
+        SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
+        fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
+        if (fErrorInDecoding) {
+            fImageCache->throwAwayCache(fCacheId);
+            fCacheId = SkImageCache::UNINITIALIZED_ID;
+            return NULL;
+        }
+    }
+    return target.fAddr;
+}
+
+void SkLazyPixelRef::onUnlockPixels() {
+    if (fErrorInDecoding) {
+        return;
+    }
+    if (fCacheId != SkImageCache::UNINITIALIZED_ID) {
+        fImageCache->releaseCache(fCacheId);
+    }
+}
+
+SkData* SkLazyPixelRef::onRefEncodedData() {
+    fData->ref();
+    return fData;
+}
diff --git a/src/lazy/SkLazyPixelRef.h b/src/lazy/SkLazyPixelRef.h
new file mode 100644
index 0000000..e5a20bd
--- /dev/null
+++ b/src/lazy/SkLazyPixelRef.h
@@ -0,0 +1,63 @@
+/*
+ * 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 "SkPixelRef.h"
+#include "SkFlattenable.h"
+
+class SkColorTable;
+class SkData;
+class SkImageCache;
+
+/**
+ *  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.
+     *         Must not be NULL.
+     */
+    SkLazyPixelRef(SkData*, SkBitmapFactory::DecodeProc, SkImageCache*);
+
+    virtual ~SkLazyPixelRef();
+
+#ifdef SK_DEBUG
+    intptr_t getCacheId() const { return fCacheId; }
+#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;
+
+private:
+    bool                        fErrorInDecoding;
+    SkData*                     fData;
+    SkBitmapFactory::DecodeProc fDecodeProc;
+    SkImageCache*               fImageCache;
+    intptr_t                    fCacheId;
+
+    typedef SkPixelRef INHERITED;
+};
+
+#endif  // SkLazyPixelRef_DEFINED
diff --git a/src/lazy/SkLruImageCache.cpp b/src/lazy/SkLruImageCache.cpp
new file mode 100644
index 0000000..e8a3ed0
--- /dev/null
+++ b/src/lazy/SkLruImageCache.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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());
+#if SK_DEBUG
+        fRamUsed -= pixels->getLength();
+#endif
+        SkDELETE(pixels);
+        pixels = prev;
+    }
+#if SK_DEBUG
+    SkASSERT(fRamUsed == 0);
+#endif
+}
+
+#ifdef SK_DEBUG
+SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
+    SkAutoMutexAcquire ac(&fMutex);
+    CachedPixels* pixels = this->findByID(ID);
+    if (NULL == pixels) {
+        return SkImageCache::kThrownAway_CacheStatus;
+    }
+    if (pixels->isLocked()) {
+        return SkImageCache::kPinned_CacheStatus;
+    }
+    return SkImageCache::kUnpinned_CacheStatus;
+}
+#endif
+
+void SkLruImageCache::setBudget(size_t newBudget) {
+    SkAutoMutexAcquire ac(&fMutex);
+    fRamBudget = newBudget;
+    this->purgeIfNeeded();
+}
+
+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) {
+    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);
+    }
+    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.
+    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/ports/SkAshmemImageCache.cpp b/src/ports/SkAshmemImageCache.cpp
new file mode 100644
index 0000000..a855422
--- /dev/null
+++ b/src/ports/SkAshmemImageCache.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 "SkAshmemImageCache.h"
+#include "SkThread.h"
+
+#ifdef SK_DEBUG
+    #include "SkTSearch.h"
+#endif
+
+#include "android/ashmem.h"
+#include <sys/mman.h>
+#include <unistd.h>
+
+
+SkAshmemImageCache::SkAshmemImageCache() {}
+
+SK_DECLARE_STATIC_MUTEX(gAshmemMutex);
+
+SkAshmemImageCache* SkAshmemImageCache::GetAshmemImageCache() {
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+    static SkAshmemImageCache gCache;
+    return &gCache;
+}
+
+#ifdef SK_DEBUG
+SkAshmemImageCache::~SkAshmemImageCache() {
+    SkASSERT(fRecs.count() == 0);
+}
+#endif
+
+// ashmem likes lengths on page boundaries.
+static size_t roundToPageSize(size_t size) {
+    const size_t mask = getpagesize() - 1;
+    size_t newSize = (size + mask) & ~mask;
+    return newSize;
+}
+
+void* SkAshmemImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
+    AshmemRec rec;
+    rec.fSize = roundToPageSize(bytes);
+
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+
+    rec.fFD = ashmem_create_region(NULL, rec.fSize);
+    if (-1 == rec.fFD) {
+        SkDebugf("ashmem_create_region failed\n");
+        return NULL;
+    }
+    int err = ashmem_set_prot_region(rec.fFD, PROT_READ | PROT_WRITE);
+    if (err != 0) {
+        SkDebugf("ashmem_set_prot_region failed\n");
+        close(rec.fFD);
+        return NULL;
+    }
+    rec.fAddr = mmap(NULL, rec.fSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, rec.fFD, 0);
+    if (-1 == (long) rec.fAddr) {
+        SkDebugf("mmap failed\n");
+        close(rec.fFD);
+        return NULL;
+    }
+    (void) ashmem_pin_region(rec.fFD, 0, 0);
+#ifdef SK_DEBUG
+    rec.fPinned = true;
+#endif
+    // In release mode, we do not keep a pointer to this object. It will be destroyed
+    // either when pinCache returns NULL or when throwAwayCache is called.
+    AshmemRec* pRec = SkNEW_ARGS(AshmemRec, (rec));
+    SkASSERT(ID != NULL);
+    *ID = reinterpret_cast<intptr_t>(pRec);
+#ifdef SK_DEBUG
+    this->appendRec(pRec);
+#endif
+    return rec.fAddr;
+}
+
+void* SkAshmemImageCache::pinCache(intptr_t ID) {
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+    AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
+    const int fd = rec->fFD;
+    int pin = ashmem_pin_region(fd, 0, 0);
+    if (ASHMEM_NOT_PURGED == pin) {
+#ifdef SK_DEBUG
+        rec->fPinned = true;
+#endif
+        return rec->fAddr;
+    }
+    // Purged. Remove the associated AshmemRec:
+    this->removeRec(rec);
+    ashmem_unpin_region(fd, 0, 0);
+    return NULL;
+}
+
+void SkAshmemImageCache::releaseCache(intptr_t ID) {
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+    AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
+    ashmem_unpin_region(rec->fFD, 0, 0);
+#ifdef SK_DEBUG
+    rec->fPinned = false;
+#endif
+}
+
+void SkAshmemImageCache::throwAwayCache(intptr_t ID) {
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+    AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
+#ifdef SK_DEBUG
+    SkASSERT(!rec->fPinned);
+#endif
+    this->removeRec(rec);
+}
+
+void SkAshmemImageCache::removeRec(SkAshmemImageCache::AshmemRec* rec) {
+    munmap(rec->fAddr, rec->fSize);
+    close(rec->fFD);
+#ifdef SK_DEBUG
+    int index = this->findRec(rec);
+    SkASSERT(index >= 0);
+    fRecs.remove(index);
+#endif
+    SkDELETE(rec);
+}
+
+#ifdef SK_DEBUG
+void SkAshmemImageCache::appendRec(SkAshmemImageCache::AshmemRec* rec) {
+    int index = this->findRec(rec);
+    // Should not already exist.
+    SkASSERT(index < 0);
+    fRecs.insert(~index, 1, &rec);
+}
+
+int SkAshmemImageCache::AshmemRec::Compare(const SkAshmemImageCache::AshmemRec* a,
+                                           const SkAshmemImageCache::AshmemRec* b) {
+    return reinterpret_cast<intptr_t>(a) - reinterpret_cast<intptr_t>(b);
+}
+
+int SkAshmemImageCache::findRec(const SkAshmemImageCache::AshmemRec* rec) const {
+    return SkTSearch<AshmemRec>((const AshmemRec**)fRecs.begin(), fRecs.count(), rec,
+                                sizeof(intptr_t), AshmemRec::Compare);
+}
+
+SkImageCache::CacheStatus SkAshmemImageCache::getCacheStatus(intptr_t ID) const {
+    SkAutoMutexAcquire ac(&gAshmemMutex);
+    AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
+    int index = this->findRec(rec);
+    if (index < 0) {
+        return SkImageCache::kThrownAway_CacheStatus;
+    }
+    return rec->fPinned ? SkImageCache::kPinned_CacheStatus
+                        : SkImageCache::kUnpinned_CacheStatus;
+}
+#endif
diff --git a/tests/BitmapFactoryTest.cpp b/tests/BitmapFactoryTest.cpp
index b24fd25..8bfbaf1 100644
--- a/tests/BitmapFactoryTest.cpp
+++ b/tests/BitmapFactoryTest.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -6,17 +5,26 @@
  * found in the LICENSE file.
  */
 
+#ifdef SK_DEBUG
+
 #include "SkBitmap.h"
 #include "SkBitmapFactory.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkData.h"
+#include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
+#include "SkLazyPixelRef.h"
+#include "SkLruImageCache.h"
 #include "SkPaint.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
 #include "Test.h"
 
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkAshmemImageCache.h"
+#endif
+
 static SkBitmap* create_bitmap() {
     SkBitmap* bm = SkNEW(SkBitmap);
     const int W = 100, H = 100;
@@ -44,6 +52,45 @@
     REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
 }
 
+static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
+                       const SkBitmap& origBitmap) {
+    SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
+    factory.setImageCache(cache);
+    SkAutoTDelete<SkBitmap> bitmapFromFactory(SkNEW(SkBitmap));
+    bool success = factory.installPixelRef(encodedData, bitmapFromFactory.get());
+    // This assumes that if the encoder worked, the decoder should also work, so the above call
+    // should not fail.
+    REPORTER_ASSERT(reporter, success);
+    assert_bounds_equal(reporter, origBitmap, *bitmapFromFactory.get());
+
+    SkPixelRef* pixelRef = bitmapFromFactory->pixelRef();
+    REPORTER_ASSERT(reporter, pixelRef != NULL);
+    if (NULL == cache) {
+        // This assumes that installPixelRef called lockPixels.
+        REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
+    } else {
+        // Lazy decoding
+        REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
+        SkLazyPixelRef* lazyRef = static_cast<SkLazyPixelRef*>(pixelRef);
+        int32_t cacheID = lazyRef->getCacheId();
+        REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
+                                  != SkImageCache::kPinned_CacheStatus);
+        {
+            SkAutoLockPixels alp(*bitmapFromFactory.get());
+            REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
+            cacheID = lazyRef->getCacheId();
+            REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
+                                      == SkImageCache::kPinned_CacheStatus);
+        }
+        REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
+        REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
+                                  != SkImageCache::kPinned_CacheStatus);
+        bitmapFromFactory.free();
+        REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
+                                  == SkImageCache::kThrownAway_CacheStatus);
+    }
+}
+
 static void TestBitmapFactory(skiatest::Reporter* reporter) {
     SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
     SkASSERT(bitmap.get() != NULL);
@@ -54,23 +101,15 @@
         return;
     }
 
-    SkBitmap bitmapFromFactory;
-    bool success = SkBitmapFactory::DecodeBitmap(&bitmapFromFactory, encodedBitmap);
-    // This assumes that if the encoder worked, the decoder should also work, so the above call
-    // should not fail.
-    REPORTER_ASSERT(reporter, success);
-    assert_bounds_equal(reporter, *bitmap.get(), bitmapFromFactory);
-    REPORTER_ASSERT(reporter, bitmapFromFactory.pixelRef() != NULL);
-
-    // When only requesting that the bounds be decoded, the bounds should be set properly while
-    // the pixels should be empty.
-    SkBitmap boundedBitmap;
-    success = SkBitmapFactory::DecodeBitmap(&boundedBitmap, encodedBitmap,
-                                            SkBitmapFactory::kDecodeBoundsOnly_Constraint);
-    REPORTER_ASSERT(reporter, success);
-    assert_bounds_equal(reporter, *bitmap.get(), boundedBitmap);
-    REPORTER_ASSERT(reporter, boundedBitmap.pixelRef() == NULL);
+    SkAutoTUnref<SkLruImageCache> lruCache(SkNEW_ARGS(SkLruImageCache, (1024 * 1024)));
+    test_cache(reporter, lruCache, encodedBitmap, *bitmap.get());
+    test_cache(reporter, NULL, encodedBitmap, *bitmap.get());
+#ifdef SK_BUILD_FOR_ANDROID
+    test_cache(reporter, SkAshmemImageCache::GetAshmemImageCache(), encodedBitmap, *bitmap.get());
+#endif
 }
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("BitmapFactory", TestBitmapFactoryClass, TestBitmapFactory)
+
+#endif // SK_DEBUG
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 579830d..60d79fc 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -9,6 +9,7 @@
 #include "CopyTilesRenderer.h"
 #include "PictureBenchmark.h"
 #include "SkBenchLogger.h"
+#include "SkBitmapFactory.h"
 #include "SkCanvas.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
@@ -126,6 +127,7 @@
 "     [--pipe]\n"
 "     [--bbh bbhType]\n"
 "     [--multi numThreads]\n"
+"     [--enable-deferred-image-decoding]\n"
 "     [--viewport width height][--scale sf]\n"
 "     [--device bitmap"
 #if SK_SUPPORT_GPU
@@ -186,6 +188,8 @@
     SkDebugf(
 "     --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
 "                          than 1. Only works with tiled rendering.\n"
+"     --enable-deferred-image-decoding : Defer decoding until drawing images. Has no effect if\n"
+"                      the provided skp does not have its images encoded.\n"
 "     --viewport width height : Set the viewport.\n"
 "     --scale sf : Scale drawing by sf.\n"
 "     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
@@ -227,6 +231,22 @@
 
 SkBenchLogger gLogger;
 
+bool lazy_decode = false;
+
+#include "SkData.h"
+#include "SkLruImageCache.h"
+
+static SkLruImageCache gLruImageCache(1024*1024);
+
+static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
+    void* copiedBuffer = sk_malloc_throw(size);
+    memcpy(copiedBuffer, buffer, size);
+    SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
+    SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
+    factory.setImageCache(&gLruImageCache);
+    return factory.installPixelRef(data, bitmap);
+}
+
 static bool run_single_benchmark(const SkString& inputPath,
                                  sk_tools::PictureBenchmark& benchmark) {
     SkFILEStream inputStream;
@@ -240,7 +260,14 @@
     }
 
     bool success = false;
-    SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
+    SkPicture* picture;
+    if (lazy_decode) {
+        picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap));
+    } else {
+        picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory));
+    }
+    SkAutoTDelete<SkPicture> ad(picture);
+
     if (!success) {
         SkString err;
         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
@@ -252,11 +279,11 @@
     sk_tools::get_basename(&filename, inputPath);
 
     SkString result;
-    result.printf("running bench [%i %i] %s ", picture.width(),
-                  picture.height(), filename.c_str());
+    result.printf("running bench [%i %i] %s ", picture->width(), picture->height(),
+                  filename.c_str());
     gLogger.logProgress(result);
 
-    benchmark.run(&picture);
+    benchmark.run(picture);
     return true;
 }
 
@@ -538,6 +565,8 @@
             benchmark->setTimeIndividualTiles(true);
         } else if (0 == strcmp(*argv, "--min")) {
             benchmark->setPrintMin(true);
+        } else if (0 == strcmp(*argv, "--enable-deferred-image-decoding")) {
+            lazy_decode = true;
         } else if (0 == strcmp(*argv, "--logPerIter")) {
             ++argv;
             if (argv < stop) {
diff --git a/tools/filtermain.cpp b/tools/filtermain.cpp
index 3133ada..e0ab835 100644
--- a/tools/filtermain.cpp
+++ b/tools/filtermain.cpp
@@ -181,7 +181,7 @@
 
     SkFILEStream inStream(inFile.c_str());
     if (inStream.isValid()) {
-        inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeStream));
+        inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeMemory));
     }
 
     if (NULL == inPicture) {
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
index 3aac682..6c2543b 100644
--- a/tools/render_pdfs_main.cpp
+++ b/tools/render_pdfs_main.cpp
@@ -8,6 +8,7 @@
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkGraphics.h"
+#include "SkImageDecoder.h"
 #include "SkOSFile.h"
 #include "SkPicture.h"
 #include "SkStream.h"
@@ -129,7 +130,7 @@
 
     bool success = false;
     SkAutoTUnref<SkPicture>
-        picture(SkNEW_ARGS(SkPicture, (&inputStream, &success)));
+        picture(SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory)));
 
     if (!success) {
         SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index 3ab5628..146b045 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -7,6 +7,7 @@
 
 #include "CopyTilesRenderer.h"
 #include "SkBitmap.h"
+#include "SkBitmapFactory.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkGraphics.h"
@@ -35,6 +36,7 @@
 "     [--validate [--maxComponentDiff n]]\n"
 "     [--writeWholeImage]\n"
 "     [--clone n]\n"
+"     [--enable-deferred-image-decoding]\n"
 "     [--viewport width height][--scale sf]\n"
 "     [--device bitmap"
 #if SK_SUPPORT_GPU
@@ -83,6 +85,8 @@
     SkDebugf(
 "     --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
 "                     than 1. Only works with tiled rendering.\n"
+"     --enable-deferred-image-decoding : Defer decoding until drawing images. Has no effect if\n"
+"                      the provided skp does not have its images encoded.\n"
 "     --viewport width height : Set the viewport.\n"
 "     --scale sf : Scale drawing by sf.\n"
 "     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
@@ -127,6 +131,39 @@
     path->remove(path->size() - 4, 4);
 }
 
+bool lazy_decode = false;
+
+#include "SkData.h"
+#include "SkLruImageCache.h"
+
+static SkLruImageCache gLruImageCache(1024*1024);
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkAshmemImageCache.h"
+#include "SkImage.h"
+
+static SkImageCache* cache_selector(const SkImage::Info& info) {
+    if (info.fWidth * info.fHeight > 32 * 1024) {
+        return SkAshmemImageCache::GetAshmemImageCache();
+    }
+    return &gLruImageCache;
+}
+
+#endif
+
+static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
+    void* copiedBuffer = sk_malloc_throw(size);
+    memcpy(copiedBuffer, buffer, size);
+    SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
+    SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
+#ifdef SK_BUILD_FOR_ANDROID
+    factory.setCacheSelector(&cache_selector);
+#else
+    factory.setImageCache(&gLruImageCache);
+#endif
+    return factory.installPixelRef(data, bitmap);
+}
+
 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
                            sk_tools::PictureRenderer& renderer,
                            SkBitmap** out,
@@ -142,8 +179,12 @@
     }
 
     bool success = false;
-    SkPicture* picture = SkNEW_ARGS(SkPicture,
-            (&inputStream, &success, &SkImageDecoder::DecodeStream));
+    SkPicture* picture;
+    if (lazy_decode) {
+        picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap));
+    } else {
+        picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory));
+    }
     if (!success) {
         SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
         return false;
@@ -536,7 +577,8 @@
                 usage(argv0);
                 exit(-1);
             }
-
+        } else if (0 == strcmp(*argv, "--enable-deferred-image-decoding")) {
+            lazy_decode = true;
         } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
             SkSafeUnref(renderer);
             usage(argv0);