Refactor Bitmap Storage for SkPicture using SkPipe's design.
authordjsollen@google.com <djsollen@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 7 Aug 2012 19:49:41 +0000 (19:49 +0000)
committerdjsollen@google.com <djsollen@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 7 Aug 2012 19:49:41 +0000 (19:49 +0000)
Refactor Picture and Pipe bitmap storage into common data structure

Update SkFlattenable buffers to be more modular.

This CL is an effort to stage the conversion to named
parameters for all SkFlattenable commands. This particular
stage only does the following two things...

1. Move flattenable buffers from SkFlattenable.h into
   their own header.
2. Update and Add new read write methods for better clarity
   and convenience.

BUG=

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

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

19 files changed:
gyp/core.gyp
gyp/effects.gyp
include/core/SkBitmap.h
include/core/SkFlattenableBuffers.h
include/core/SkOrderedReadBuffer.h
include/core/SkOrderedWriteBuffer.h
src/core/SkBitmap.cpp
src/core/SkBitmapHeap.cpp [new file with mode: 0644]
src/core/SkBitmapHeap.h [new file with mode: 0644]
src/core/SkFlattenableBuffers.cpp
src/core/SkOrderedReadBuffer.cpp
src/core/SkOrderedWriteBuffer.cpp
src/core/SkPictureFlat.cpp
src/core/SkPictureFlat.h
src/core/SkPicturePlayback.cpp
src/core/SkPicturePlayback.h
src/core/SkPictureRecord.cpp
src/core/SkPictureRecord.h
tests/CanvasTest.cpp

index 6af2a57..49b2b85 100644 (file)
@@ -14,6 +14,8 @@
         '../src/core/SkAlphaRuns.cpp',
         '../src/core/SkAntiRun.h',
         '../src/core/SkBitmap.cpp',
+        '../src/core/SkBitmapHeap.cpp',
+        '../src/core/SkBitmapHeap.h',
         '../src/core/SkBitmapProcShader.cpp',
         '../src/core/SkBitmapProcShader.h',
         '../src/core/SkBitmapProcState.cpp',
index 0a9e8d7..86308b7 100644 (file)
@@ -5,6 +5,7 @@
       'type': 'static_library',
       'include_dirs': [
         '../include/effects',
+        '../src/core',
       ],
       'sources': [
         '../include/effects/Sk1DPathEffect.h',
index b4f86ea..eba3e0b 100644 (file)
@@ -548,6 +548,13 @@ public:
     bool extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator,
                       SkIPoint* offset) const;
 
+    /** The following two functions provide the means to both flatten and
+        unflatten the bitmap AND its pixels into the provided buffer.
+        It is recommended that you do not call these functions directly,
+        but instead call the write/readBitmap functions on the respective
+        buffers as they can optimize the recording process and avoid recording
+        duplicate bitmaps and pixelRefs.
+     */
     void flatten(SkFlattenableWriteBuffer&) const;
     void unflatten(SkFlattenableReadBuffer&);
 
index d87539e..6137724 100644 (file)
@@ -87,7 +87,6 @@ public:
     // helper functions
     virtual void* readFunctionPtr();
     virtual void readPaint(SkPaint* paint);
-    virtual SkRefCnt* readRefCntPtr();
 
     virtual void readBitmap(SkBitmap* bitmap) = 0;
     virtual SkTypeface* readTypeface() = 0;
@@ -147,7 +146,6 @@ public:
     // helper functions
     virtual void writeFunctionPtr(void* ptr);
     virtual void writePaint(const SkPaint& paint);
-    virtual void writeRefCntPtr(SkRefCnt* refCnt);
 
     virtual void writeBitmap(const SkBitmap& bitmap) = 0;
     virtual void writeTypeface(SkTypeface* typeface) = 0;
index 5651e7c..8462b7f 100644 (file)
 
 #include "SkRefCnt.h"
 #include "SkBitmap.h"
