Improvements/additions to SkImageCache/SkLazyPixelRef.
authorscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 18 Mar 2013 21:37:39 +0000 (21:37 +0000)
committerscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 18 Mar 2013 21:37:39 +0000 (21:37 +0000)
SkPurgeableImageCache:
New image cache that uses virtual memory to store the pixels. Combines
features of SkAshmemImageCache (which has been removed) with SkPurgeableMemoryBlock, which has android and Mac versions.

SkImageCache:
Modified the API. pinCache now returns a status out parameter which
states whether the pinned memory retained the old data. This allows
allocAndPinCache to only be used for allocations.
Add a new debug only interface to purge unpinned data.
Updates to documentation, clarifying behavior.
Changed CachedStatus to MemoryStatus

SkLruImageCache:
Implement the new function purgeAllUnpinnedCaches and change implementation
of pinCache for the new behavior.

SkLazyPixelRef:
Rewrite onLockPixels to account for the new behavior of pinCache.

BitmapFactoryTest:
Test the new SkPurgeableImageCache.
Write tests which directly test the SkImageCaches.
Create a larger bitmap, since some of the SkImageCaches are designed
to handle large bitmaps.

bench_ and render_pictures:
Consolidate lazy_decode_bitmap into one function.
Allow using a flag to specify using the purgeable image cache.
Clean up some #includes.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@8207 2bbb7eff-a529-9590-31e7-b0007b416f81

19 files changed:
gyp/core.gypi
gyp/ports.gyp
gyp/tools.gyp
include/lazy/SkBitmapFactory.h
include/lazy/SkImageCache.h
include/lazy/SkLruImageCache.h
include/lazy/SkPurgeableImageCache.h [new file with mode: 0644]
include/ports/SkAshmemImageCache.h [deleted file]
src/lazy/SkBitmapFactory.cpp
src/lazy/SkLazyPixelRef.cpp
src/lazy/SkLazyPixelRef.h
src/lazy/SkLruImageCache.cpp
src/lazy/SkPurgeableImageCache.cpp [new file with mode: 0644]
src/ports/SkAshmemImageCache.cpp [deleted file]
tests/BitmapFactoryTest.cpp
tools/PictureRenderingFlags.cpp
tools/PictureRenderingFlags.h
tools/bench_pictures_main.cpp
tools/render_pictures_main.cpp

index 23a652a..3810334 100644 (file)
         '<(skia_include_path)/lazy/SkBitmapFactory.h',
         '<(skia_include_path)/lazy/SkImageCache.h',
         '<(skia_include_path)/lazy/SkLruImageCache.h',
+        '<(skia_include_path)/lazy/SkPurgeableImageCache.h',
 
         '<(skia_src_path)/lazy/SkBitmapFactory.cpp',
         '<(skia_src_path)/lazy/SkLazyPixelRef.h',
         '<(skia_src_path)/lazy/SkLruImageCache.cpp',
         '<(skia_src_path)/lazy/SkPurgeableMemoryBlock.h',
         '<(skia_src_path)/lazy/SkPurgeableMemoryBlock_common.cpp',
+        '<(skia_src_path)/lazy/SkPurgeableImageCache.cpp',
     ],
 }
 
