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