/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkPictureFlat_DEFINED #define SkPictureFlat_DEFINED //#define SK_DEBUG_SIZE #include "SkBitmap.h" #include "SkBitmapHeap.h" #include "SkChecksum.h" #include "SkChunkAlloc.h" #include "SkMatrix.h" #include "SkOrderedReadBuffer.h" #include "SkOrderedWriteBuffer.h" #include "SkPaint.h" #include "SkPath.h" #include "SkPicture.h" #include "SkPtrRecorder.h" #include "SkRegion.h" #include "SkTDynamicHash.h" #include "SkTRefArray.h" #include "SkTSearch.h" enum DrawType { UNUSED, CLIP_PATH, CLIP_REGION, CLIP_RECT, CLIP_RRECT, CONCAT, DRAW_BITMAP, DRAW_BITMAP_MATRIX, DRAW_BITMAP_NINE, DRAW_BITMAP_RECT_TO_RECT, DRAW_CLEAR, DRAW_DATA, DRAW_OVAL, DRAW_PAINT, DRAW_PATH, DRAW_PICTURE, DRAW_POINTS, DRAW_POS_TEXT, DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT DRAW_POS_TEXT_H, DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H DRAW_RECT, DRAW_RRECT, DRAW_SPRITE, DRAW_TEXT, DRAW_TEXT_ON_PATH, DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT DRAW_VERTICES, RESTORE, ROTATE, SAVE, SAVE_LAYER, SCALE, SET_MATRIX, SKEW, TRANSLATE, NOOP, BEGIN_COMMENT_GROUP, COMMENT, END_COMMENT_GROUP, LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP }; // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1; enum DrawVertexFlags { DRAW_VERTICES_HAS_TEXS = 0x01, DRAW_VERTICES_HAS_COLORS = 0x02, DRAW_VERTICES_HAS_INDICES = 0x04 }; /////////////////////////////////////////////////////////////////////////////// // clipparams are packed in 5 bits // doAA:1 | regionOp:4 static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { unsigned doAABit = doAA ? 1 : 0; return (doAABit << 4) | op; } static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { return (SkRegion::Op)(packed & 0xF); } static inline bool ClipParams_unpackDoAA(uint32_t packed) { return SkToBool((packed >> 4) & 1); } /////////////////////////////////////////////////////////////////////////////// class SkTypefacePlayback { public: SkTypefacePlayback(); virtual ~SkTypefacePlayback(); int count() const { return fCount; } void reset(const SkRefCntSet*); void setCount(int count); SkRefCnt* set(int index, SkRefCnt*); void setupBuffer(SkOrderedReadBuffer& buffer) const { buffer.setTypefaceArray((SkTypeface**)fArray, fCount); } protected: int fCount; SkRefCnt** fArray; }; class SkFactoryPlayback { public: SkFactoryPlayback(int count) : fCount(count) { fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); } ~SkFactoryPlayback() { SkDELETE_ARRAY(fArray); } SkFlattenable::Factory* base() const { return fArray; } void setupBuffer(SkOrderedReadBuffer& buffer) const { buffer.setFactoryPlayback(fArray, fCount); } private: int fCount; 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 procs // 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 procs. // The end of this header contains dictionary subclasses for some common classes // like SkBitmap, SkMatrix, SkPaint, and SkRegion. 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: SK_DECLARE_INST_COUNT(SkFlatController) SkFlatController(); 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; } /** * Optional factory recorder used during creation of SkFlatData objects. Set * using the protected method setNamedFactorySet. */ SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } /** * 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*); /** * Set an SkNamedFactorySet to be used to store Factorys and their * corresponding names during flattening. Ref counted. Returns the same * set as a convenience. */ SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); /** * Set the flags to be used during flattening. */ void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; } private: SkBitmapHeap* fBitmapHeap; SkRefCntSet* fTypefaceSet; SkTypefacePlayback* fTypefacePlayback; SkNamedFactorySet* fFactorySet; uint32_t fWriteBufferFlags; typedef SkRefCnt INHERITED; }; class SkFlatData { public: // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*. static SkFlatData* Create(SkFlatController* controller, const void* obj, int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*)); // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given. void unflatten(void* result, void (*unflattenProc)(SkOrderedReadBuffer&, void*), SkBitmapHeap* bitmapHeap = NULL, SkTypefacePlayback* facePlayback = NULL) const; // 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: // For SkTDynamicHash. static const SkFlatData& Identity(const SkFlatData& flat) { return flat; } static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } static bool Equal(const SkFlatData& a, const SkFlatData& b) { return a == b; } 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::Compute((uint32_t*)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 <class T> friend class SkFlatDictionary; }; template <class T> class SkFlatDictionary { static const size_t kWriteBufferGrowthBytes = 1024; public: SkFlatDictionary(SkFlatController* controller, size_t scratchSizeGuess = 0) : fFlattenProc(NULL) , fUnflattenProc(NULL) , fController(SkRef(controller)) , fScratchSize(scratchSizeGuess) , fScratch(AllocScratch(fScratchSize)) , fWriteBuffer(kWriteBufferGrowthBytes) , fWriteBufferReady(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(); // TODO(mtklein): There's no reason to have the index start from 1. Clean this up. // index 0 is always empty since it is used as a signal that find failed fIndexedData.push(NULL); fNextIndex = 1; } ~SkFlatDictionary() { sk_free(fScratch); } int count() const { SkASSERT(fIndexedData.count() == fNextIndex); SkASSERT(fHash.count() == fNextIndex - 1); return fNextIndex - 1; } // For testing only. Index is zero-based. const SkFlatData* operator[](int index) { return fIndexedData[index+1]; } /** * 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-NULL. 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 != NULL && replaced != NULL); 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 == NULL) { *replaced = false; return flat; } // If we don't have the thing to replace, we're done. const SkFlatData* found = fHash.find(*toReplace); if (found == NULL) { *replaced = false; return flat; } // findAndReturnMutableFlat gave us index (fNextIndex-1), but we'll use the old one. fIndexedData.remove(flat->index()); fNextIndex--; flat->setIndex(found->index()); fIndexedData[flat->index()] = flat; // 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 objects and return them in SkTRefArray, or return NULL * if there no objects. Caller takes ownership of result. */ SkTRefArray<T>* unflattenToArray() const { const int count = this->count(); if (count == 0) { return NULL; } SkTRefArray<T>* array = SkTRefArray<T>::Create(count); for (int i = 0; i < count; i++) { this->unflatten(&array->writableAt(i), fIndexedData[i+1]); } return array; } /** * Unflatten the specific object at the given index. * Caller takes ownership of the result. */ T* unflatten(int index) const { const SkFlatData* element = fIndexedData[index]; 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 NULL. */ const SkFlatData* findAndReturnFlat(const T& element) { return this->findAndReturnMutableFlat(element); } protected: void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*); void (*fUnflattenProc)(SkOrderedReadBuffer&, void*); private: // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] static size_t SizeWithPadding(size_t flatDataSize) { SkASSERT(SkIsAlign4(flatDataSize)); return sizeof(SkFlatData) + flatDataSize; } // Allocate a new scratch SkFlatData. Must be sk_freed. static SkFlatData* AllocScratch(size_t scratchSize) { return (SkFlatData*) sk_malloc_throw(SizeWithPadding(scratchSize)); } // We have to delay fWriteBuffer's initialization until its first use; fController might not // be fully set up by the time we get it in the constructor. void lazyWriteBufferInit() { if (fWriteBufferReady) { return; } // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. SkASSERT(fController->getBitmapHeap() != NULL); fWriteBuffer.setBitmapHeap(fController->getBitmapHeap()); fWriteBuffer.setTypefaceRecorder(fController->getTypefaceSet()); fWriteBuffer.setNamedFactoryRecorder(fController->getNamedFactorySet()); fWriteBuffer.setFlags(fController->getWriteBufferFlags()); fWriteBufferReady = 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, fNextIndex); SkFlatData* candidate = fHash.find(scratch); if (candidate != NULL) return candidate; SkFlatData* detached = this->detachScratch(); fHash.add(detached); *fIndexedData.insert(fNextIndex) = detached; fNextIndex++; return detached; } // This reference is valid only until the next call to resetScratch() or detachScratch(). const SkFlatData& resetScratch(const T& element, int index) { this->lazyWriteBufferInit(); // Flatten element into fWriteBuffer (using fScratch as storage). fWriteBuffer.reset(fScratch->data(), fScratchSize); fFlattenProc(fWriteBuffer, &element); const size_t bytesWritten = fWriteBuffer.bytesWritten(); // If all the flattened bytes fit into fScratch, we can skip a call to writeToMemory. if (!fWriteBuffer.wroteOnlyToStorage()) { SkASSERT(bytesWritten > fScratchSize); // It didn't all fit. Copy into a larger replacement SkFlatData. // We can't just realloc because it might move the pointer and confuse writeToMemory. SkFlatData* larger = AllocScratch(bytesWritten); fWriteBuffer.writeToMemory(larger->data()); // Carry on with this larger scratch to minimize the likelihood of future resizing. sk_free(fScratch); fScratchSize = bytesWritten; fScratch = larger; } // The data is in fScratch now but we need to stamp its header. fScratch->stampHeader(index, bytesWritten); return *fScratch; } // 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. const size_t paddedSize = SizeWithPadding(fScratch->flatSize()); SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize); // Copy scratch into the new SkFlatData. memcpy(detached, fScratch, paddedSize); // We can now reuse fScratch, and detached will live until fController dies. return detached; } void unflatten(T* dst, const SkFlatData* element) const { element->unflatten(dst, fUnflattenProc, fController->getBitmapHeap(), fController->getTypefacePlayback()); } // All SkFlatData* stored in fIndexedData and fHash are owned by the controller. SkAutoTUnref<SkFlatController> fController; size_t fScratchSize; // How many bytes fScratch has allocated for data itself. SkFlatData* fScratch; // Owned, must be freed with sk_free. SkOrderedWriteBuffer fWriteBuffer; bool fWriteBufferReady; // We map between SkFlatData and a 1-based integer index. int fNextIndex; // For index -> SkFlatData. fIndexedData[0] is always NULL. SkTDArray<const SkFlatData*> fIndexedData; // For SkFlatData -> cached SkFlatData, which has index(). SkTDynamicHash<SkFlatData, SkFlatData, SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash; }; /////////////////////////////////////////////////////////////////////////////// // Some common dictionaries are defined here for both reference and convenience /////////////////////////////////////////////////////////////////////////////// template <class T> static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) { ((T*)obj)->flatten(buffer); } template <class T> static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) { ((T*)obj)->unflatten(buffer); } class SkChunkFlatController : public SkFlatController { public: SkChunkFlatController(size_t minSize) : fHeap(minSize) , fTypefaceSet(SkNEW(SkRefCntSet)) , fLastAllocated(NULL) { this->setTypefaceSet(fTypefaceSet); this->setTypefacePlayback(&fTypefacePlayback); } virtual void* allocThrow(size_t bytes) SK_OVERRIDE { fLastAllocated = fHeap.allocThrow(bytes); return fLastAllocated; } virtual void unalloc(void* ptr) SK_OVERRIDE { // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just // have to wait until fHeap is destroyed. if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); } void setupPlaybacks() const { fTypefacePlayback.reset(fTypefaceSet.get()); } void setBitmapStorage(SkBitmapHeap* heap) { this->setBitmapHeap(heap); } private: SkChunkAlloc fHeap; SkAutoTUnref<SkRefCntSet> fTypefaceSet; void* fLastAllocated; mutable SkTypefacePlayback fTypefacePlayback; }; class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { public: // All matrices fit in 36 bytes. SkMatrixDictionary(SkFlatController* controller) : SkFlatDictionary<SkMatrix>(controller, 36) { fFlattenProc = &flattenMatrix; fUnflattenProc = &unflattenMatrix; } static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) { buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj); } static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) { buffer.getReader32()->readMatrix((SkMatrix*)obj); } }; class SkPaintDictionary : public SkFlatDictionary<SkPaint> { public: // The largest paint across ~60 .skps was 500 bytes. SkPaintDictionary(SkFlatController* controller) : SkFlatDictionary<SkPaint>(controller, 512) { fFlattenProc = &SkFlattenObjectProc<SkPaint>; fUnflattenProc = &SkUnflattenObjectProc<SkPaint>; } }; class SkRegionDictionary : public SkFlatDictionary<SkRegion> { public: SkRegionDictionary(SkFlatController* controller) : SkFlatDictionary<SkRegion>(controller) { fFlattenProc = &flattenRegion; fUnflattenProc = &unflattenRegion; } static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) { buffer.getWriter32()->writeRegion(*(SkRegion*)obj); } static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) { buffer.getReader32()->readRegion((SkRegion*)obj); } }; #endif