index 7814148..db4ea7a 100644 (file)
             '../src/ports/SkPurgeableMemoryBlock_none.cpp',
           ],
           'sources': [
-            '../include/ports/SkAshmemImageCache.h',
-
+            '../src/ports/FontHostConfiguration_android.cpp',
             '../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/SkPurgeableMemoryBlock_android.cpp',
-            '../src/ports/FontHostConfiguration_android.cpp',
-            '../src/ports/SkAshmemImageCache.cpp',
           ],
           'dependencies': [
              'freetype.gyp:freetype',
index 4e3bc53..9cbaf93 100644 (file)
         'skia_base_libs.gyp:skia_base_libs',
         'tools.gyp:picture_renderer',
         'tools.gyp:picture_utils',
+        'ports.gyp:ports',
       ],
     },
     {
         'tools.gyp:picture_utils',
         'tools.gyp:picture_renderer',
         'bench.gyp:bench_timer',
+        'ports.gyp:ports',
       ],
     },
     {
index bebd3a7..eb427ee 100644 (file)
@@ -67,20 +67,29 @@ public:
     bool installPixelRef(SkData*, SkBitmap*);
 
     /**
-     *  A function for selecting an SkImageCache to use based on an SkImage::Info.
+     *  An object for selecting an SkImageCache to use based on an SkImage::Info.
      */
-    typedef SkImageCache* (*CacheSelector)(const SkImage::Info&);
+    class CacheSelector : public SkRefCnt {
+
+    public:
+        /**
+         *  Return an SkImageCache to use based on the provided SkImage::Info. If the caller decides
+         *  to hang on to the result, it will call ref, so the implementation should not add a ref
+         *  as a result of this call.
+         */
+        virtual SkImageCache* selectCache(const SkImage::Info&) = 0;
+    };
 
     /**
      *  Set the function to be used to select which SkImageCache to use. Mutually exclusive with
      *  fImageCache.
      */
-    void setCacheSelector(CacheSelector);
+    void setCacheSelector(CacheSelector*);
 
 private:
-    DecodeProc    fDecodeProc;
-    SkImageCache* fImageCache;
-    CacheSelector fCacheSelector;
+    DecodeProc     fDecodeProc;
+    SkImageCache*  fImageCache;
+    CacheSelector* fCacheSelector;
 };
 
 #endif // SkBitmapFactory_DEFINED
index 6cd064b..bfd5269 100644 (file)
@@ -22,22 +22,45 @@ public:
      *  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.
+     *      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.
+     *      releaseCache is called with ID.
      */
     virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) = 0;
 
     /**
-     *  Re-request the memory associated with ID.
+     *  Output parameter for pinCache, stating whether the memory still contains the data it held
+     *  when releaseCache was last called for the same ID.
+     */
+    enum DataStatus {
+        /**
+         *  The data has been purged, and therefore needs to be rewritten to the returned memory.
+         */
+        kUninitialized_DataStatus,
+
+        /**
+         *  The memory still contains the data it held when releaseCache was last called with the
+         *  same ID.
+         */
+        kRetained_DataStatus,
+    };
+
+    /**
+     *  Re-request the memory associated with ID and pin it so that it will not be reclaimed until
+     *  the next call to releaseCache with the same ID.
      *  @param ID Unique ID for the memory block.
+     *  @param status Output parameter which must not be NULL. On success (i.e. the return value is
+     *      not NULL), status will be set to one of two states representing the cached memory. If
+     *      status is set to kRetained_DataStatus, the memory contains the same data it did
+     *      before releaseCache was called with this ID. If status is set to
+     *      kUninitialized_DataStatus, the memory is still pinned, but the previous data is no
+     *      longer available. If the return value is NULL, status is unchanged.
      *  @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 with a pointer to
-     *          the same ID.
+     *      this call must be balanced with a call to releaseCache. If NULL, the memory
+     *      has been reclaimed, and throwAwayCache MUST NOT be called.
      */
