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