text: 
  /*
   * 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
  
  
  #include "SkBitmapHeap.h"
  #include "SkChecksum.h"
  #include "SkChunkAlloc.h"
  #include "SkReadBuffer.h"
  #include "SkWriteBuffer.h"
  #include "SkPaint.h"
  #include "SkPicture.h"
  #include "SkPtrRecorder.h"
  #include "SkTDynamicHash.h"
  #include "SkTRefArray.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,
  
      // new ops -- feel free to re-alphabetize on next version bump
      DRAW_DRRECT,
      PUSH_CULL,
      POP_CULL,
      
      DRAW_PATCH, // could not add in aphabetical order
      DRAW_PICTURE_MATRIX_PAINT,
      DRAW_TEXT_BLOB,
  
      LAST_DRAWTYPE_ENUM = DRAW_TEXT_BLOB
  };
  
  // 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,
      DRAW_VERTICES_HAS_XFER    = 0x08,
  };
  
  ///////////////////////////////////////////////////////////////////////////////
  // 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(SkReadBuffer& 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(SkReadBuffer& 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 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:
      SK_DECLARE_INST_COUNT(SkFlatController)
  
      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; }
  
      /**
       * 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*);
  
  private:
      SkBitmapHeap*       fBitmapHeap;
      SkRefCntSet*        fTypefaceSet;
      SkTypefacePlayback* fTypefacePlayback;
      SkNamedFactorySet*  fFactorySet;
      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());
          buffer.setNamedFactoryRecorder(controller->getNamedFactorySet());
  
          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 = NULL,
                     SkTypefacePlayback* facePlayback = NULL) 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::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 <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-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 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 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]);
          }
          return array;
      }
  
      /**
       * 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 NULL.
       */
      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() != NULL);
          fScratch.setBitmapHeap(fController->getBitmapHeap());
          fScratch.setTypefaceRecorder(fController->getTypefaceSet());
          fScratch.setNamedFactoryRecorder(fController->getNamedFactorySet());
          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 != NULL) {
              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 != NULL);
          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 != NULL);
          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;
  };
  
  typedef SkFlatDictionary<SkPaint, SkPaint::FlatteningTraits> SkPaintDictionary;
  
  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;
  };
  
  #endif
  