-    virtual void* pinCache(intptr_t ID) = 0;
+    virtual void* pinCache(intptr_t ID, DataStatus* status) = 0;
 
     /**
      *  Inform the cache that it is safe to free the block of memory corresponding to ID. After
@@ -61,16 +84,42 @@ public:
     static const intptr_t UNINITIALIZED_ID = 0;
 
 #ifdef SK_DEBUG
-    enum CacheStatus {
-        kPinned_CacheStatus,
-        kUnpinned_CacheStatus,
-        kThrownAway_CacheStatus,
+    /**
+     *  Debug only status of a memory block.
+     */
+    enum MemoryStatus {
+        /**
+         *  It is safe to use the pointer returned by the most recent of allocAndPinCache(ID) or
+         *  pinCache(ID) with the same ID.
+         */
+        kPinned_MemoryStatus,
+
+        /**
+         *  The pointer returned by the most recent call to allocAndPinCache(ID) or pinCache(ID) has
+         *  since been released by releaseCache(ID). In order to reuse it, pinCache(ID) must be
+         *  called again. Note that after calling releaseCache(ID), the status of that particular
+         *  ID may not be kUnpinned_MemoryStatus, depending on the implementation, but it will not
+         *  be kPinned_MemoryStatus.
+         */
+        kUnpinned_MemoryStatus,
+
+        /**
+         *  The memory associated with ID has been thrown away. No calls should be made using the
+         *  same ID.
+         */
+        kFreed_MemoryStatus,
     };
 
     /**
-     *  Debug only function to get the status of a particular block of memory.
+     *  Debug only function to get the status of a particular block of memory. Safe to call after
+     *  throwAwayCache has been called with this ID.
+     */
+    virtual MemoryStatus getMemoryStatus(intptr_t ID) const = 0;
+
+    /**
+     *  Debug only function to clear all unpinned caches.
      */
-    virtual CacheStatus getCacheStatus(intptr_t ID) const = 0;
+    virtual void purgeAllUnpinnedCaches() = 0;
 #endif
 };
 #endif // SkImageCache_DEFINED
index 05d2815..f655230 100644 (file)
@@ -25,7 +25,8 @@ public:
     virtual ~SkLruImageCache();
 
 #ifdef SK_DEBUG
