'../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',
'type': 'static_library',
'include_dirs': [
'../include/effects',
+ '../src/core',
],
'sources': [
'../include/effects/Sk1DPathEffect.h',
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&);
// helper functions
virtual void* readFunctionPtr();
virtual void readPaint(SkPaint* paint);
- virtual SkRefCnt* readRefCntPtr();
virtual void readBitmap(SkBitmap* bitmap) = 0;
virtual SkTypeface* readTypeface() = 0;
// 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;
#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();
// 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) {
SkReader32 fReader;
void* fMemoryPtr;
- SkRefCnt** fRCArray;
- int fRCCount;
-
+ SkBitmapHeapReader* fBitmapStorage;
SkTypeface** fTFArray;
int fTFCount;
#include "SkRefCnt.h"
#include "SkBitmap.h"
+#include "SkBitmapHeap.h"
#include "SkPath.h"
#include "SkWriter32.h"
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;
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;
};
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);
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);
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>();
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);
--- /dev/null
+
+/*
+ * 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;
+}
--- /dev/null
+
+/*
+ * 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
paint->unflatten(*this);
}
-SkRefCnt* SkFlattenableReadBuffer::readRefCntPtr() {
- void* ptrStorage[] = { NULL };
- SkASSERT(sizeof(void*) == this->getArrayCount());
- this->readByteArray(*ptrStorage);
- return (SkRefCnt*)ptrStorage[0];
-}
-
///////////////////////////////////////////////////////////////////////////////
SkFlattenableWriteBuffer::SkFlattenableWriteBuffer() {
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);
}
#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;
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() {
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();
, fFactorySet(NULL)
, fNamedFactorySet(NULL)
, fWriter(minSize)
- , fRCSet(NULL)
+ , fBitmapHeap(NULL)
, fTFSet(NULL) {
}
, 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);
}
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);
return rec;
}
-SkRefCntSet* SkOrderedWriteBuffer::setRefCntRecorder(SkRefCntSet* rec) {
- SkRefCnt_SafeAssign(fRCSet, rec);
- return rec;
-}
-
SkRefCntSet* SkOrderedWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) {
SkRefCnt_SafeAssign(fTFSet, rec);
return rec;
///////////////////////////////////////////////////////////////////////////////
-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();
}
}
-void SkRefCntPlayback::setCount(int count) {
+void SkTypefacePlayback::setCount(int count) {
this->reset(NULL);
fCount = 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;
///////////////////////////////////////////////////////////////////////////////
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;
}
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);
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());
}
#include "SkChunkAlloc.h"
#include "SkBitmap.h"
+#include "SkBitmapHeap.h"
#include "SkOrderedReadBuffer.h"
#include "SkOrderedWriteBuffer.h"
#include "SkPicture.h"
///////////////////////////////////////////////////////////////////////////////
-class SkRefCntPlayback {
+class SkTypefacePlayback {
public:
- SkRefCntPlayback();
- virtual ~SkRefCntPlayback();
+ SkTypefacePlayback();
+ virtual ~SkTypefacePlayback();
int count() const { return fCount; }
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:
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) {
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
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
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.
SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*);
private:
- SkRefCntSet* fPixelRefSet;
+ SkBitmapHeap* fBitmapHeap;
SkRefCntSet* fTypefaceSet;
- SkRefCntPlayback* fPixelRefPlayback;
SkTypefacePlayback* fTypefacePlayback;
SkNamedFactorySet* fFactorySet;
};
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,
int index = element->index() - 1;
SkASSERT((unsigned)index < (unsigned)count);
element->unflatten(&array[index], fUnflattenProc,
- fController->getPixelRefPlayback(),
+ fController->getBitmapHeap(),
fController->getTypefacePlayback());
-
}
}
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();
}
(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;
};
}
// 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();
const SkBitmap& getBitmap(SkReader32& reader) {
int index = reader.readInt();
- return (*fBitmaps)[index - 1];
+ return (*fBitmaps)[index];
}
const SkMatrix* getMatrix(SkReader32& reader) {
int fPictureCount;
SkTypefacePlayback fTFPlayback;
- SkFactoryPlayback* fFactoryPlayback;
+ SkFactoryPlayback* fFactoryPlayback;
#ifdef SK_BUILD_FOR_ANDROID
SkMutex fDrawMutex;
#endif
};
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
fRestoreOffsetStack.setReserve(32);
fInitialSaveCount = kNoInitialSave;
+ fFlattenableHeap.setBitmapStorage(&fBitmapHeap);
fPathHeap = NULL; // lazy allocate
fFirstSavedLayerIndex = kNoSavedLayerIndex;
}
SkPictureRecord::~SkPictureRecord() {
- reset();
+ SkSafeUnref(fPathHeap);
+ fFlattenableHeap.setBitmapStorage(NULL);
+ fPictureRefs.unrefAll();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
-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) {
///////////////////////////////////////////////////////////////////////////////
-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;
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;
}
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);
#endif
private:
- SkChunkFlatController fHeap;
+ SkBitmapHeap fBitmapHeap;
+ SkChunkFlatController fFlattenableHeap;
- SkTDArray<BitmapIndexCacheEntry> fBitmapIndexCache;
- SkBitmapDictionary fBitmaps;
SkMatrixDictionary fMatrices;
SkPaintDictionary fPaints;
SkRegionDictionary fRegions;
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());