Index: src/core/SkPictureFlat.h |
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h |
index edfe24a4516dfe5e23d08be212e7c9bffad6ecee..d69b658efc834a1c3bd7619ab5a47aa997ade87d 100644 |
--- a/src/core/SkPictureFlat.h |
+++ b/src/core/SkPictureFlat.h |
@@ -163,397 +163,4 @@ private: |
SkFlattenable::Factory* fArray; |
}; |
-/////////////////////////////////////////////////////////////////////////////// |
-// |
-// |
-// The following templated classes provide an efficient way to store and compare |
-// objects that have been flattened (i.e. serialized in an ordered binary |
-// format). |
-// |
-// SkFlatData: is a simple indexable container for the flattened data |
-// which is agnostic to the type of data is is indexing. It is |
-// also responsible for flattening/unflattening objects but |
-// details of that operation are hidden in the provided traits |
-// SkFlatDictionary: is an abstract templated dictionary that maintains a |
-// searchable set of SkFlatData objects of type T. |
-// SkFlatController: is an interface provided to SkFlatDictionary which handles |
-// allocation (and unallocation in some cases). It also holds |
-// ref count recorders and the like. |
-// |
-// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary must subclass the |
-// dictionary and provide the necessary flattening traits. SkFlatController must also be |
-// implemented, or SkChunkFlatController can be used to use an SkChunkAllocator and never do |
-// replacements. |
-// |
-// |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-class SkFlatData; |
- |
-class SkFlatController : public SkRefCnt { |
-public: |
- |
- |
- SkFlatController(uint32_t writeBufferFlags = 0); |
- virtual ~SkFlatController(); |
- /** |
- * Return a new block of memory for the SkFlatDictionary to use. |
- * This memory is owned by the controller and has the same lifetime unless you |
- * call unalloc(), in which case it may be freed early. |
- */ |
- virtual void* allocThrow(size_t bytes) = 0; |
- |
- /** |
- * Hint that this block, which was allocated with allocThrow, is no longer needed. |
- * The implementation may choose to free this memory any time beteween now and destruction. |
- */ |
- virtual void unalloc(void* ptr) = 0; |
- |
- /** |
- * 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. |
- */ |
- SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } |
- |
- /** |
- * Used during creation of SkFlatData objects. If a typeface recorder is |
- * required to flatten the objects being flattened (i.e. for SkPaints), this |
- * should be set by the protected setTypefaceSet. |
- */ |
- SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } |
- |
- /** |
- * Used during unflattening of the SkFlatData objects in the |
- * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback |
- * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. |
- */ |
- SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } |
- |
- /** |
- * Flags to use during creation of SkFlatData objects. Defaults to zero. |
- */ |
- uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } |
- |
-protected: |
- /** |
- * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. |
- */ |
- void setBitmapHeap(SkBitmapHeap*); |
- |
- /** |
- * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref |
- * counted. |
- */ |
- void setTypefaceSet(SkRefCntSet*); |
- |
- /** |
- * Set an SkTypefacePlayback to be used to find references to SkTypefaces |
- * during unflattening. Should be reset to the set provided to |
- * setTypefaceSet. |
- */ |
- void setTypefacePlayback(SkTypefacePlayback*); |
- |
-private: |
- SkBitmapHeap* fBitmapHeap; |
- SkRefCntSet* fTypefaceSet; |
- SkTypefacePlayback* fTypefacePlayback; |
- const uint32_t fWriteBufferFlags; |
- |
- typedef SkRefCnt INHERITED; |
-}; |
- |
-class SkFlatData { |
-public: |
- // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*. |
- template <typename Traits, typename T> |
- static SkFlatData* Create(SkFlatController* controller, const T& obj, int index) { |
- // A buffer of 256 bytes should fit most paints, regions, and matrices. |
- uint32_t storage[64]; |
- SkWriteBuffer buffer(storage, sizeof(storage), controller->getWriteBufferFlags()); |
- |
- buffer.setBitmapHeap(controller->getBitmapHeap()); |
- buffer.setTypefaceRecorder(controller->getTypefaceSet()); |
- |
- Traits::Flatten(buffer, obj); |
- size_t size = buffer.bytesWritten(); |
- SkASSERT(SkIsAlign4(size)); |
- |
- // Allocate enough memory to hold SkFlatData struct and the flat data itself. |
- size_t allocSize = sizeof(SkFlatData) + size; |
- SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize); |
- |
- // Put the serialized contents into the data section of the new allocation. |
- buffer.writeToMemory(result->data()); |
- // Stamp the index, size and checksum in the header. |
- result->stampHeader(index, SkToS32(size)); |
- return result; |
- } |
- |
- // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given |
- template <typename Traits, typename T> |
- void unflatten(T* result, |
- SkBitmapHeap* bitmapHeap = nullptr, |
- SkTypefacePlayback* facePlayback = nullptr) const { |
- SkReadBuffer buffer(this->data(), fFlatSize); |
- |
- if (bitmapHeap) { |
- buffer.setBitmapStorage(bitmapHeap); |
- } |
- if (facePlayback) { |
- facePlayback->setupBuffer(buffer); |
- } |
- |
- Traits::Unflatten(buffer, result); |
- SkASSERT(fFlatSize == (int32_t)buffer.offset()); |
- } |
- |
- // Do these contain the same data? Ignores index() and topBot(). |
- bool operator==(const SkFlatData& that) const { |
- if (this->checksum() != that.checksum() || this->flatSize() != that.flatSize()) { |
- return false; |
- } |
- return memcmp(this->data(), that.data(), this->flatSize()) == 0; |
- } |
- |
- int index() const { return fIndex; } |
- const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); } |
- size_t flatSize() const { return fFlatSize; } |
- uint32_t checksum() const { return fChecksum; } |
- |
- // Returns true if fTopBot[] has been recorded. |
- bool isTopBotWritten() const { |
- return !SkScalarIsNaN(fTopBot[0]); |
- } |
- |
- // Returns fTopBot array, so it can be passed to a routine to compute them. |
- // For efficiency, we assert that fTopBot have not been recorded yet. |
- SkScalar* writableTopBot() const { |
- SkASSERT(!this->isTopBotWritten()); |
- return fTopBot; |
- } |
- |
- // Return the topbot[] after it has been recorded. |
- const SkScalar* topBot() const { |
- SkASSERT(this->isTopBotWritten()); |
- return fTopBot; |
- } |
- |
-private: |
- struct HashTraits { |
- static const SkFlatData& GetKey(const SkFlatData& flat) { return flat; } |
- static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } |
- }; |
- |
- void setIndex(int index) { fIndex = index; } |
- uint8_t* data() { return (uint8_t*)this + sizeof(*this); } |
- |
- // This assumes the payload flat data has already been written and does not modify it. |
- void stampHeader(int index, int32_t size) { |
- SkASSERT(SkIsAlign4(size)); |
- fIndex = index; |
- fFlatSize = size; |
- fTopBot[0] = SK_ScalarNaN; // Mark as unwritten. |
- fChecksum = SkChecksum::Murmur3(this->data(), size); |
- } |
- |
- int fIndex; |
- int32_t fFlatSize; |
- uint32_t fChecksum; |
- mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?]. |
- // uint32_t flattenedData[] implicitly hangs off the end. |
- |
- template <typename T, typename Traits> friend class SkFlatDictionary; |
-}; |
- |
-template <typename T, typename Traits> |
-class SkFlatDictionary { |
-public: |
- explicit SkFlatDictionary(SkFlatController* controller) |
- : fController(SkRef(controller)) |
- , fScratch(controller->getWriteBufferFlags()) |
- , fReady(false) { |
- this->reset(); |
- } |
- |
- /** |
- * Clears the dictionary of all entries. However, it does NOT free the |
- * memory that was allocated for each entry (that's owned by controller). |
- */ |
- void reset() { |
- fIndexedData.rewind(); |
- } |
- |
- int count() const { |
- SkASSERT(fHash.count() == fIndexedData.count()); |
- return fHash.count(); |
- } |
- |
- // For testing only. Index is zero-based. |
- const SkFlatData* operator[](int index) { |
- return fIndexedData[index]; |
- } |
- |
- /** |
- * Given an element of type T return its 1-based index in the dictionary. If |
- * the element wasn't previously in the dictionary it is automatically |
- * added. |
- * |
- */ |
- int find(const T& element) { |
- return this->findAndReturnFlat(element)->index(); |
- } |
- |
- /** |
- * Similar to find. Allows the caller to specify an SkFlatData to replace in |
- * the case of an add. Also tells the caller whether a new SkFlatData was |
- * added and whether the old one was replaced. The parameters added and |
- * replaced are required to be non-nullptr. Rather than returning the index of |
- * the entry in the dictionary, it returns the actual SkFlatData. |
- */ |
- const SkFlatData* findAndReplace(const T& element, |
- const SkFlatData* toReplace, |
- bool* added, |
- bool* replaced) { |
- SkASSERT(added != nullptr && replaced != nullptr); |
- |
- const int oldCount = this->count(); |
- SkFlatData* flat = this->findAndReturnMutableFlat(element); |
- *added = this->count() > oldCount; |
- |
- // If we don't want to replace anything, we're done. |
- if (!*added || toReplace == nullptr) { |
- *replaced = false; |
- return flat; |
- } |
- |
- // If we don't have the thing to replace, we're done. |
- const SkFlatData* found = fHash.find(*toReplace); |
- if (found == nullptr) { |
- *replaced = false; |
- return flat; |
- } |
- |
- // findAndReturnMutableFlat put flat at the back. Swap it into found->index() instead. |
- // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Watch out! |
- SkASSERT(flat->index() == this->count()); |
- flat->setIndex(found->index()); |
- fIndexedData.removeShuffle(found->index()-1); |
- SkASSERT(flat == fIndexedData[found->index()-1]); |
- |
- // findAndReturnMutableFlat already called fHash.add(), so we just clean up the old entry. |
- fHash.remove(*found); |
- fController->unalloc((void*)found); |
- SkASSERT(this->count() == oldCount); |
- |
- *replaced = true; |
- return flat; |
- } |
- |
- /** |
- * Unflatten the specific object at the given index. |
- * Caller takes ownership of the result. |
- */ |
- T* unflatten(int index) const { |
- // index is 1-based, while fIndexedData is 0-based. |
- const SkFlatData* element = fIndexedData[index-1]; |
- SkASSERT(index == element->index()); |
- |
- T* dst = new T; |
- this->unflatten(dst, element); |
- return dst; |
- } |
- |
- /** |
- * Find or insert a flattened version of element into the dictionary. |
- * Caller does not take ownership of the result. This will not return nullptr. |
- */ |
- const SkFlatData* findAndReturnFlat(const T& element) { |
- return this->findAndReturnMutableFlat(element); |
- } |
- |
-private: |
- // We have to delay fScratch's initialization until its first use; fController might not |
- // be fully set up by the time we get it in the constructor. |
- void lazyInit() { |
- if (fReady) { |
- return; |
- } |
- |
- // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. |
- SkASSERT(fController->getBitmapHeap() != nullptr); |
- fScratch.setBitmapHeap(fController->getBitmapHeap()); |
- fScratch.setTypefaceRecorder(fController->getTypefaceSet()); |
- fReady = true; |
- } |
- |
- // As findAndReturnFlat, but returns a mutable pointer for internal use. |
- SkFlatData* findAndReturnMutableFlat(const T& element) { |
- // Only valid until the next call to resetScratch(). |
- const SkFlatData& scratch = this->resetScratch(element, this->count()+1); |
- |
- SkFlatData* candidate = fHash.find(scratch); |
- if (candidate != nullptr) { |
- return candidate; |
- } |
- |
- SkFlatData* detached = this->detachScratch(); |
- fHash.add(detached); |
- *fIndexedData.append() = detached; |
- SkASSERT(fIndexedData.top()->index() == this->count()); |
- return detached; |
- } |
- |
- // This reference is valid only until the next call to resetScratch() or detachScratch(). |
- const SkFlatData& resetScratch(const T& element, int index) { |
- this->lazyInit(); |
- |
- // Layout of fScratch: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] |
- fScratch.reset(); |
- fScratch.reserve(sizeof(SkFlatData)); |
- Traits::Flatten(fScratch, element); |
- const size_t dataSize = fScratch.bytesWritten() - sizeof(SkFlatData); |
- |
- // Reinterpret data in fScratch as an SkFlatData. |
- SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); |
- SkASSERT(scratch != nullptr); |
- scratch->stampHeader(index, SkToS32(dataSize)); |
- return *scratch; |
- } |
- |
- // This result is owned by fController and lives as long as it does (unless unalloc'd). |
- SkFlatData* detachScratch() { |
- // Allocate a new SkFlatData exactly big enough to hold our current scratch. |
- // We use the controller for this allocation to extend the allocation's lifetime and allow |
- // the controller to do whatever memory management it wants. |
- SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.bytesWritten()); |
- |
- // Copy scratch into the new SkFlatData. |
- SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); |
- SkASSERT(scratch != nullptr); |
- memcpy(detached, scratch, fScratch.bytesWritten()); |
- |
- // We can now reuse fScratch, and detached will live until fController dies. |
- return detached; |
- } |
- |
- void unflatten(T* dst, const SkFlatData* element) const { |
- element->unflatten<Traits>(dst, |
- fController->getBitmapHeap(), |
- fController->getTypefacePlayback()); |
- } |
- |
- // All SkFlatData* stored in fIndexedData and fHash are owned by the controller. |
- SkAutoTUnref<SkFlatController> fController; |
- SkWriteBuffer fScratch; |
- bool fReady; |
- |
- // For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful! |
- SkTDArray<const SkFlatData*> fIndexedData; |
- |
- // For SkFlatData -> cached SkFlatData, which has index(). |
- SkTDynamicHash<SkFlatData, SkFlatData, SkFlatData::HashTraits> fHash; |
-}; |
- |
#endif |