-    CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
+    virtual MemoryStatus getMemoryStatus(intptr_t ID) const SK_OVERRIDE;
+    virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
 #endif
 
     /**
@@ -45,7 +46,7 @@ public:
     size_t getImageCacheUsed() const { return fRamUsed; }
 
     virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
-    virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
+    virtual void* pinCache(intptr_t ID, SkImageCache::DataStatus*) SK_OVERRIDE;
     virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
     virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
 
@@ -55,7 +56,7 @@ private:
     typedef SkTInternalLList<CachedPixels>::Iter Iter;
 
 #ifdef SK_DEBUG
-    // fMutex is mutable so that getCacheStatus can be const
+    // fMutex is mutable so that getMemoryStatus can be const
     mutable
 #endif
     SkMutex fMutex;
diff --git a/include/lazy/SkPurgeableImageCache.h b/include/lazy/SkPurgeableImageCache.h
new file mode 100644 (file)
index 0000000..0516ff1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 SkPurgeableImageCache_DEFINED
+#define SkPurgeableImageCache_DEFINED
+
+#include "SkImageCache.h"
+
+#ifdef SK_DEBUG
+    #include "SkTDArray.h"
+#endif
+
+/**
+ *  Implementation for SkImageCache that uses system defined purgeable memory.
+ */
+class SkPurgeableImageCache : public SkImageCache {
+
+public:
+    static SkImageCache* Create();
+
+    virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
+    virtual void* pinCache(intptr_t ID, SkImageCache::DataStatus*) SK_OVERRIDE;
+    virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
+    virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
+
+#ifdef SK_DEBUG
+    virtual MemoryStatus getMemoryStatus(intptr_t ID) const SK_OVERRIDE;
+    virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
+    virtual ~SkPurgeableImageCache();
+#endif
+
+private:
+    SkPurgeableImageCache();
+
+#ifdef SK_DEBUG
+    SkTDArray<intptr_t> fRecs;
+    int findRec(intptr_t) const;
+#endif
+    void removeRec(intptr_t);
+};
+#endif // SkPurgeableImageCache_DEFINED
diff --git a/include/ports/SkAshmemImageCache.h b/include/ports/SkAshmemImageCache.h
deleted file mode 100644 (file)
index 817e702..0000000
+++ /dev/null
@@ -1,72 +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.
- */
-
-#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
index d67e019..60c4993 100644 (file)
@@ -22,17 +22,19 @@ SkBitmapFactory::SkBitmapFactory(SkBitmapFactory::DecodeProc proc)
 
 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) {
-    fCacheSelector = selector;
+void SkBitmapFactory::setCacheSelector(CacheSelector* selector) {
+    SkRefCnt_SafeAssign(fCacheSelector, selector);
     if (selector != NULL) {
         SkSafeUnref(fImageCache);
         fImageCache = NULL;
@@ -63,7 +65,7 @@ bool SkBitmapFactory::installPixelRef(SkData* data, SkBitmap* dst) {
     // fImageCache and fCacheSelector are mutually exclusive.
     SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
 
-    SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector(info);
+    SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector->selectCache(info);
 
     if (cache != NULL) {
         // Now set a new LazyPixelRef on dst.
index 9ae22f2..dc9aef9 100644 (file)
@@ -24,7 +24,8 @@ SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, S
     : INHERITED(NULL)
     , fDecodeProc(proc)
     , fImageCache(cache)
-    , fCacheId(SkImageCache::UNINITIALIZED_ID) {
+    , fCacheId(SkImageCache::UNINITIALIZED_ID)
+    , fRowBytes(0) {
     SkASSERT(fDecodeProc != NULL);
     if (NULL == data) {
         fData = SkData::NewEmpty();
@@ -71,37 +72,54 @@ void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
     if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
         target.fAddr = NULL;
     } else {
-        target.fAddr = fImageCache->pinCache(fCacheId);
-        if (NULL != target.fAddr) {
+        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);
+                sk_atomic_inc(&gCacheHits);
 #endif
-            return target.fAddr;
+                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(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) {
-        // In case a previous call to allocAndPinCache succeeded.
-        fImageCache->throwAwayCache(fCacheId);
-        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;
+        // 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) {
+            // We can only reach here if fCacheId was already set to UNINITIALIZED_ID, or if
+            // pinCache returned NULL, in which case it was reset to UNINITIALIZED_ID.
+            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(), &info, &target);
     if (fErrorInDecoding) {
@@ -109,6 +127,8 @@ void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
         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;
 }
 
index af85f90..fd41dd4 100644 (file)
@@ -68,6 +68,7 @@ private:
     SkBitmapFactory::DecodeProc fDecodeProc;
     SkImageCache*               fImageCache;
     intptr_t                    fCacheId;
+    size_t                      fRowBytes;
 
 #if LAZY_CACHE_STATS
     static int32_t              gCacheHits;
index 54f26fb..26f7ef5 100644 (file)
@@ -74,16 +74,24 @@ SkLruImageCache::~SkLruImageCache() {
 }
 
 #ifdef SK_DEBUG
-SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
+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::kThrownAway_CacheStatus;
+        return SkImageCache::kFreed_MemoryStatus;
     }
     if (pixels->isLocked()) {
-        return SkImageCache::kPinned_CacheStatus;
+        return SkImageCache::kPinned_MemoryStatus;
     }
-    return SkImageCache::kUnpinned_CacheStatus;
+    return SkImageCache::kUnpinned_MemoryStatus;
+}
+
+void SkLruImageCache::purgeAllUnpinnedCaches() {
+    SkAutoMutexAcquire ac(&fMutex);
+    this->purgeTilAtOrBelow(0);
 }
 #endif
 
@@ -108,7 +116,7 @@ void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
     return pixels->getData();
 }
 
-void* SkLruImageCache::pinCache(intptr_t ID) {
+void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
     SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
     SkAutoMutexAcquire ac(&fMutex);
     CachedPixels* pixels = this->findByID(ID);
@@ -119,6 +127,9 @@ void* SkLruImageCache::pinCache(intptr_t ID) {
         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();
 }
@@ -133,6 +144,7 @@ void SkLruImageCache::releaseCache(intptr_t ID) {
 }
 
 void SkLruImageCache::throwAwayCache(intptr_t ID) {
+    SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
     SkAutoMutexAcquire ac(&fMutex);
     CachedPixels* pixels = this->findByID(ID);
     if (pixels != NULL) {
@@ -155,9 +167,6 @@ void SkLruImageCache::removePixels(CachedPixels* pixels) {
 
 CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
     // Mutex is already locked.
-    if (SkImageCache::UNINITIALIZED_ID == ID) {
-        return NULL;
-    }
     Iter iter;
     // Start from the head, most recently used.
     CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
diff --git a/src/lazy/SkPurgeableImageCache.cpp b/src/lazy/SkPurgeableImageCache.cpp
new file mode 100644 (file)
index 0000000..0f2c5e3
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * 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/SkAshmemImageCache.cpp b/src/ports/SkAshmemImageCache.cpp
deleted file mode 100644 (file)
index b7c6c70..0000000
+++ /dev/null
@@ -1,161 +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 "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) {
-    SkASSERT(ID != NULL);
-
-    SkAutoMutexAcquire ac(&gAshmemMutex);
-
-    if (*ID != SkImageCache::UNINITIALIZED_ID) {
-        // This rec was previously allocated, but pinCache subsequently
-        // failed.
-        AshmemRec* pRec = reinterpret_cast<AshmemRec*>(*ID);
-        SkASSERT(roundToPageSize(bytes) == pRec->fSize);
-        SkASSERT(pRec->fFD != -1);
-        (void) ashmem_pin_region(pRec->fFD, 0, 0);
-#ifdef SK_DEBUG
-        pRec->fPinned = true;
-#endif
-        return pRec->fAddr;
-    }
-
-    AshmemRec rec;
-    rec.fSize = roundToPageSize(bytes);
-
-    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));
-    *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;
-    }
-    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);
-    munmap(rec->fAddr, rec->fSize);
-    close(rec->fFD);
-#ifdef SK_DEBUG
-    SkASSERT(!rec->fPinned);
-    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
index 8bfbaf1..216903a 100644 (file)
 #include "SkLazyPixelRef.h"
 #include "SkLruImageCache.h"
 #include "SkPaint.h"
+#include "SkPurgeableImageCache.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;
+    // Use a large bitmap.
+    const int W = 1000, H = 1000;
     bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
     bm->allocPixels();
     bm->eraseColor(SK_ColorBLACK);
@@ -52,7 +50,53 @@ static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm
     REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
 }
 
-static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
+static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache) {
+    // Test the cache directly:
+    cache->purgeAllUnpinnedCaches();
+    intptr_t ID = SkImageCache::UNINITIALIZED_ID;
+    const size_t size = 1000;
+    char buffer[size];
+    sk_bzero((void*) buffer, size);
+    void* memory = cache->allocAndPinCache(size, &ID);
+    if (memory != NULL) {
+        memcpy(memory, (void*)buffer, size);
+        REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) == SkImageCache::kPinned_MemoryStatus);
+        cache->releaseCache(ID);
+        REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) != SkImageCache::kPinned_MemoryStatus);
+        SkImageCache::DataStatus dataStatus;
+        memory = cache->pinCache(ID, &dataStatus);
+        if (memory != NULL) {
+            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                      == SkImageCache::kPinned_MemoryStatus);
+            if (SkImageCache::kRetained_DataStatus == dataStatus) {
+                REPORTER_ASSERT(reporter, !memcmp(memory, (void*) buffer, size));
+            }
+            cache->releaseCache(ID);
+            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                      != SkImageCache::kPinned_MemoryStatus);
+            cache->purgeAllUnpinnedCaches();
+            REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                      != SkImageCache::kPinned_MemoryStatus);
+            memory = cache->pinCache(ID, &dataStatus);
+            if (memory != NULL) {
+                // Since the cache was thrown away, and ID was not pinned, it should have
+                // been purged.
+                REPORTER_ASSERT(reporter, SkImageCache::kUninitialized_DataStatus == dataStatus);
+                cache->releaseCache(ID);
+                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                          != SkImageCache::kPinned_MemoryStatus);
+                cache->throwAwayCache(ID);
+                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                          == SkImageCache::kFreed_MemoryStatus);
+            } else {
+                REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
+                                          == SkImageCache::kFreed_MemoryStatus);
+            }
+        }
+    }
+}
+
+static void test_factory(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
                        const SkBitmap& origBitmap) {
     SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
     factory.setImageCache(cache);
@@ -72,41 +116,80 @@ static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache, SkData
         // 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);
+        intptr_t cacheID = lazyRef->getCacheId();
+        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
+                                  != SkImageCache::kPinned_MemoryStatus);
         {
             SkAutoLockPixels alp(*bitmapFromFactory.get());
             REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
             cacheID = lazyRef->getCacheId();
-            REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
-                                      == SkImageCache::kPinned_CacheStatus);
+            REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
+                                      == SkImageCache::kPinned_MemoryStatus);
         }
         REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
