Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | |
michael@0 | 2 | /* |
michael@0 | 3 | * Copyright 2011 Google Inc. |
michael@0 | 4 | * |
michael@0 | 5 | * Use of this source code is governed by a BSD-style license that can be |
michael@0 | 6 | * found in the LICENSE file. |
michael@0 | 7 | */ |
michael@0 | 8 | #ifndef SkPictureFlat_DEFINED |
michael@0 | 9 | #define SkPictureFlat_DEFINED |
michael@0 | 10 | |
michael@0 | 11 | //#define SK_DEBUG_SIZE |
michael@0 | 12 | |
michael@0 | 13 | #include "SkBitmapHeap.h" |
michael@0 | 14 | #include "SkChecksum.h" |
michael@0 | 15 | #include "SkChunkAlloc.h" |
michael@0 | 16 | #include "SkReadBuffer.h" |
michael@0 | 17 | #include "SkWriteBuffer.h" |
michael@0 | 18 | #include "SkPaint.h" |
michael@0 | 19 | #include "SkPicture.h" |
michael@0 | 20 | #include "SkPtrRecorder.h" |
michael@0 | 21 | #include "SkTDynamicHash.h" |
michael@0 | 22 | #include "SkTRefArray.h" |
michael@0 | 23 | |
michael@0 | 24 | enum DrawType { |
michael@0 | 25 | UNUSED, |
michael@0 | 26 | CLIP_PATH, |
michael@0 | 27 | CLIP_REGION, |
michael@0 | 28 | CLIP_RECT, |
michael@0 | 29 | CLIP_RRECT, |
michael@0 | 30 | CONCAT, |
michael@0 | 31 | DRAW_BITMAP, |
michael@0 | 32 | DRAW_BITMAP_MATRIX, |
michael@0 | 33 | DRAW_BITMAP_NINE, |
michael@0 | 34 | DRAW_BITMAP_RECT_TO_RECT, |
michael@0 | 35 | DRAW_CLEAR, |
michael@0 | 36 | DRAW_DATA, |
michael@0 | 37 | DRAW_OVAL, |
michael@0 | 38 | DRAW_PAINT, |
michael@0 | 39 | DRAW_PATH, |
michael@0 | 40 | DRAW_PICTURE, |
michael@0 | 41 | DRAW_POINTS, |
michael@0 | 42 | DRAW_POS_TEXT, |
michael@0 | 43 | DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT |
michael@0 | 44 | DRAW_POS_TEXT_H, |
michael@0 | 45 | DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H |
michael@0 | 46 | DRAW_RECT, |
michael@0 | 47 | DRAW_RRECT, |
michael@0 | 48 | DRAW_SPRITE, |
michael@0 | 49 | DRAW_TEXT, |
michael@0 | 50 | DRAW_TEXT_ON_PATH, |
michael@0 | 51 | DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT |
michael@0 | 52 | DRAW_VERTICES, |
michael@0 | 53 | RESTORE, |
michael@0 | 54 | ROTATE, |
michael@0 | 55 | SAVE, |
michael@0 | 56 | SAVE_LAYER, |
michael@0 | 57 | SCALE, |
michael@0 | 58 | SET_MATRIX, |
michael@0 | 59 | SKEW, |
michael@0 | 60 | TRANSLATE, |
michael@0 | 61 | NOOP, |
michael@0 | 62 | BEGIN_COMMENT_GROUP, |
michael@0 | 63 | COMMENT, |
michael@0 | 64 | END_COMMENT_GROUP, |
michael@0 | 65 | |
michael@0 | 66 | // new ops -- feel free to re-alphabetize on next version bump |
michael@0 | 67 | DRAW_DRRECT, |
michael@0 | 68 | PUSH_CULL, |
michael@0 | 69 | POP_CULL, |
michael@0 | 70 | |
michael@0 | 71 | LAST_DRAWTYPE_ENUM = POP_CULL |
michael@0 | 72 | }; |
michael@0 | 73 | |
michael@0 | 74 | // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* |
michael@0 | 75 | static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1; |
michael@0 | 76 | |
michael@0 | 77 | enum DrawVertexFlags { |
michael@0 | 78 | DRAW_VERTICES_HAS_TEXS = 0x01, |
michael@0 | 79 | DRAW_VERTICES_HAS_COLORS = 0x02, |
michael@0 | 80 | DRAW_VERTICES_HAS_INDICES = 0x04, |
michael@0 | 81 | DRAW_VERTICES_HAS_XFER = 0x08, |
michael@0 | 82 | }; |
michael@0 | 83 | |
michael@0 | 84 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 85 | // clipparams are packed in 5 bits |
michael@0 | 86 | // doAA:1 | regionOp:4 |
michael@0 | 87 | |
michael@0 | 88 | static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { |
michael@0 | 89 | unsigned doAABit = doAA ? 1 : 0; |
michael@0 | 90 | return (doAABit << 4) | op; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { |
michael@0 | 94 | return (SkRegion::Op)(packed & 0xF); |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | static inline bool ClipParams_unpackDoAA(uint32_t packed) { |
michael@0 | 98 | return SkToBool((packed >> 4) & 1); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 102 | |
michael@0 | 103 | class SkTypefacePlayback { |
michael@0 | 104 | public: |
michael@0 | 105 | SkTypefacePlayback(); |
michael@0 | 106 | virtual ~SkTypefacePlayback(); |
michael@0 | 107 | |
michael@0 | 108 | int count() const { return fCount; } |
michael@0 | 109 | |
michael@0 | 110 | void reset(const SkRefCntSet*); |
michael@0 | 111 | |
michael@0 | 112 | void setCount(int count); |
michael@0 | 113 | SkRefCnt* set(int index, SkRefCnt*); |
michael@0 | 114 | |
michael@0 | 115 | void setupBuffer(SkReadBuffer& buffer) const { |
michael@0 | 116 | buffer.setTypefaceArray((SkTypeface**)fArray, fCount); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | protected: |
michael@0 | 120 | int fCount; |
michael@0 | 121 | SkRefCnt** fArray; |
michael@0 | 122 | }; |
michael@0 | 123 | |
michael@0 | 124 | class SkFactoryPlayback { |
michael@0 | 125 | public: |
michael@0 | 126 | SkFactoryPlayback(int count) : fCount(count) { |
michael@0 | 127 | fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | ~SkFactoryPlayback() { |
michael@0 | 131 | SkDELETE_ARRAY(fArray); |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | SkFlattenable::Factory* base() const { return fArray; } |
michael@0 | 135 | |
michael@0 | 136 | void setupBuffer(SkReadBuffer& buffer) const { |
michael@0 | 137 | buffer.setFactoryPlayback(fArray, fCount); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | private: |
michael@0 | 141 | int fCount; |
michael@0 | 142 | SkFlattenable::Factory* fArray; |
michael@0 | 143 | }; |
michael@0 | 144 | |
michael@0 | 145 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 146 | // |
michael@0 | 147 | // |
michael@0 | 148 | // The following templated classes provide an efficient way to store and compare |
michael@0 | 149 | // objects that have been flattened (i.e. serialized in an ordered binary |
michael@0 | 150 | // format). |
michael@0 | 151 | // |
michael@0 | 152 | // SkFlatData: is a simple indexable container for the flattened data |
michael@0 | 153 | // which is agnostic to the type of data is is indexing. It is |
michael@0 | 154 | // also responsible for flattening/unflattening objects but |
michael@0 | 155 | // details of that operation are hidden in the provided traits |
michael@0 | 156 | // SkFlatDictionary: is an abstract templated dictionary that maintains a |
michael@0 | 157 | // searchable set of SkFlatData objects of type T. |
michael@0 | 158 | // SkFlatController: is an interface provided to SkFlatDictionary which handles |
michael@0 | 159 | // allocation (and unallocation in some cases). It also holds |
michael@0 | 160 | // ref count recorders and the like. |
michael@0 | 161 | // |
michael@0 | 162 | // NOTE: any class that wishes to be used in conjunction with SkFlatDictionary must subclass the |
michael@0 | 163 | // dictionary and provide the necessary flattening traits. SkFlatController must also be |
michael@0 | 164 | // implemented, or SkChunkFlatController can be used to use an SkChunkAllocator and never do |
michael@0 | 165 | // replacements. |
michael@0 | 166 | // |
michael@0 | 167 | // |
michael@0 | 168 | /////////////////////////////////////////////////////////////////////////////// |
michael@0 | 169 | |
michael@0 | 170 | class SkFlatData; |
michael@0 | 171 | |
michael@0 | 172 | class SkFlatController : public SkRefCnt { |
michael@0 | 173 | public: |
michael@0 | 174 | SK_DECLARE_INST_COUNT(SkFlatController) |
michael@0 | 175 | |
michael@0 | 176 | SkFlatController(uint32_t writeBufferFlags = 0); |
michael@0 | 177 | virtual ~SkFlatController(); |
michael@0 | 178 | /** |
michael@0 | 179 | * Return a new block of memory for the SkFlatDictionary to use. |
michael@0 | 180 | * This memory is owned by the controller and has the same lifetime unless you |
michael@0 | 181 | * call unalloc(), in which case it may be freed early. |
michael@0 | 182 | */ |
michael@0 | 183 | virtual void* allocThrow(size_t bytes) = 0; |
michael@0 | 184 | |
michael@0 | 185 | /** |
michael@0 | 186 | * Hint that this block, which was allocated with allocThrow, is no longer needed. |
michael@0 | 187 | * The implementation may choose to free this memory any time beteween now and destruction. |
michael@0 | 188 | */ |
michael@0 | 189 | virtual void unalloc(void* ptr) = 0; |
michael@0 | 190 | |
michael@0 | 191 | /** |
michael@0 | 192 | * Used during creation and unflattening of SkFlatData objects. If the |
michael@0 | 193 | * objects being flattened contain bitmaps they are stored in this heap |
michael@0 | 194 | * and the flattenable stores the index to the bitmap on the heap. |
michael@0 | 195 | * This should be set by the protected setBitmapHeap. |
michael@0 | 196 | */ |
michael@0 | 197 | SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } |
michael@0 | 198 | |
michael@0 | 199 | /** |
michael@0 | 200 | * Used during creation of SkFlatData objects. If a typeface recorder is |
michael@0 | 201 | * required to flatten the objects being flattened (i.e. for SkPaints), this |
michael@0 | 202 | * should be set by the protected setTypefaceSet. |
michael@0 | 203 | */ |
michael@0 | 204 | SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } |
michael@0 | 205 | |
michael@0 | 206 | /** |
michael@0 | 207 | * Used during unflattening of the SkFlatData objects in the |
michael@0 | 208 | * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback |
michael@0 | 209 | * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. |
michael@0 | 210 | */ |
michael@0 | 211 | SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } |
michael@0 | 212 | |
michael@0 | 213 | /** |
michael@0 | 214 | * Optional factory recorder used during creation of SkFlatData objects. Set |
michael@0 | 215 | * using the protected method setNamedFactorySet. |
michael@0 | 216 | */ |
michael@0 | 217 | SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } |
michael@0 | 218 | |
michael@0 | 219 | /** |
michael@0 | 220 | * Flags to use during creation of SkFlatData objects. Defaults to zero. |
michael@0 | 221 | */ |
michael@0 | 222 | uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } |
michael@0 | 223 | |
michael@0 | 224 | protected: |
michael@0 | 225 | /** |
michael@0 | 226 | * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. |
michael@0 | 227 | */ |
michael@0 | 228 | void setBitmapHeap(SkBitmapHeap*); |
michael@0 | 229 | |
michael@0 | 230 | /** |
michael@0 | 231 | * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref |
michael@0 | 232 | * counted. |
michael@0 | 233 | */ |
michael@0 | 234 | void setTypefaceSet(SkRefCntSet*); |
michael@0 | 235 | |
michael@0 | 236 | /** |
michael@0 | 237 | * Set an SkTypefacePlayback to be used to find references to SkTypefaces |
michael@0 | 238 | * during unflattening. Should be reset to the set provided to |
michael@0 | 239 | * setTypefaceSet. |
michael@0 | 240 | */ |
michael@0 | 241 | void setTypefacePlayback(SkTypefacePlayback*); |
michael@0 | 242 | |
michael@0 | 243 | /** |
michael@0 | 244 | * Set an SkNamedFactorySet to be used to store Factorys and their |
michael@0 | 245 | * corresponding names during flattening. Ref counted. Returns the same |
michael@0 | 246 | * set as a convenience. |
michael@0 | 247 | */ |
michael@0 | 248 | SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); |
michael@0 | 249 | |
michael@0 | 250 | private: |
michael@0 | 251 | SkBitmapHeap* fBitmapHeap; |
michael@0 | 252 | SkRefCntSet* fTypefaceSet; |
michael@0 | 253 | SkTypefacePlayback* fTypefacePlayback; |
michael@0 | 254 | SkNamedFactorySet* fFactorySet; |
michael@0 | 255 | const uint32_t fWriteBufferFlags; |
michael@0 | 256 | |
michael@0 | 257 | typedef SkRefCnt INHERITED; |
michael@0 | 258 | }; |
michael@0 | 259 | |
michael@0 | 260 | class SkFlatData { |
michael@0 | 261 | public: |
michael@0 | 262 | // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*. |
michael@0 | 263 | template <typename Traits, typename T> |
michael@0 | 264 | static SkFlatData* Create(SkFlatController* controller, const T& obj, int index) { |
michael@0 | 265 | // A buffer of 256 bytes should fit most paints, regions, and matrices. |
michael@0 | 266 | uint32_t storage[64]; |
michael@0 | 267 | SkWriteBuffer buffer(storage, sizeof(storage), controller->getWriteBufferFlags()); |
michael@0 | 268 | |
michael@0 | 269 | buffer.setBitmapHeap(controller->getBitmapHeap()); |
michael@0 | 270 | buffer.setTypefaceRecorder(controller->getTypefaceSet()); |
michael@0 | 271 | buffer.setNamedFactoryRecorder(controller->getNamedFactorySet()); |
michael@0 | 272 | |
michael@0 | 273 | Traits::Flatten(buffer, obj); |
michael@0 | 274 | size_t size = buffer.bytesWritten(); |
michael@0 | 275 | SkASSERT(SkIsAlign4(size)); |
michael@0 | 276 | |
michael@0 | 277 | // Allocate enough memory to hold SkFlatData struct and the flat data itself. |
michael@0 | 278 | size_t allocSize = sizeof(SkFlatData) + size; |
michael@0 | 279 | SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize); |
michael@0 | 280 | |
michael@0 | 281 | // Put the serialized contents into the data section of the new allocation. |
michael@0 | 282 | buffer.writeToMemory(result->data()); |
michael@0 | 283 | // Stamp the index, size and checksum in the header. |
michael@0 | 284 | result->stampHeader(index, SkToS32(size)); |
michael@0 | 285 | return result; |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given |
michael@0 | 289 | template <typename Traits, typename T> |
michael@0 | 290 | void unflatten(T* result, |
michael@0 | 291 | SkBitmapHeap* bitmapHeap = NULL, |
michael@0 | 292 | SkTypefacePlayback* facePlayback = NULL) const { |
michael@0 | 293 | SkReadBuffer buffer(this->data(), fFlatSize); |
michael@0 | 294 | |
michael@0 | 295 | if (bitmapHeap) { |
michael@0 | 296 | buffer.setBitmapStorage(bitmapHeap); |
michael@0 | 297 | } |
michael@0 | 298 | if (facePlayback) { |
michael@0 | 299 | facePlayback->setupBuffer(buffer); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | Traits::Unflatten(buffer, result); |
michael@0 | 303 | SkASSERT(fFlatSize == (int32_t)buffer.offset()); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | // Do these contain the same data? Ignores index() and topBot(). |
michael@0 | 307 | bool operator==(const SkFlatData& that) const { |
michael@0 | 308 | if (this->checksum() != that.checksum() || this->flatSize() != that.flatSize()) { |
michael@0 | 309 | return false; |
michael@0 | 310 | } |
michael@0 | 311 | return memcmp(this->data(), that.data(), this->flatSize()) == 0; |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | int index() const { return fIndex; } |
michael@0 | 315 | const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); } |
michael@0 | 316 | size_t flatSize() const { return fFlatSize; } |
michael@0 | 317 | uint32_t checksum() const { return fChecksum; } |
michael@0 | 318 | |
michael@0 | 319 | // Returns true if fTopBot[] has been recorded. |
michael@0 | 320 | bool isTopBotWritten() const { |
michael@0 | 321 | return !SkScalarIsNaN(fTopBot[0]); |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | // Returns fTopBot array, so it can be passed to a routine to compute them. |
michael@0 | 325 | // For efficiency, we assert that fTopBot have not been recorded yet. |
michael@0 | 326 | SkScalar* writableTopBot() const { |
michael@0 | 327 | SkASSERT(!this->isTopBotWritten()); |
michael@0 | 328 | return fTopBot; |
michael@0 | 329 | } |
michael@0 | 330 | |
michael@0 | 331 | // Return the topbot[] after it has been recorded. |
michael@0 | 332 | const SkScalar* topBot() const { |
michael@0 | 333 | SkASSERT(this->isTopBotWritten()); |
michael@0 | 334 | return fTopBot; |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | private: |
michael@0 | 338 | // For SkTDynamicHash. |
michael@0 | 339 | static const SkFlatData& Identity(const SkFlatData& flat) { return flat; } |
michael@0 | 340 | static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } |
michael@0 | 341 | static bool Equal(const SkFlatData& a, const SkFlatData& b) { return a == b; } |
michael@0 | 342 | |
michael@0 | 343 | void setIndex(int index) { fIndex = index; } |
michael@0 | 344 | uint8_t* data() { return (uint8_t*)this + sizeof(*this); } |
michael@0 | 345 | |
michael@0 | 346 | // This assumes the payload flat data has already been written and does not modify it. |
michael@0 | 347 | void stampHeader(int index, int32_t size) { |
michael@0 | 348 | SkASSERT(SkIsAlign4(size)); |
michael@0 | 349 | fIndex = index; |
michael@0 | 350 | fFlatSize = size; |
michael@0 | 351 | fTopBot[0] = SK_ScalarNaN; // Mark as unwritten. |
michael@0 | 352 | fChecksum = SkChecksum::Compute((uint32_t*)this->data(), size); |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | int fIndex; |
michael@0 | 356 | int32_t fFlatSize; |
michael@0 | 357 | uint32_t fChecksum; |
michael@0 | 358 | mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?]. |
michael@0 | 359 | // uint32_t flattenedData[] implicitly hangs off the end. |
michael@0 | 360 | |
michael@0 | 361 | template <typename T, typename Traits> friend class SkFlatDictionary; |
michael@0 | 362 | }; |
michael@0 | 363 | |
michael@0 | 364 | template <typename T, typename Traits> |
michael@0 | 365 | class SkFlatDictionary { |
michael@0 | 366 | public: |
michael@0 | 367 | explicit SkFlatDictionary(SkFlatController* controller) |
michael@0 | 368 | : fController(SkRef(controller)) |
michael@0 | 369 | , fScratch(controller->getWriteBufferFlags()) |
michael@0 | 370 | , fReady(false) { |
michael@0 | 371 | this->reset(); |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | /** |
michael@0 | 375 | * Clears the dictionary of all entries. However, it does NOT free the |
michael@0 | 376 | * memory that was allocated for each entry (that's owned by controller). |
michael@0 | 377 | */ |
michael@0 | 378 | void reset() { |
michael@0 | 379 | fIndexedData.rewind(); |
michael@0 | 380 | } |
michael@0 | 381 | |
michael@0 | 382 | int count() const { |
michael@0 | 383 | SkASSERT(fHash.count() == fIndexedData.count()); |
michael@0 | 384 | return fHash.count(); |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | // For testing only. Index is zero-based. |
michael@0 | 388 | const SkFlatData* operator[](int index) { |
michael@0 | 389 | return fIndexedData[index]; |
michael@0 | 390 | } |
michael@0 | 391 | |
michael@0 | 392 | /** |
michael@0 | 393 | * Given an element of type T return its 1-based index in the dictionary. If |
michael@0 | 394 | * the element wasn't previously in the dictionary it is automatically |
michael@0 | 395 | * added. |
michael@0 | 396 | * |
michael@0 | 397 | */ |
michael@0 | 398 | int find(const T& element) { |
michael@0 | 399 | return this->findAndReturnFlat(element)->index(); |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | /** |
michael@0 | 403 | * Similar to find. Allows the caller to specify an SkFlatData to replace in |
michael@0 | 404 | * the case of an add. Also tells the caller whether a new SkFlatData was |
michael@0 | 405 | * added and whether the old one was replaced. The parameters added and |
michael@0 | 406 | * replaced are required to be non-NULL. Rather than returning the index of |
michael@0 | 407 | * the entry in the dictionary, it returns the actual SkFlatData. |
michael@0 | 408 | */ |
michael@0 | 409 | const SkFlatData* findAndReplace(const T& element, |
michael@0 | 410 | const SkFlatData* toReplace, |
michael@0 | 411 | bool* added, |
michael@0 | 412 | bool* replaced) { |
michael@0 | 413 | SkASSERT(added != NULL && replaced != NULL); |
michael@0 | 414 | |
michael@0 | 415 | const int oldCount = this->count(); |
michael@0 | 416 | SkFlatData* flat = this->findAndReturnMutableFlat(element); |
michael@0 | 417 | *added = this->count() > oldCount; |
michael@0 | 418 | |
michael@0 | 419 | // If we don't want to replace anything, we're done. |
michael@0 | 420 | if (!*added || toReplace == NULL) { |
michael@0 | 421 | *replaced = false; |
michael@0 | 422 | return flat; |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | // If we don't have the thing to replace, we're done. |
michael@0 | 426 | const SkFlatData* found = fHash.find(*toReplace); |
michael@0 | 427 | if (found == NULL) { |
michael@0 | 428 | *replaced = false; |
michael@0 | 429 | return flat; |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | // findAndReturnMutableFlat put flat at the back. Swap it into found->index() instead. |
michael@0 | 433 | // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Watch out! |
michael@0 | 434 | SkASSERT(flat->index() == this->count()); |
michael@0 | 435 | flat->setIndex(found->index()); |
michael@0 | 436 | fIndexedData.removeShuffle(found->index()-1); |
michael@0 | 437 | SkASSERT(flat == fIndexedData[found->index()-1]); |
michael@0 | 438 | |
michael@0 | 439 | // findAndReturnMutableFlat already called fHash.add(), so we just clean up the old entry. |
michael@0 | 440 | fHash.remove(*found); |
michael@0 | 441 | fController->unalloc((void*)found); |
michael@0 | 442 | SkASSERT(this->count() == oldCount); |
michael@0 | 443 | |
michael@0 | 444 | *replaced = true; |
michael@0 | 445 | return flat; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | /** |
michael@0 | 449 | * Unflatten the objects and return them in SkTRefArray, or return NULL |
michael@0 | 450 | * if there no objects. Caller takes ownership of result. |
michael@0 | 451 | */ |
michael@0 | 452 | SkTRefArray<T>* unflattenToArray() const { |
michael@0 | 453 | const int count = this->count(); |
michael@0 | 454 | if (count == 0) { |
michael@0 | 455 | return NULL; |
michael@0 | 456 | } |
michael@0 | 457 | SkTRefArray<T>* array = SkTRefArray<T>::Create(count); |
michael@0 | 458 | for (int i = 0; i < count; i++) { |
michael@0 | 459 | this->unflatten(&array->writableAt(i), fIndexedData[i]); |
michael@0 | 460 | } |
michael@0 | 461 | return array; |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | /** |
michael@0 | 465 | * Unflatten the specific object at the given index. |
michael@0 | 466 | * Caller takes ownership of the result. |
michael@0 | 467 | */ |
michael@0 | 468 | T* unflatten(int index) const { |
michael@0 | 469 | // index is 1-based, while fIndexedData is 0-based. |
michael@0 | 470 | const SkFlatData* element = fIndexedData[index-1]; |
michael@0 | 471 | SkASSERT(index == element->index()); |
michael@0 | 472 | |
michael@0 | 473 | T* dst = new T; |
michael@0 | 474 | this->unflatten(dst, element); |
michael@0 | 475 | return dst; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | /** |
michael@0 | 479 | * Find or insert a flattened version of element into the dictionary. |
michael@0 | 480 | * Caller does not take ownership of the result. This will not return NULL. |
michael@0 | 481 | */ |
michael@0 | 482 | const SkFlatData* findAndReturnFlat(const T& element) { |
michael@0 | 483 | return this->findAndReturnMutableFlat(element); |
michael@0 | 484 | } |
michael@0 | 485 | |
michael@0 | 486 | private: |
michael@0 | 487 | // We have to delay fScratch's initialization until its first use; fController might not |
michael@0 | 488 | // be fully set up by the time we get it in the constructor. |
michael@0 | 489 | void lazyInit() { |
michael@0 | 490 | if (fReady) { |
michael@0 | 491 | return; |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. |
michael@0 | 495 | SkASSERT(fController->getBitmapHeap() != NULL); |
michael@0 | 496 | fScratch.setBitmapHeap(fController->getBitmapHeap()); |
michael@0 | 497 | fScratch.setTypefaceRecorder(fController->getTypefaceSet()); |
michael@0 | 498 | fScratch.setNamedFactoryRecorder(fController->getNamedFactorySet()); |
michael@0 | 499 | fReady = true; |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | // As findAndReturnFlat, but returns a mutable pointer for internal use. |
michael@0 | 503 | SkFlatData* findAndReturnMutableFlat(const T& element) { |
michael@0 | 504 | // Only valid until the next call to resetScratch(). |
michael@0 | 505 | const SkFlatData& scratch = this->resetScratch(element, this->count()+1); |
michael@0 | 506 | |
michael@0 | 507 | SkFlatData* candidate = fHash.find(scratch); |
michael@0 | 508 | if (candidate != NULL) { |
michael@0 | 509 | return candidate; |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | SkFlatData* detached = this->detachScratch(); |
michael@0 | 513 | fHash.add(detached); |
michael@0 | 514 | *fIndexedData.append() = detached; |
michael@0 | 515 | SkASSERT(fIndexedData.top()->index() == this->count()); |
michael@0 | 516 | return detached; |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | // This reference is valid only until the next call to resetScratch() or detachScratch(). |
michael@0 | 520 | const SkFlatData& resetScratch(const T& element, int index) { |
michael@0 | 521 | this->lazyInit(); |
michael@0 | 522 | |
michael@0 | 523 | // Layout of fScratch: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] |
michael@0 | 524 | fScratch.reset(); |
michael@0 | 525 | fScratch.reserve(sizeof(SkFlatData)); |
michael@0 | 526 | Traits::Flatten(fScratch, element); |
michael@0 | 527 | const size_t dataSize = fScratch.bytesWritten() - sizeof(SkFlatData); |
michael@0 | 528 | |
michael@0 | 529 | // Reinterpret data in fScratch as an SkFlatData. |
michael@0 | 530 | SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); |
michael@0 | 531 | SkASSERT(scratch != NULL); |
michael@0 | 532 | scratch->stampHeader(index, dataSize); |
michael@0 | 533 | return *scratch; |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | // This result is owned by fController and lives as long as it does (unless unalloc'd). |
michael@0 | 537 | SkFlatData* detachScratch() { |
michael@0 | 538 | // Allocate a new SkFlatData exactly big enough to hold our current scratch. |
michael@0 | 539 | // We use the controller for this allocation to extend the allocation's lifetime and allow |
michael@0 | 540 | // the controller to do whatever memory management it wants. |
michael@0 | 541 | SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.bytesWritten()); |
michael@0 | 542 | |
michael@0 | 543 | // Copy scratch into the new SkFlatData. |
michael@0 | 544 | SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); |
michael@0 | 545 | SkASSERT(scratch != NULL); |
michael@0 | 546 | memcpy(detached, scratch, fScratch.bytesWritten()); |
michael@0 | 547 | |
michael@0 | 548 | // We can now reuse fScratch, and detached will live until fController dies. |
michael@0 | 549 | return detached; |
michael@0 | 550 | } |
michael@0 | 551 | |
michael@0 | 552 | void unflatten(T* dst, const SkFlatData* element) const { |
michael@0 | 553 | element->unflatten<Traits>(dst, |
michael@0 | 554 | fController->getBitmapHeap(), |
michael@0 | 555 | fController->getTypefacePlayback()); |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | // All SkFlatData* stored in fIndexedData and fHash are owned by the controller. |
michael@0 | 559 | SkAutoTUnref<SkFlatController> fController; |
michael@0 | 560 | SkWriteBuffer fScratch; |
michael@0 | 561 | bool fReady; |
michael@0 | 562 | |
michael@0 | 563 | // For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful! |
michael@0 | 564 | SkTDArray<const SkFlatData*> fIndexedData; |
michael@0 | 565 | |
michael@0 | 566 | // For SkFlatData -> cached SkFlatData, which has index(). |
michael@0 | 567 | SkTDynamicHash<SkFlatData, SkFlatData, |
michael@0 | 568 | SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash; |
michael@0 | 569 | }; |
michael@0 | 570 | |
michael@0 | 571 | typedef SkFlatDictionary<SkPaint, SkPaint::FlatteningTraits> SkPaintDictionary; |
michael@0 | 572 | |
michael@0 | 573 | class SkChunkFlatController : public SkFlatController { |
michael@0 | 574 | public: |
michael@0 | 575 | SkChunkFlatController(size_t minSize) |
michael@0 | 576 | : fHeap(minSize) |
michael@0 | 577 | , fTypefaceSet(SkNEW(SkRefCntSet)) |
michael@0 | 578 | , fLastAllocated(NULL) { |
michael@0 | 579 | this->setTypefaceSet(fTypefaceSet); |
michael@0 | 580 | this->setTypefacePlayback(&fTypefacePlayback); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | virtual void* allocThrow(size_t bytes) SK_OVERRIDE { |
michael@0 | 584 | fLastAllocated = fHeap.allocThrow(bytes); |
michael@0 | 585 | return fLastAllocated; |
michael@0 | 586 | } |
michael@0 | 587 | |
michael@0 | 588 | virtual void unalloc(void* ptr) SK_OVERRIDE { |
michael@0 | 589 | // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just |
michael@0 | 590 | // have to wait until fHeap is destroyed. |
michael@0 | 591 | if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | void setupPlaybacks() const { |
michael@0 | 595 | fTypefacePlayback.reset(fTypefaceSet.get()); |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | void setBitmapStorage(SkBitmapHeap* heap) { |
michael@0 | 599 | this->setBitmapHeap(heap); |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | private: |
michael@0 | 603 | SkChunkAlloc fHeap; |
michael@0 | 604 | SkAutoTUnref<SkRefCntSet> fTypefaceSet; |
michael@0 | 605 | void* fLastAllocated; |
michael@0 | 606 | mutable SkTypefacePlayback fTypefacePlayback; |
michael@0 | 607 | }; |
michael@0 | 608 | |
michael@0 | 609 | #endif |