+#include "SkBitmapHeap.h"
 #include "SkFlattenableBuffers.h"
 #include "SkReader32.h"
 #include "SkPath.h"
 
 class SkOrderedReadBuffer : public SkFlattenableReadBuffer {
 public:
-    SkOrderedReadBuffer() : INHERITED() {}
+    SkOrderedReadBuffer();
     SkOrderedReadBuffer(const void* data, size_t size);
     SkOrderedReadBuffer(SkStream* stream);
     virtual ~SkOrderedReadBuffer();
@@ -63,14 +64,11 @@ public:
     // helpers to get info about arrays and binary data
     virtual uint32_t getArrayCount() SK_OVERRIDE;
 
-    virtual SkRefCnt* readRefCntPtr() SK_OVERRIDE;
-
     virtual void readBitmap(SkBitmap* bitmap) SK_OVERRIDE;
     virtual SkTypeface* readTypeface() SK_OVERRIDE;
 
-    void setRefCntArray(SkRefCnt* array[], int count) {
-        fRCArray = array;
-        fRCCount = count;
+    void setBitmapStorage(SkBitmapHeapReader* bitmapStorage) {
+        SkRefCnt_SafeAssign(fBitmapStorage, bitmapStorage);
     }
 
     void setTypefaceArray(SkTypeface* array[], int count) {
@@ -103,9 +101,7 @@ private:
     SkReader32 fReader;
     void* fMemoryPtr;
 
-    SkRefCnt** fRCArray;
-    int        fRCCount;
-
+    SkBitmapHeapReader* fBitmapStorage;
     SkTypeface** fTFArray;
     int        fTFCount;
 
index 8cd2d81..7be2a90 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "SkRefCnt.h"
 #include "SkBitmap.h"
+#include "SkBitmapHeap.h"
 #include "SkPath.h"
 #include "SkWriter32.h"
 
@@ -59,8 +60,6 @@ public:
     virtual void writePath(const SkPath& path) SK_OVERRIDE;
     virtual size_t writeStream(SkStream* stream, size_t length) SK_OVERRIDE;
 
-    virtual void writeRefCntPtr(SkRefCnt* refCnt) SK_OVERRIDE;
-
     virtual void writeBitmap(const SkBitmap& bitmap) SK_OVERRIDE;
     virtual void writeTypeface(SkTypeface* typeface) SK_OVERRIDE;
 
@@ -72,16 +71,17 @@ public:
     SkRefCntSet* getTypefaceRecorder() const { return fTFSet; }
     SkRefCntSet* setTypefaceRecorder(SkRefCntSet*);
 
-    SkRefCntSet* getRefCntRecorder() const { return fRCSet; }
-    SkRefCntSet* setRefCntRecorder(SkRefCntSet*);
+    void setBitmapHeap(SkBitmapHeap* bitmapHeap) {
+        SkRefCnt_SafeAssign(fBitmapHeap, bitmapHeap);
+    }
 
 private:
     SkFactorySet* fFactorySet;
     SkNamedFactorySet* fNamedFactorySet;
     SkWriter32 fWriter;
 
-    SkRefCntSet*    fRCSet;
-    SkRefCntSet*    fTFSet;
+    SkBitmapHeap* fBitmapHeap;
+    SkRefCntSet* fTFSet;
 
     typedef SkFlattenableWriteBuffer INHERITED;
 };
index 70060f2..e59c506 100644 (file)
@@ -1365,21 +1365,9 @@ bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
 
 enum {
     SERIALIZE_PIXELTYPE_NONE,
-    SERIALIZE_PIXELTYPE_REF_DATA,
-    SERIALIZE_PIXELTYPE_REF_PTR
+    SERIALIZE_PIXELTYPE_REF_DATA
 };
 
-/*
-    It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
-    we just have pixels, then we can only flatten the pixels, or write out an
-    empty bitmap.
-
-    With a pixelref, we still have the question of recognizing when two sitings
-    of the same pixelref are the same, and when they are different. Perhaps we
-    should look at the generationID and keep a record of that in some dictionary
-    associated with the buffer. SkGLTextureCache does this sort of thing to know
-    when to create a new texture.
-*/
 void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
     buffer.writeInt(fWidth);
     buffer.writeInt(fHeight);
@@ -1387,26 +1375,6 @@ void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
     buffer.writeInt(fConfig);
     buffer.writeBool(this->isOpaque());
 
-    /*  If we are called in this mode, then it is up to the caller to manage
-        the owner-counts on the pixelref, as we just record the ptr itself.
-    */
-    if (!buffer.persistBitmapPixels()) {
-        if (fPixelRef) {
-            buffer.writeInt(SERIALIZE_PIXELTYPE_REF_PTR);
-            buffer.writeUInt(fPixelRefOffset);
-            buffer.writeRefCntPtr(fPixelRef);
-            return;
-        } else {
-            // we ignore the non-persist request, since we don't have a ref
-            // ... or we could just write an empty bitmap...
-            // (true) will write an empty bitmap, (false) will flatten the pix
-            if (true) {
-                buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
-                return;
-            }
-        }
-    }
-
     if (fPixelRef) {
         if (fPixelRef->getFactory()) {
             buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
@@ -1434,12 +1402,6 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
 
     int reftype = buffer.readInt();
     switch (reftype) {
-        case SERIALIZE_PIXELTYPE_REF_PTR: {
-            size_t offset = buffer.readUInt();
-            SkPixelRef* pr = (SkPixelRef*)buffer.readRefCntPtr();
-            this->setPixelRef(pr, offset);
-            break;
-        }
         case SERIALIZE_PIXELTYPE_REF_DATA: {
             size_t offset = buffer.readUInt();
             SkPixelRef* pr = buffer.readFlattenableT<SkPixelRef>();
@@ -1472,7 +1434,7 @@ SkBitmap::RLEPixels::~RLEPixels() {
 void SkBitmap::validate() const {
     SkASSERT(fConfig < kConfigCount);
     SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
-    SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
+    SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag));
     SkASSERT(fPixelLockCount >= 0);
     SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
     SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
diff --git a/src/core/SkBitmapHeap.cpp b/src/core/SkBitmapHeap.cpp
new file mode 100644 (file)
index 0000000..7cff8ea
--- /dev/null
@@ -0,0 +1,258 @@
+
+/*
+ * 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 "SkBitmapHeap.h"
+
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkTSearch.h"
+
+SkBitmapHeapEntry::SkBitmapHeapEntry()
+    : fSlot(-1)
+    , fRefCount(0)
+    , fBytesAllocated(0)
+    , fMoreRecentlyUsed(NULL)
+    , fLessRecentlyUsed(NULL) {
+}
+
+SkBitmapHeapEntry::~SkBitmapHeapEntry() {
+    SkASSERT(0 == fRefCount);
+}
+
+void SkBitmapHeapEntry::addReferences(int count) {
+    if (0 == fRefCount) {
+        // If there are no current owners then the heap manager
+        // will be the only one able to modify it, so it does not
+        // need to be an atomic operation.
+        fRefCount = count;
+    } else {
+        sk_atomic_add(&fRefCount, count);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapHeap::SkBitmapHeap(int32_t preferredSize, int32_t ownerCount)
+    : INHERITED()
+    , fExternalStorage(NULL)
+    , fMostRecentlyUsed(NULL)
+    , fLeastRecentlyUsed(NULL)
+    , fPreferredCount(preferredSize)
+    , fOwnerCount(ownerCount)
+    , fBytesAllocated(0) {
+}
+
+SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize)
+    : INHERITED()
+    , fExternalStorage(storage)
+    , fMostRecentlyUsed(NULL)
+    , fLeastRecentlyUsed(NULL)
+    , fPreferredCount(preferredSize)
+    , fOwnerCount(IGNORE_OWNERS)
+    , fBytesAllocated(0) {
+}
+
+SkBitmapHeap::~SkBitmapHeap() {
+    fStorage.deleteAll();
+    SkSafeUnref(fExternalStorage);
+}
+
+SkTRefArray<SkBitmap>* SkBitmapHeap::extractBitmaps() const {
+    const int size = fStorage.count();
+    SkTRefArray<SkBitmap>* array = NULL;
+    if (size > 0) {
+        array = SkTRefArray<SkBitmap>::Create(size);
+        for (int i = 0; i < size; i++) {
+            // make a shallow copy of the bitmap
+            array->writableAt(i) = fStorage[i]->fBitmap;
+        }
+    }
+    return array;
+}
+
+// We just "used" the entry. Update our LRU accordingly
+void SkBitmapHeap::setMostRecentlyUsed(SkBitmapHeapEntry* entry) {
+    SkASSERT(entry != NULL);
+    if (entry == fMostRecentlyUsed) {
+        return;
+    }
+    // Remove info from its prior place, and make sure to cover the hole.
+    if (fLeastRecentlyUsed == entry) {
+        SkASSERT(entry->fMoreRecentlyUsed != NULL);
+        fLeastRecentlyUsed = entry->fMoreRecentlyUsed;
+    }
+    if (entry->fMoreRecentlyUsed != NULL) {
+        SkASSERT(fMostRecentlyUsed != entry);
+        entry->fMoreRecentlyUsed->fLessRecentlyUsed = entry->fLessRecentlyUsed;
+    }
+    if (entry->fLessRecentlyUsed != NULL) {
+        SkASSERT(fLeastRecentlyUsed != entry);
+        entry->fLessRecentlyUsed->fMoreRecentlyUsed = entry->fMoreRecentlyUsed;
+    }
+    entry->fMoreRecentlyUsed = NULL;
+    // Set up the head and tail pointers properly.
+    if (fMostRecentlyUsed != NULL) {
+        SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed);
+        fMostRecentlyUsed->fMoreRecentlyUsed = entry;
+        entry->fLessRecentlyUsed = fMostRecentlyUsed;
+    }
+    fMostRecentlyUsed = entry;
+    if (NULL == fLeastRecentlyUsed) {
+        fLeastRecentlyUsed = entry;
+    }
+}
+
+// iterate through our LRU cache and try to find an entry to evict
+SkBitmapHeapEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement) {
+    SkASSERT(fPreferredCount != UNLIMITED_SIZE);
+    SkASSERT(fStorage.count() >= fPreferredCount);
+
+    SkBitmapHeapEntry* iter = fLeastRecentlyUsed;
+    while (iter != NULL) {
+        if (iter->fRefCount > 0) {
+            // If the least recently used bitmap has not been unreferenced
+            // by its owner, then according to our LRU specifications a more
+            // recently used one can not have used all it's references yet either.
+            return NULL;
+        }
+        if (replacement.pixelRef() && replacement.pixelRef() == iter->fBitmap.pixelRef()) {
+            // Do not replace a bitmap with a new one using the same
+            // pixel ref. Instead look for a different one that will
+            // potentially free up more space.
+            iter = iter->fMoreRecentlyUsed;
+        } else {
+            return iter;
+        }
+    }
+    return NULL;
+}
+
+int SkBitmapHeap::findInLookupTable(const SkBitmap& bitmap, SkBitmapHeapEntry** entry) {
+    LookupEntry indexEntry;
+    indexEntry.fGenerationId = bitmap.getGenerationID();
+    indexEntry.fPixelOffset = bitmap.pixelRefOffset();
+    indexEntry.fWidth = bitmap.width();
+    indexEntry.fHeight = bitmap.height();
+    int index = SkTSearch<const LookupEntry>(fLookupTable.begin(),
+                                                  fLookupTable.count(),
+                                                  indexEntry, sizeof(indexEntry));
+
+    if (index < 0) {
+        // insert ourselves into the bitmapIndex
+        index = ~index;
+        fLookupTable.insert(index, 1, &indexEntry);
+    } else if (entry != NULL) {
+        // populate the entry if needed
+        *entry = fStorage[fLookupTable[index].fStorageSlot];
+    }
+
+    return index;
+}
+
+bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap) {
+    SkASSERT(!fExternalStorage);
+
+    // If the bitmap is mutable, we need to do a deep copy, since the
+    // caller may modify it afterwards.
+    if (originalBitmap.isImmutable()) {
+        copiedBitmap = originalBitmap;
+// TODO if we have the pixel ref in the heap we could pass it here to avoid a potential deep copy
+//    else if (sharedPixelRef != NULL) {
+//        copiedBitmap = orig;
+//        copiedBitmap.setPixelRef(sharedPixelRef, originalBitmap.pixelRefOffset());
+    } else if (originalBitmap.empty()) {
+        copiedBitmap.reset();
+    } else if (!originalBitmap.deepCopyTo(&copiedBitmap, originalBitmap.getConfig())) {
+        return false;
+    }
+    copiedBitmap.setImmutable();
+    return true;
+}
+
+int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
+    SkBitmapHeapEntry* entry = NULL;
+    int searchIndex = this->findInLookupTable(originalBitmap, &entry);
+
+    // check to see if we already had a copy of the bitmap in the heap
+    if (entry) {
+        if (fOwnerCount != IGNORE_OWNERS) {
+            entry->addReferences(fOwnerCount);
+        }
+        if (fPreferredCount != UNLIMITED_SIZE) {
+            this->setMostRecentlyUsed(entry);
+        }
+        return entry->fSlot;
+    }
+
+    // decide if we need to evict an existing heap entry or create a new one
+    if (fPreferredCount != UNLIMITED_SIZE && fStorage.count() >= fPreferredCount) {
+        // iterate through our LRU cache and try to find an entry to evict
+        entry = this->findEntryToReplace(originalBitmap);
+        // we found an entry to evict
+        if (entry) {
+            // remove the bitmap index for the deleted entry
+            SkDEBUGCODE(int count = fLookupTable.count();)
+            int index = findInLookupTable(entry->fBitmap, NULL);
+            SkASSERT(count == fLookupTable.count());
+
+            fLookupTable.remove(index);
+            fBytesAllocated -= entry->fBytesAllocated;
+
+            // update the current search index now that we have removed one
+            if (index < searchIndex) {
+                searchIndex--;
+            }
+        }
+    }
+
+    // if we didn't have an entry yet we need to create one
+    if (!entry) {
+        entry = SkNEW(SkBitmapHeapEntry);
+        fStorage.append(1, &entry);
+        entry->fSlot = fStorage.count() - 1;
+        fBytesAllocated += sizeof(SkBitmapHeapEntry);
+    }
+
+    // create a copy of the bitmap
+    bool copySucceeded;
+    if (fExternalStorage) {
+        copySucceeded = fExternalStorage->insert(originalBitmap, entry->fSlot);
+    } else {
+        copySucceeded = copyBitmap(originalBitmap, entry->fBitmap);
+    }
+
+    // if the copy failed then we must abort
+    if (!copySucceeded) {
+        // delete the index
+        fLookupTable.remove(searchIndex);
+        // free the slot
+        fStorage.remove(entry->fSlot);
+        SkDELETE(entry);
+        return INVALID_SLOT;
+    }
+
+    // update the index with the appropriate slot in the heap
+    fLookupTable[searchIndex].fStorageSlot = entry->fSlot;
+
+    // compute the space taken by the this entry
+    // TODO if there is a shared pixel ref don't count it
+    // If the SkBitmap does not share an SkPixelRef with an SkBitmap already
+    // in the SharedHeap, also include the size of its pixels.
+    entry->fBytesAllocated += originalBitmap.getSize();
+
+    // add the bytes from this entry to the total count
+    fBytesAllocated += entry->fBytesAllocated;
+
+    if (fOwnerCount != IGNORE_OWNERS) {
+        entry->addReferences(fOwnerCount);
+    }
+    if (fPreferredCount != UNLIMITED_SIZE) {
+        this->setMostRecentlyUsed(entry);
+    }
+    return entry->fSlot;
+}
diff --git a/src/core/SkBitmapHeap.h b/src/core/SkBitmapHeap.h
new file mode 100644 (file)
index 0000000..bcfd039
--- /dev/null
@@ -0,0 +1,248 @@
+
+/*
+ * 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 SkBitmapHeap_DEFINED
+#define SkBitmapHeap_DEFINED
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+#include "SkThread.h"
+#include "SkTRefArray.h"
+
+/**
+ * SkBitmapHeapEntry provides users of SkBitmapHeap (using internal storage) with a means to...
+ *  (1) get access a bitmap in the heap
+ *  (2) indicate they are done with bitmap by releasing their reference (if they were an owner).
+ */
+class SkBitmapHeapEntry : SkNoncopyable {
+public:
+    ~SkBitmapHeapEntry();
+
+    int32_t getSlot() { return fSlot; }
+
+    SkBitmap* getBitmap() { return &fBitmap; }
+
+    void releaseRef() {
+        sk_atomic_dec(&fRefCount);
+    }
+
+private:
+    SkBitmapHeapEntry();
+
+    void addReferences(int count);
+
+    int32_t fSlot;
+    int32_t fRefCount;
+
+    SkBitmap fBitmap;
+    // Keep track of the bytes allocated for this bitmap. When replacing the
+    // bitmap or removing this HeapEntry we know how much memory has been
+    // reclaimed.
+    size_t fBytesAllocated;
+    // TODO: Generalize the LRU caching mechanism
+    SkBitmapHeapEntry* fMoreRecentlyUsed;
+    SkBitmapHeapEntry* fLessRecentlyUsed;
+
+    friend class SkBitmapHeap;
+};
+
+
+class SkBitmapHeapReader : public SkRefCnt {
+public:
+    SkBitmapHeapReader() : INHERITED() {}
+    virtual SkBitmap* getBitmap(int32_t slot) const = 0;
+    virtual void releaseRef(int32_t slot) = 0;
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+
+/**
+ * TODO: stores immutable bitmaps into a heap
+ */
+class SkBitmapHeap : public SkBitmapHeapReader {
+public:
+    class ExternalStorage : public SkRefCnt {
+     public:
+        virtual bool insert(const SkBitmap& bitmap, int32_t slot) = 0;
+    };
+
+    static const int32_t UNLIMITED_SIZE = -1;
+    static const int32_t IGNORE_OWNERS  = -1;
+    static const int32_t INVALID_SLOT   = -1;
+
+    /**
+     * Constructs a heap that is responsible for allocating and managing its own storage.  In the
+     * case where we choose to allow the heap to grow indefinitely (i.e. UNLIMITED_SIZE) we
+     * guarantee that once allocated in the heap a bitmap's index in the heap is immutable.
+     * Otherwise we guarantee the bitmaps placement in the heap until its owner count goes to zero.
+     *
+     * @param preferredSize  Specifies the preferred maximum number of bitmaps to store. This is
+     *   not a hard limit as it can grow larger if the number of bitmaps in the heap with active
+     *   owners exceeds this limit.
+     * @param ownerCount  The number of owners to assign to each inserted bitmap. NOTE: while a
+     *   bitmap in the heap has a least one owner it can't be removed.
+     */
+    SkBitmapHeap(int32_t preferredSize = UNLIMITED_SIZE, int32_t ownerCount = IGNORE_OWNERS);
+
+    /**
+     * Constructs a heap that defers the responsibility of storing the bitmaps to an external
+     * function. This is especially useful if the bitmaps will be used in a separate process as the
+     * external storage can ensure the data is properly shuttled to the appropriate processes.
+     *
+     * Our LRU implementation assumes that inserts into the external storage are consumed in the
+     * order that they are inserted (i.e. SkPipe). This ensures that we don't need to query the
+     * external storage to see if a slot in the heap is eligible to be overwritten.
+     *
+     * @param externalStorage  The class responsible for storing the bitmaps inserted into the heap
+     * @param heapSize  The maximum size of the heap. Because of the sequential limitation imposed
+     *   by our LRU implementation we can guarantee that the heap will never grow beyond this size.
+     */
+    SkBitmapHeap(ExternalStorage* externalStorage, int32_t heapSize = UNLIMITED_SIZE);
+
+    ~SkBitmapHeap();
+
+    /**
+     * Makes a shallow copy of all bitmaps currently in the heap and returns them as an array. The
+     * array indices match their position in the heap.
+     *
+     * @return  a ptr to an array of bitmaps or NULL if external storage is being used.
+     */
+    SkTRefArray<SkBitmap>* extractBitmaps() const;
+
+    /**
+     * Retrieves the bitmap from the specified slot in the heap
+     *
+     * @return  The bitmap located at that slot or NULL if external storage is being used.
+     */
+    virtual SkBitmap* getBitmap(int32_t slot) const SK_OVERRIDE {
+        SkASSERT(fExternalStorage == NULL);
+        SkBitmapHeapEntry* entry = getEntry(slot);
+        if (entry) {
+            return &entry->fBitmap;
+        }
+        return NULL;
+    }
+
+    /**
+     * Retrieves the bitmap from the specified slot in the heap
+     *
+     * @return  The bitmap located at that slot or NULL if external storage is being used.
+     */
+    virtual void releaseRef(int32_t slot) SK_OVERRIDE {
+        SkASSERT(fExternalStorage == NULL);
+        if (fOwnerCount != IGNORE_OWNERS) {
+            SkBitmapHeapEntry* entry = getEntry(slot);
+            if (entry) {
+                entry->releaseRef();
+            }
+        }
+    }
+
+    /**
+     * Inserts a bitmap into the heap. The stored version of bitmap is guaranteed to be immutable
+     * and is not dependent on the lifecycle of the provided bitmap.
+     *
+     * @param bitmap  the bitmap to be inserted into the heap
+     * @return  the slot in the heap where the bitmap is stored or INVALID_SLOT if the bitmap could
+     *          not be added to the heap. If it was added the slot will remain valid...
+     *            (1) indefinitely if no owner count has been specified.
+     *            (2) until all owners have called releaseRef on the appropriate SkBitmapHeapEntry*
+     */
+    int32_t insert(const SkBitmap& bitmap);
+
+    /**
+     * Retrieves an entry from the heap at a given slot.
+     *
+     * @param slot  the slot in the heap where a bitmap was stored.
+     * @return  a SkBitmapHeapEntry that wraps the bitmap or NULL if external storage is used.
+     */
+    SkBitmapHeapEntry* getEntry(int32_t slot) const {
+        SkASSERT(slot <= fStorage.count());
+        if (fExternalStorage != NULL) {
+            return NULL;
+        }
+        return fStorage[slot];
+    }
+
+    /**
+     * Returns a count of the number of items currently in the heap
+     */
+    int count() const {
+        SkASSERT(fExternalStorage != NULL || fStorage.count() == fLookupTable.count());
+        return fLookupTable.count();
+    }
+
+    /**
+     * Returns the total number of bytes allocated by the bitmaps in the heap
+     */
+    size_t bytesAllocated() const {
+        return fBytesAllocated;
+    }
+
+private:
+    struct LookupEntry {
+        uint32_t fGenerationId; // SkPixelRef GenerationID.
+        size_t fPixelOffset;
+        uint32_t fWidth;
+        uint32_t fHeight;
+
+        uint32_t fStorageSlot; // slot of corresponding bitmap in fStorage.
+
+        bool operator < (const LookupEntry& other) const {
+            if (this->fGenerationId != other.fGenerationId) {
+                return this->fGenerationId < other.fGenerationId;
+            } else if(this->fPixelOffset != other.fPixelOffset) {
+                return this->fPixelOffset < other.fPixelOffset;
+            } else if(this->fWidth != other.fWidth) {
+                return this->fWidth < other.fWidth;
+            } else {
+                return this->fHeight < other.fHeight;
+            }
+        }
+        bool operator != (const LookupEntry& other) const {
+            return this->fGenerationId != other.fGenerationId
+                || this->fPixelOffset != other.fPixelOffset
+                || this->fWidth != other.fWidth
+                || this->fHeight != other.fHeight;
+        }
+    };
+
+    /**
+     * Searches for the bitmap in the lookup table and returns the bitmaps index within the table.
+     * If the bitmap was not already in the table it is added.
+     *
+     * @param bitmap  The bitmap we using as a key to search the lookup table
+     * @param entry  A pointer to a SkBitmapHeapEntry* that if non-null AND the bitmap is found
+     *               in the lookup table is populated with the entry from the heap storage.
+     */
+    int findInLookupTable(const SkBitmap& bitmap, SkBitmapHeapEntry** entry);
+
+    SkBitmapHeapEntry* findEntryToReplace(const SkBitmap& replacement);
+    bool copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap);
+    void setMostRecentlyUsed(SkBitmapHeapEntry* entry);
+
+    // searchable index that maps to entries in the heap
+    SkTDArray<LookupEntry> fLookupTable;
+
+    // heap storage
+    SkTDArray<SkBitmapHeapEntry*> fStorage;
+    ExternalStorage* fExternalStorage;
+
+    SkBitmapHeapEntry* fMostRecentlyUsed;
+    SkBitmapHeapEntry* fLeastRecentlyUsed;
+
+    const int32_t fPreferredCount;
+    const int32_t fOwnerCount;
+    size_t fBytesAllocated;
+
+    typedef SkBitmapHeapReader INHERITED;
+};
+
+#endif // SkBitmapHeap_DEFINED
index e5a9545..50a47d5 100644 (file)
@@ -34,13 +34,6 @@ void SkFlattenableReadBuffer::readPaint(SkPaint* paint) {
     paint->unflatten(*this);
 }
 
-SkRefCnt* SkFlattenableReadBuffer::readRefCntPtr() {
-    void* ptrStorage[] = { NULL };
-    SkASSERT(sizeof(void*) == this->getArrayCount());
-    this->readByteArray(*ptrStorage);
-    return (SkRefCnt*)ptrStorage[0];
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 SkFlattenableWriteBuffer::SkFlattenableWriteBuffer() {
@@ -58,11 +51,6 @@ void SkFlattenableWriteBuffer::writePaint(const SkPaint& paint) {
     paint.flatten(*this);
 }
 
-void SkFlattenableWriteBuffer::writeRefCntPtr(SkRefCnt* refCnt) {
-    void* ptrStorage[] = { (void*)refCnt };
-    this->writeByteArray(ptrStorage, sizeof(void*));
-}
-
 void SkFlattenableWriteBuffer::flattenObject(SkFlattenable* obj, SkFlattenableWriteBuffer& buffer) {
     obj->flatten(buffer);
 }
index a502c5d..5835c34 100644 (file)
 #include "SkStream.h"
 #include "SkTypeface.h"
 
+SkOrderedReadBuffer::SkOrderedReadBuffer() : INHERITED() {
+    fMemoryPtr = NULL;
+
+    fBitmapStorage = NULL;
+    fTFArray = NULL;
+    fTFCount = 0;
 
-SkOrderedReadBuffer::SkOrderedReadBuffer(const void* data, size_t size) {
+    fFactoryTDArray = NULL;
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+}
+
+SkOrderedReadBuffer::SkOrderedReadBuffer(const void* data, size_t size) : INHERITED()  {
     fReader.setMemory(data, size);
     fMemoryPtr = NULL;
 
-    fRCArray = NULL;
-    fRCCount = 0;
-
+    fBitmapStorage = NULL;
     fTFArray = NULL;
     fTFCount = 0;
 
@@ -31,10 +40,19 @@ SkOrderedReadBuffer::SkOrderedReadBuffer(SkStream* stream) {
     fMemoryPtr = sk_malloc_throw(length);
     stream->read(fMemoryPtr, length);
     fReader.setMemory(fMemoryPtr, length);
+
+    fBitmapStorage = NULL;
+    fTFArray = NULL;
+    fTFCount = 0;
+
+    fFactoryTDArray = NULL;
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
 }
 
 SkOrderedReadBuffer::~SkOrderedReadBuffer() {
     sk_free(fMemoryPtr);
+    SkSafeUnref(fBitmapStorage);
 }
 
 bool SkOrderedReadBuffer::readBool() {
@@ -145,20 +163,16 @@ uint32_t SkOrderedReadBuffer::getArrayCount() {
     return *(uint32_t*)fReader.peek();
 }
 
-SkRefCnt* SkOrderedReadBuffer::readRefCntPtr() {
-    if (fRCArray) {
+void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
+    if (fBitmapStorage) {
         const uint32_t index = fReader.readU32();
-        SkASSERT(index <= (unsigned)fRCCount);
-        return fRCArray[index - 1];
+        *bitmap = *fBitmapStorage->getBitmap(index);
+        fBitmapStorage->releaseRef(index);
     } else {
-        return INHERITED::readRefCntPtr();
+        bitmap->unflatten(*this);
     }
 }
 
-void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
-    bitmap->unflatten(*this);
-}
-
 SkTypeface* SkOrderedReadBuffer::readTypeface() {
 
     uint32_t index = fReader.readU32();
index 9dd70fb..f7d6dbb 100644 (file)
@@ -14,7 +14,7 @@ SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize)
     , fFactorySet(NULL)
     , fNamedFactorySet(NULL)
     , fWriter(minSize)
-    , fRCSet(NULL)
+    , fBitmapHeap(NULL)
     , fTFSet(NULL) {
 }
 
@@ -23,14 +23,14 @@ SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize, void* storage, size_t
     , fFactorySet(NULL)
     , fNamedFactorySet(NULL)
     , fWriter(minSize, storage, storageSize)
-    , fRCSet(NULL)
+    , fBitmapHeap(NULL)
     , fTFSet(NULL) {
 }
 
 SkOrderedWriteBuffer::~SkOrderedWriteBuffer() {
     SkSafeUnref(fFactorySet);
     SkSafeUnref(fNamedFactorySet);
-    SkSafeUnref(fRCSet);
+    SkSafeUnref(fBitmapHeap);
     SkSafeUnref(fTFSet);
 }
 
@@ -132,19 +132,14 @@ bool SkOrderedWriteBuffer::writeToStream(SkWStream* stream) {
     return fWriter.writeToStream(stream);
 }
 
-void SkOrderedWriteBuffer::writeRefCntPtr(SkRefCnt* refCnt) {
-    SkASSERT(!isCrossProcess());
-    if (NULL == fRCSet) {
-        INHERITED::writeRefCntPtr(refCnt);
+void SkOrderedWriteBuffer::writeBitmap(const SkBitmap& bitmap) {
+    if (fBitmapHeap) {
+        fWriter.write32(fBitmapHeap->insert(bitmap));
     } else {
-        this->write32(fRCSet->add(refCnt));
+        bitmap.flatten(*this);
     }
 }
 
-void SkOrderedWriteBuffer::writeBitmap(const SkBitmap& bitmap) {
-    bitmap.flatten(*this);
-}
-
 void SkOrderedWriteBuffer::writeTypeface(SkTypeface* obj) {
     if (NULL == obj || NULL == fTFSet) {
         fWriter.write32(0);
@@ -171,11 +166,6 @@ SkNamedFactorySet* SkOrderedWriteBuffer::setNamedFactoryRecorder(SkNamedFactoryS
     return rec;
 }
 
-SkRefCntSet* SkOrderedWriteBuffer::setRefCntRecorder(SkRefCntSet* rec) {
-    SkRefCnt_SafeAssign(fRCSet, rec);
-    return rec;
-}
-
 SkRefCntSet* SkOrderedWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) {
     SkRefCnt_SafeAssign(fTFSet, rec);
     return rec;
index a593e36..bd44192 100644 (file)
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {}
+SkTypefacePlayback::SkTypefacePlayback() : fCount(0), fArray(NULL) {}
 
-SkRefCntPlayback::~SkRefCntPlayback() {
+SkTypefacePlayback::~SkTypefacePlayback() {
     this->reset(NULL);
 }
 
-void SkRefCntPlayback::reset(const SkRefCntSet* rec) {
+void SkTypefacePlayback::reset(const SkRefCntSet* rec) {
     for (int i = 0; i < fCount; i++) {
         SkASSERT(fArray[i]);
         fArray[i]->unref();
@@ -44,7 +44,7 @@ void SkRefCntPlayback::reset(const SkRefCntSet* rec) {
     }
 }
 
-void SkRefCntPlayback::setCount(int count) {
+void SkTypefacePlayback::setCount(int count) {
     this->reset(NULL);
     
     fCount = count;
@@ -52,7 +52,7 @@ void SkRefCntPlayback::setCount(int count) {
     sk_bzero(fArray, count * sizeof(SkRefCnt*));
 }
 
-SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
+SkRefCnt* SkTypefacePlayback::set(int index, SkRefCnt* obj) {
     SkASSERT((unsigned)index < (unsigned)fCount);
     SkRefCnt_SafeAssign(fArray[index], obj);
     return obj;
@@ -61,30 +61,25 @@ SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
 ///////////////////////////////////////////////////////////////////////////////
 
 SkFlatController::SkFlatController()
-: fPixelRefSet(NULL)
+: fBitmapHeap(NULL)
 , fTypefaceSet(NULL)
-, fPixelRefPlayback(NULL)
 , fTypefacePlayback(NULL)
 , fFactorySet(NULL) {}
 
 SkFlatController::~SkFlatController() {
-    SkSafeUnref(fPixelRefSet);
+    SkSafeUnref(fBitmapHeap);
     SkSafeUnref(fTypefaceSet);
     SkSafeUnref(fFactorySet);
 }
 
-void SkFlatController::setPixelRefSet(SkRefCntSet *set) {
-    SkRefCnt_SafeAssign(fPixelRefSet, set);
+void SkFlatController::setBitmapHeap(SkBitmapHeap* heap) {
+    SkRefCnt_SafeAssign(fBitmapHeap, heap);
 }
 
 void SkFlatController::setTypefaceSet(SkRefCntSet *set) {
     SkRefCnt_SafeAssign(fTypefaceSet, set);
 }
 
-void SkFlatController::setPixelRefPlayback(SkRefCntPlayback* playback) {
-    fPixelRefPlayback = playback;
-}
-
 void SkFlatController::setTypefacePlayback(SkTypefacePlayback* playback) {
     fTypefacePlayback = playback;
 }
@@ -104,7 +99,7 @@ SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
     intptr_t storage[256];
     SkOrderedWriteBuffer buffer(256, storage, sizeof(storage));
 
-    buffer.setRefCntRecorder(controller->getPixelRefSet());
+    buffer.setBitmapHeap(controller->getBitmapHeap());
     buffer.setTypefaceRecorder(controller->getTypefaceSet());
     buffer.setNamedFactoryRecorder(controller->getNamedFactorySet());
     buffer.setFlags(writeBufferflags);
@@ -134,16 +129,18 @@ SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
 
 void SkFlatData::unflatten(void* result,
         void (*unflattenProc)(SkOrderedReadBuffer&, void*),
-        SkRefCntPlayback* refCntPlayback,
+        SkBitmapHeap* bitmapHeap,
         SkTypefacePlayback* facePlayback) const {
 
     SkOrderedReadBuffer buffer(this->data(), fFlatSize);
-    if (refCntPlayback) {
-        refCntPlayback->setupBuffer(buffer);
+
+    if (bitmapHeap) {
+        buffer.setBitmapStorage(bitmapHeap);
     }
     if (facePlayback) {
         facePlayback->setupBuffer(buffer);
     }
+
     unflattenProc(buffer, result);
     SkASSERT(fFlatSize == (int32_t)buffer.offset());
 }
index c4605aa..0f846fd 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "SkChunkAlloc.h"
 #include "SkBitmap.h"
+#include "SkBitmapHeap.h"
 #include "SkOrderedReadBuffer.h"
 #include "SkOrderedWriteBuffer.h"
 #include "SkPicture.h"
@@ -83,10 +84,10 @@ static inline bool ClipParams_unpackDoAA(uint32_t packed) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkRefCntPlayback {
+class SkTypefacePlayback {
 public:
-    SkRefCntPlayback();
-    virtual ~SkRefCntPlayback();
+    SkTypefacePlayback();
+    virtual ~SkTypefacePlayback();
     
     int count() const { return fCount; }
     
@@ -95,8 +96,8 @@ public:
     void setCount(int count);
     SkRefCnt* set(int index, SkRefCnt*);
 
-    virtual void setupBuffer(SkOrderedReadBuffer& buffer) const {
-        buffer.setRefCntArray(fArray, fCount);
+    void setupBuffer(SkOrderedReadBuffer& buffer) const {
+        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
     }
     
 protected:
@@ -104,13 +105,6 @@ protected:
     SkRefCnt** fArray;
 };
 
-class SkTypefacePlayback : public SkRefCntPlayback {
-public:
-    virtual void setupBuffer(SkOrderedReadBuffer& buffer) const {
-        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
-    }
-};
-
 class SkFactoryPlayback {
 public:
     SkFactoryPlayback(int count) : fCount(count) {
@@ -180,19 +174,12 @@ public:
     virtual void unalloc(void* ptr) = 0;
 
     /**
-     * Used during creation of SkFlatData objects. Only used for storing refs to
-     * SkPixelRefs. If the objects being flattened have SkPixelRefs (i.e.
-     * SkBitmaps or SkPaints, which may have SkBitmapShaders), this should be
-     * set by the protected setPixelRefSet.
-     */
-    SkRefCntSet* getPixelRefSet() { return fPixelRefSet; }
-
-    /**
-     * Used during unflattening of the SkFlatData objects in the
-     * SkFlatDictionary. Needs to be set by the protected setPixelRefPlayback
-     * and needs to be reset to the SkRefCntSet passed to setPixelRefSet.
+     * Used during creation and unflattening of SkFlatData objects. If the
+     * objects being flattened contain bitmaps they are stored in this heap
+     * and the flattenable stores the index to the bitmap on the heap.
+     * This should be set by the protected setBitmapHeap.
      */
-    SkRefCntPlayback* getPixelRefPlayback() { return fPixelRefPlayback; }
+    SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; }
 
     /**
      * Used during creation of SkFlatData objects. If a typeface recorder is
@@ -216,10 +203,9 @@ public:
 
 protected:
     /**
-     * Set an SkRefCntSet to be used to store SkPixelRefs during flattening. Ref
-     * counted.
+     * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted.
      */
-    void setPixelRefSet(SkRefCntSet*);
+    void setBitmapHeap(SkBitmapHeap*);
 
     /**
      * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref
@@ -228,13 +214,6 @@ protected:
     void setTypefaceSet(SkRefCntSet*);
 
     /**
-     * Set an SkRefCntPlayback to be used to find references to SkPixelRefs
-     * during unflattening. Should be reset to the set provided to
-     * setPixelRefSet.
-     */
-    void setPixelRefPlayback(SkRefCntPlayback*);
-
-    /**
      * Set an SkTypefacePlayback to be used to find references to SkTypefaces
      * during unflattening. Should be reset to the set provided to
      * setTypefaceSet.
@@ -249,9 +228,8 @@ protected:
     SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*);
 
 private:
-    SkRefCntSet*        fPixelRefSet;
+    SkBitmapHeap*       fBitmapHeap;
     SkRefCntSet*        fTypefaceSet;
-    SkRefCntPlayback*   fPixelRefPlayback;
     SkTypefacePlayback* fTypefacePlayback;
     SkNamedFactorySet*  fFactorySet;
 };
@@ -321,7 +299,7 @@ public:
 
     void unflatten(void* result,
                    void (*unflattenProc)(SkOrderedReadBuffer&, void*),
-                   SkRefCntPlayback* refCntPlayback = NULL,
+                   SkBitmapHeap* bitmapHeap = NULL,
                    SkTypefacePlayback* facePlayback = NULL) const;
 
     // When we purge an entry, we want to reuse an old index for the new entry,
@@ -495,9 +473,8 @@ private:
             int index = element->index() - 1;
             SkASSERT((unsigned)index < (unsigned)count);
             element->unflatten(&array[index], fUnflattenProc,
-                               fController->getPixelRefPlayback(),
+                               fController->getBitmapHeap(),
                                fController->getTypefacePlayback());
-
         }
     }
 
@@ -581,16 +558,12 @@ class SkChunkFlatController : public SkFlatController {
 public:
     SkChunkFlatController(size_t minSize)
     : fHeap(minSize)
-    , fRefSet(SkNEW(SkRefCntSet))
     , fTypefaceSet(SkNEW(SkRefCntSet)) {
-        this->setPixelRefSet(fRefSet);
         this->setTypefaceSet(fTypefaceSet);
-        this->setPixelRefPlayback(&fRefPlayback);
         this->setTypefacePlayback(&fTypefacePlayback);
     }
 
     ~SkChunkFlatController() {
-        fRefSet->unref();
         fTypefaceSet->unref();
     }
 
@@ -602,24 +575,17 @@ public:
         (void) fHeap.unalloc(ptr);
     }
 
-    void reset() {
-        fHeap.reset();
-        fRefSet->reset();
-        fTypefaceSet->reset();
-        fRefPlayback.reset(NULL);
-        fTypefacePlayback.reset(NULL);
-    }
-
     void setupPlaybacks() const {
-        fRefPlayback.reset(fRefSet);
         fTypefacePlayback.reset(fTypefaceSet);
     }
 
+    void setBitmapStorage(SkBitmapHeap* heap) {
+        this->setBitmapHeap(heap);
+    }
+
 private:
     SkChunkAlloc               fHeap;
-    SkRefCntSet*               fRefSet;
     SkRefCntSet*               fTypefaceSet;
-    mutable SkRefCntPlayback   fRefPlayback;
     mutable SkTypefacePlayback fTypefacePlayback;
 };
 
index f05aeaa..8ed5fe0 100644 (file)
@@ -78,15 +78,14 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) {
     }
 
     // copy over the refcnt dictionary to our reader
-    //
-    record.fHeap.setupPlaybacks();
-    fBitmaps = record.getBitmaps().unflattenToArray();
-    fMatrices = record.getMatrices().unflattenToArray();
-    fPaints = record.getPaints().unflattenToArray();
-    fRegions = record.getRegions().unflattenToArray();
-
-    fPathHeap = record.fPathHeap;
-    SkSafeRef(fPathHeap);
+    record.fFlattenableHeap.setupPlaybacks();
+
+    fBitmaps = record.fBitmapHeap.extractBitmaps();
+    fMatrices = record.fMatrices.unflattenToArray();
+    fPaints = record.fPaints.unflattenToArray();
+    fRegions = record.fRegions.unflattenToArray();
+
+    SkRefCnt_SafeAssign(fPathHeap, record.fPathHeap);
 
     const SkTDArray<SkPicture* >& pictures = record.getPictureRefs();
     fPictureCount = pictures.count();
index 2110c71..9cc2fd3 100644 (file)
@@ -72,7 +72,7 @@ private:
 
     const SkBitmap& getBitmap(SkReader32& reader) {
         int index = reader.readInt();
-        return (*fBitmaps)[index - 1];
+        return (*fBitmaps)[index];
     }
 
     const SkMatrix* getMatrix(SkReader32& reader) {
@@ -179,7 +179,7 @@ private:
     int fPictureCount;
 
     SkTypefacePlayback fTFPlayback;
-    SkFactoryPlayback*   fFactoryPlayback;
+    SkFactoryPlayback* fFactoryPlayback;
 #ifdef SK_BUILD_FOR_ANDROID
     SkMutex fDrawMutex;
 #endif
index c3f3ad1..fb4f991 100644 (file)
@@ -17,11 +17,10 @@ enum {
 };
 
 SkPictureRecord::SkPictureRecord(uint32_t flags) :
-        fHeap(HEAP_BLOCK_SIZE),
-        fBitmaps(&fHeap),
-        fMatrices(&fHeap),
-        fPaints(&fHeap),
-        fRegions(&fHeap),
+        fFlattenableHeap(HEAP_BLOCK_SIZE),
+        fMatrices(&fFlattenableHeap),
+        fPaints(&fFlattenableHeap),
+        fRegions(&fFlattenableHeap),
         fWriter(MIN_WRITER_SIZE),
         fRecordFlags(flags) {
 #ifdef SK_DEBUG_SIZE
@@ -32,12 +31,15 @@ SkPictureRecord::SkPictureRecord(uint32_t flags) :
     fRestoreOffsetStack.setReserve(32);
     fInitialSaveCount = kNoInitialSave;
 
+    fFlattenableHeap.setBitmapStorage(&fBitmapHeap);
     fPathHeap = NULL;   // lazy allocate
     fFirstSavedLayerIndex = kNoSavedLayerIndex;
 }
 
 SkPictureRecord::~SkPictureRecord() {
-    reset();
+    SkSafeUnref(fPathHeap);
+    fFlattenableHeap.setBitmapStorage(NULL);
+    fPictureRefs.unrefAll();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -518,25 +520,8 @@ void SkPictureRecord::drawData(const void* data, size_t length) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkPictureRecord::reset() {
-    SkSafeUnref(fPathHeap);
-    fPathHeap = NULL;
-
-    fBitmaps.reset();
-    fBitmapIndexCache.reset();
-    fMatrices.reset();
-    fPaints.reset();
-    fPictureRefs.unrefAll();
-    fRegions.reset();
-    fWriter.reset();
-    fHeap.reset();
-
-    fRestoreOffsetStack.setCount(1);
-    fRestoreOffsetStack.top() = 0;
-}
-
 void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
-    addInt(find(bitmap));
+    addInt(fBitmapHeap.insert(bitmap));
 }
 
 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
@@ -637,38 +622,6 @@ void SkPictureRecord::addText(const void* text, size_t byteLength) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-int SkPictureRecord::find(const SkBitmap& bitmap) {
-    int dictionaryIndex = 0;
-    BitmapIndexCacheEntry entry;
-    const bool flattenPixels = !bitmap.isImmutable();
-    if (flattenPixels) {
-        // Flattened bitmap may be very large. First attempt a fast lookup
-        // based on generation ID to avoid unnecessary flattening in
-        // fBitmaps.find()
-        entry.fGenerationId = bitmap.getGenerationID();
-        entry.fPixelOffset = bitmap.pixelRefOffset();
-        entry.fWidth = bitmap.width();
-        entry.fHeight = bitmap.height();
-        dictionaryIndex = 
-            SkTSearch<const BitmapIndexCacheEntry>(fBitmapIndexCache.begin(),
-                    fBitmapIndexCache.count(), entry, sizeof(entry));
-        if (dictionaryIndex >= 0) {
-            return fBitmapIndexCache[dictionaryIndex].fIndex;
-        }
-    }
-    
-    uint32_t writeFlags = flattenPixels ?
-        SkFlattenableWriteBuffer::kForceFlattenBitmapPixels_Flag : 0;
-    int index = fBitmaps.find(bitmap, writeFlags);
-
-    if (flattenPixels) {
-        entry.fIndex = index;
-        dictionaryIndex = ~dictionaryIndex;
-        *fBitmapIndexCache.insert(dictionaryIndex) = entry;
-    }
-    return index;
-}
-
 #ifdef SK_DEBUG_SIZE
 size_t SkPictureRecord::size() const {
     size_t result = 0;
index cd429da..50cf4db 100644 (file)
@@ -71,23 +71,10 @@ public:
 
     void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
 
-    const SkBitmapDictionary& getBitmaps() const {
-        return fBitmaps;
-    }
-    const SkMatrixDictionary& getMatrices() const {
-        return fMatrices;
-    }
-    const SkPaintDictionary& getPaints() const {
-        return fPaints;
-    }
     const SkTDArray<SkPicture* >& getPictureRefs() const {
         return fPictureRefs;
     }
-    const SkRegionDictionary& getRegions() const {
-        return fRegions;
-    }
 
-    void reset();
     void setFlags(uint32_t recordFlags) {
         fRecordFlags = recordFlags;
     }
@@ -98,31 +85,6 @@ public:
 
     void endRecording();
 private:
-    struct BitmapIndexCacheEntry {
-        uint32_t fGenerationId; // SkPixelRef GenerationID.
-        size_t fPixelOffset;
-        uint32_t fWidth;
-        uint32_t fHeight;
-        uint32_t fIndex; // Index of corresponding flattened bitmap in fBitmaps.
-        bool operator < (const BitmapIndexCacheEntry& other) const {
-            if (this->fGenerationId != other.fGenerationId) {
-                return this->fGenerationId < other.fGenerationId;
-            } else if(this->fPixelOffset != other.fPixelOffset) {
-                return this->fPixelOffset < other.fPixelOffset;
-            } else if(this->fWidth != other.fWidth) {
-                return this->fWidth < other.fWidth;
-            } else {
-                return this->fHeight < other.fHeight;
-            }
-        } 
-        bool operator != (const BitmapIndexCacheEntry& other) const {
-            return this->fGenerationId != other.fGenerationId
-                || this->fPixelOffset != other.fPixelOffset
-                || this->fWidth != other.fWidth
-                || this->fHeight != other.fHeight;
-        } 
-    };
-
     void recordRestoreOffsetPlaceholder(SkRegion::Op);
     void fillRestoreOffsetPlaceholdersForCurrentStackLevel(
         uint32_t restoreOffset);
@@ -199,10 +161,9 @@ public:
 #endif
 
 private:
-    SkChunkFlatController fHeap;
+    SkBitmapHeap fBitmapHeap;
+    SkChunkFlatController fFlattenableHeap;
 
-    SkTDArray<BitmapIndexCacheEntry> fBitmapIndexCache;
-    SkBitmapDictionary fBitmaps;
     SkMatrixDictionary fMatrices;
     SkPaintDictionary fPaints;
     SkRegionDictionary fRegions;
index 866bf0c..2e15ebb 100644 (file)
@@ -659,13 +659,8 @@ private:
         CanvasTestStep* testStep) {
 
         REPORTER_ASSERT_MESSAGE(reporter,
-            referenceRecord->fBitmaps.count() ==
-            testRecord->fBitmaps.count(), testStep->assertMessage());
-        for (int i = 0; i < referenceRecord->fBitmaps.count(); ++i) {
-            REPORTER_ASSERT_MESSAGE(reporter,
-                EQ(referenceRecord->fBitmaps[i], testRecord->fBitmaps[i]),
-                                    testStep->assertMessage());
-        }
+            referenceRecord->fBitmapHeap.count() ==
+            testRecord->fBitmapHeap.count(), testStep->assertMessage());
         REPORTER_ASSERT_MESSAGE(reporter,
             referenceRecord->fMatrices.count() ==
             testRecord->fMatrices.count(), testStep->assertMessage());