-        REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
-                                  != SkImageCache::kPinned_CacheStatus);
+        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
+                                  != SkImageCache::kPinned_MemoryStatus);
         bitmapFromFactory.free();
-        REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
-                                  == SkImageCache::kThrownAway_CacheStatus);
+        REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
+                                  == SkImageCache::kFreed_MemoryStatus);
     }
 }
 
+class ImageCacheHolder : public SkNoncopyable {
+
+public:
+    ~ImageCacheHolder() {
+        fCaches.safeUnrefAll();
+    }
+
+    void addImageCache(SkImageCache* cache) {
+        SkSafeRef(cache);
+        *fCaches.append() = cache;
+    }
+
+    int count() const { return fCaches.count(); }
+
+    SkImageCache* getAt(int i) {
+        if (i < 0 || i > fCaches.count()) {
+            return NULL;
+        }
+        return fCaches.getAt(i);
+    }
+
+private:
+    SkTDArray<SkImageCache*> fCaches;
+};
+
 static void TestBitmapFactory(skiatest::Reporter* reporter) {
     SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
     SkASSERT(bitmap.get() != NULL);
 
     SkAutoDataUnref encodedBitmap(create_data_from_bitmap(*bitmap.get()));
-    if (encodedBitmap.get() == NULL) {
-        // Encoding failed.
-        return;
-    }
+    bool encodeSucceeded = encodedBitmap.get() != NULL;
+    SkASSERT(encodeSucceeded);
+
+    ImageCacheHolder cacheHolder;
 
     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
+    cacheHolder.addImageCache(lruCache);
+
+    cacheHolder.addImageCache(NULL);
+
+    SkImageCache* purgeableCache = SkPurgeableImageCache::Create();
+    if (purgeableCache != NULL) {
+        cacheHolder.addImageCache(purgeableCache);
+        purgeableCache->unref();
+    }
+
+    for (int i = 0; i < cacheHolder.count(); i++) {
+        SkImageCache* cache = cacheHolder.getAt(i);
+        if (cache != NULL) {
+            test_cache(reporter, cache);
+        }
+        if (encodeSucceeded) {
+            test_factory(reporter, cache, encodedBitmap, *bitmap.get());
+        }
+    }
 }
 
 #include "TestClassDef.h"
