Refactor Bitmap Storage for SkPicture using SkPipe's design.

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
This commit is contained in:
djsollen@google.com 2012-08-07 19:49:41 +00:00
parent 44b67b2ed1
commit 21830d9009
19 changed files with 623 additions and 288 deletions

View 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',

View File

@ -5,6 +5,7 @@
'type': 'static_library',
'include_dirs': [
'../include/effects',
'../src/core',
],
'sources': [
'../include/effects/Sk1DPathEffect.h',

View 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&);

View 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;

View File

@ -11,13 +11,14 @@
#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;

View 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,15 +71,16 @@ 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;
SkBitmapHeap* fBitmapHeap;
SkRefCntSet* fTFSet;
typedef SkFlattenableWriteBuffer INHERITED;

View 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);

258
src/core/SkBitmapHeap.cpp Normal file
View File

@ -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;
}

248
src/core/SkBitmapHeap.h Normal file
View File

@ -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

View 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);
}

View File

@ -10,14 +10,23 @@
#include "SkStream.h"
#include "SkTypeface.h"
SkOrderedReadBuffer::SkOrderedReadBuffer() : INHERITED() {
fMemoryPtr = NULL;
SkOrderedReadBuffer::SkOrderedReadBuffer(const void* data, size_t size) {
fBitmapStorage = NULL;
fTFArray = NULL;
fTFCount = 0;
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,19 +163,15 @@ uint32_t SkOrderedReadBuffer::getArrayCount() {
return *(uint32_t*)fReader.peek();
}
SkRefCnt* SkOrderedReadBuffer::readRefCntPtr() {
if (fRCArray) {
const uint32_t index = fReader.readU32();
SkASSERT(index <= (unsigned)fRCCount);
return fRCArray[index - 1];
} else {
return INHERITED::readRefCntPtr();
}
}
void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
if (fBitmapStorage) {
const uint32_t index = fReader.readU32();
*bitmap = *fBitmapStorage->getBitmap(index);
fBitmapStorage->releaseRef(index);
} else {
bitmap->unflatten(*this);
}
}
SkTypeface* SkOrderedReadBuffer::readTypeface() {

View 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,18 +132,13 @@ bool SkOrderedWriteBuffer::writeToStream(SkWStream* stream) {
return fWriter.writeToStream(stream);
}
void SkOrderedWriteBuffer::writeRefCntPtr(SkRefCnt* refCnt) {
SkASSERT(!isCrossProcess());
if (NULL == fRCSet) {
INHERITED::writeRefCntPtr(refCnt);
} else {
this->write32(fRCSet->add(refCnt));
}
}
void SkOrderedWriteBuffer::writeBitmap(const SkBitmap& bitmap) {
if (fBitmapHeap) {
fWriter.write32(fBitmapHeap->insert(bitmap));
} else {
bitmap.flatten(*this);
}
}
void SkOrderedWriteBuffer::writeTypeface(SkTypeface* obj) {
if (NULL == obj || NULL == fTFSet) {
@ -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;

View File

@ -18,13 +18,13 @@
///////////////////////////////////////////////////////////////////////////////
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());
}

View 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.
* 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.
*/
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.
*/
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
@ -227,13 +213,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
@ -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 {
fTypefacePlayback.reset(fTypefaceSet);
}
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;
};

View 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();
record.fFlattenableHeap.setupPlaybacks();
fPathHeap = record.fPathHeap;
SkSafeRef(fPathHeap);
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();

View 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) {

View 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;

View 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;

View 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());