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