index 3cb1e96..29046ae 100644 (file)
 #include "PictureRenderer.h"
 #include "picture_utils.h"
 
+#include "SkBitmapFactory.h"
+#include "SkData.h"
 #include "SkFlags.h"
+#include "SkImage.h"
+#include "SkImageDecoder.h"
+#include "SkLruImageCache.h"
+#include "SkPurgeableImageCache.h"
+#include "SkString.h"
 
 // Alphabetized list of flags used by this file or bench_ and render_pictures.
 DEFINE_string(bbh, "none", "bbhType [width height]: Set the bounding box hierarchy type to "
@@ -57,6 +64,9 @@ DEFINE_string(r, "", "skp files or directories of skp files to process.");
 DEFINE_double(scale, 1, "Set the scale factor.");
 DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
               "in the x and y directions.");
+DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. "
+            "Only meaningful if --deferImageDecoding is set to true and the platform has an "
+            "implementation.");
 DEFINE_string(viewport, "", "width height: Set the viewport.");
 
 sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
@@ -308,3 +318,52 @@ sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
 
     return renderer.detach();
 }
+
+SkLruImageCache gLruImageCache(1024*1024);
+
+// Simple cache selector to choose between a purgeable cache for large images and the standard one
+// for smaller images.
+class MyCacheSelector : public SkBitmapFactory::CacheSelector {
+
+public:
+    MyCacheSelector() {
+        fPurgeableImageCache = SkPurgeableImageCache::Create();
+    }
+
+    ~MyCacheSelector() {
+        SkSafeUnref(fPurgeableImageCache);
+    }
+
+    virtual SkImageCache* selectCache(const SkImage::Info& info) SK_OVERRIDE {
+        if (info.fWidth * info.fHeight > 32 * 1024 && fPurgeableImageCache != NULL) {
+            return fPurgeableImageCache;
+        }
+        return &gLruImageCache;
+    }
+private:
+    SkImageCache* fPurgeableImageCache;
+};
+
+static MyCacheSelector gCacheSelector;
+static SkBitmapFactory gFactory(&SkImageDecoder::DecodeMemoryToTarget);
+
+bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
+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));
+
+    static bool gOnce;
+    if (!gOnce) {
+        // Only use the cache selector if there is a purgeable image cache to use for large
+        // images.
+        if (FLAGS_useVolatileCache && SkAutoTUnref<SkImageCache>(
+                SkPurgeableImageCache::Create()).get() != NULL) {
+            gFactory.setCacheSelector(&gCacheSelector);
+        } else {
+            gFactory.setImageCache(&gLruImageCache);
+        }
+        gOnce = true;
+    }
+    return gFactory.installPixelRef(data, bitmap);
+}
index 9e49d30..e8909f9 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef PICTURE_RENDERING_FLAGS
 #define PICTURE_RENDERING_FLAGS
 
-#include "SkString.h"
+class SkString;
 
 namespace sk_tools {
     class PictureRenderer;
index d027db9..31ef484 100644 (file)
 #include "PictureBenchmark.h"
 #include "PictureRenderingFlags.h"
 #include "SkBenchLogger.h"
-#include "SkBitmapFactory.h"
-#include "SkCanvas.h"
 #include "SkFlags.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
+#if LAZY_CACHE_STATS
+    #include "SkLazyPixelRef.h"
+#endif
+#include "SkLruImageCache.h"
 #include "SkMath.h"
 #include "SkOSFile.h"
 #include "SkPicture.h"
 #include "SkStream.h"
-#include "SkTArray.h"
 #include "picture_utils.h"
 
 
@@ -142,20 +143,9 @@ static SkString filterFlagsUsage() {
     return result;
 }
 
-#include "SkData.h"
-#include "SkLruImageCache.h"
-#include "SkLazyPixelRef.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);
-}
+// These are defined in PictureRenderingFlags.cpp
+extern SkLruImageCache gLruImageCache;
+extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
 
 #if LAZY_CACHE_STATS
 static int32_t gTotalCacheHits;
index b289d39..5e0ac99 100644 (file)
@@ -7,8 +7,6 @@
 
 #include "CopyTilesRenderer.h"
 #include "SkBitmap.h"
-#include "SkBitmapFactory.h"
-#include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkFlags.h"
 #include "SkGraphics.h"
@@ -19,7 +17,6 @@
 #include "SkPicture.h"
 #include "SkStream.h"
 #include "SkString.h"
-#include "SkTArray.h"
 #include "PictureRenderer.h"
 #include "PictureRenderingFlags.h"
 #include "picture_utils.h"
@@ -45,36 +42,8 @@ static void make_output_filepath(SkString* path, const SkString& dir,
     path->remove(path->size() - 4, 4);
 }
 
-#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);
-}
+// Defined in PictureRenderingFlags.cpp
+extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
 
 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
                            sk_tools::PictureRenderer& renderer,