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 "SkChunkAlloc.h"
14#include "SkBitmap.h"
15#include "SkBitmapHeap.h"
16#include "SkOrderedReadBuffer.h"
17#include "SkOrderedWriteBuffer.h"
18#include "SkPicture.h"
19#include "SkPtrRecorder.h"
20#include "SkMatrix.h"
21#include "SkPaint.h"
22#include "SkPath.h"
23#include "SkRegion.h"
24#include "SkTRefArray.h"
25#include "SkTSearch.h"
26
27enum DrawType {
28    UNUSED,
29    CLIP_PATH,
30    CLIP_REGION,
31    CLIP_RECT,
32    CLIP_RRECT,
33    CONCAT,
34    DRAW_BITMAP,
35    DRAW_BITMAP_MATRIX,
36    DRAW_BITMAP_NINE,
37    DRAW_BITMAP_RECT_TO_RECT,
38    DRAW_CLEAR,
39    DRAW_DATA,
40    DRAW_OVAL,
41    DRAW_PAINT,
42    DRAW_PATH,
43    DRAW_PICTURE,
44    DRAW_POINTS,
45    DRAW_POS_TEXT,
46    DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
47    DRAW_POS_TEXT_H,
48    DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
49    DRAW_RECT,
50    DRAW_RRECT,
51    DRAW_SPRITE,
52    DRAW_TEXT,
53    DRAW_TEXT_ON_PATH,
54    DRAW_TEXT_TOP_BOTTOM,   // fast variant of DRAW_TEXT
55    DRAW_VERTICES,
56    RESTORE,
57    ROTATE,
58    SAVE,
59    SAVE_LAYER,
60    SCALE,
61    SET_MATRIX,
62    SKEW,
63    TRANSLATE,
64
65    LAST_DRAWTYPE_ENUM = TRANSLATE
66};
67
68enum DrawVertexFlags {
69    DRAW_VERTICES_HAS_TEXS    = 0x01,
70    DRAW_VERTICES_HAS_COLORS  = 0x02,
71    DRAW_VERTICES_HAS_INDICES = 0x04
72};
73
74///////////////////////////////////////////////////////////////////////////////
75// clipparams are packed in 5 bits
76//  doAA:1 | regionOp:4
77
78static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) {
79    unsigned doAABit = doAA ? 1 : 0;
80    return (doAABit << 4) | op;
81}
82
83static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) {
84    return (SkRegion::Op)(packed & 0xF);
85}
86
87static inline bool ClipParams_unpackDoAA(uint32_t packed) {
88    return SkToBool((packed >> 4) & 1);
89}
90
91///////////////////////////////////////////////////////////////////////////////
92
93class SkTypefacePlayback {
94public:
95    SkTypefacePlayback();
96    virtual ~SkTypefacePlayback();
97
98    int count() const { return fCount; }
99
100    void reset(const SkRefCntSet*);
101
102    void setCount(int count);
103    SkRefCnt* set(int index, SkRefCnt*);
104
105    void setupBuffer(SkOrderedReadBuffer& buffer) const {
106        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
107    }
108
109protected:
110    int fCount;
111    SkRefCnt** fArray;
112};
113
114class SkFactoryPlayback {
115public:
116    SkFactoryPlayback(int count) : fCount(count) {
117        fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
118    }
119
120    ~SkFactoryPlayback() {
121        SkDELETE_ARRAY(fArray);
122    }
123
124    SkFlattenable::Factory* base() const { return fArray; }
125
126    void setupBuffer(SkOrderedReadBuffer& buffer) const {
127        buffer.setFactoryPlayback(fArray, fCount);
128    }
129
130private:
131    int fCount;
132    SkFlattenable::Factory* fArray;
133};
134
135///////////////////////////////////////////////////////////////////////////////
136//
137//
138// The following templated classes provide an efficient way to store and compare
139// objects that have been flattened (i.e. serialized in an ordered binary
140// format).
141//
142// SkFlatData:       is a simple indexable container for the flattened data
143//                   which is agnostic to the type of data is is indexing. It is
144//                   also responsible for flattening/unflattening objects but
145//                   details of that operation are hidden in the provided procs
146// SkFlatDictionary: is an abstract templated dictionary that maintains a
147//                   searchable set of SkFlataData objects of type T.
148// SkFlatController: is an interface provided to SkFlatDictionary which handles
149//                   allocation and unallocation in some cases. It also holds
150//                   ref count recorders and the like.
151//
152// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary
153// must subclass the dictionary and provide the necessary flattening procs.
154// The end of this header contains dictionary subclasses for some common classes
155// like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also
156// be implemented, or SkChunkFlatController can be used to use an
157// SkChunkAllocator and never do replacements.
158//
159//
160///////////////////////////////////////////////////////////////////////////////
161
162class SkFlatData;
163
164class SkFlatController : public SkRefCnt {
165public:
166    SK_DECLARE_INST_COUNT(SkFlatController)
167
168    SkFlatController();
169    virtual ~SkFlatController();
170    /**
171     * Provide a new block of memory for the SkFlatDictionary to use.
172     */
173    virtual void* allocThrow(size_t bytes) = 0;
174
175    /**
176     * Unallocate a previously allocated block, returned by allocThrow.
177     * Implementation should at least perform an unallocation if passed the last
178     * pointer returned by allocThrow. If findAndReplace() is intended to be
179     * used, unalloc should also be able to unallocate the SkFlatData that is
180     * provided.
181     */
182    virtual void unalloc(void* ptr) = 0;
183
184    /**
185     * Used during creation and unflattening of SkFlatData objects. If the
186     * objects being flattened contain bitmaps they are stored in this heap
187     * and the flattenable stores the index to the bitmap on the heap.
188     * This should be set by the protected setBitmapHeap.
189     */
190    SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; }
191
192    /**
193     * Used during creation of SkFlatData objects. If a typeface recorder is
194     * required to flatten the objects being flattened (i.e. for SkPaints), this
195     * should be set by the protected setTypefaceSet.
196     */
197    SkRefCntSet* getTypefaceSet() { return fTypefaceSet; }
198
199    /**
200     * Used during unflattening of the SkFlatData objects in the
201     * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback
202     * and needs to be reset to the SkRefCntSet passed to setTypefaceSet.
203     */
204    SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; }
205
206    /**
207     * Optional factory recorder used during creation of SkFlatData objects. Set
208     * using the protected method setNamedFactorySet.
209     */
210    SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; }
211
212    /**
213     * Flags to use during creation of SkFlatData objects. Defaults to zero.
214     */
215    uint32_t getWriteBufferFlags() { return fWriteBufferFlags; }
216
217protected:
218    /**
219     * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted.
220     */
221    void setBitmapHeap(SkBitmapHeap*);
222
223    /**
224     * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref
225     * counted.
226     */
227    void setTypefaceSet(SkRefCntSet*);
228
229    /**
230     * Set an SkTypefacePlayback to be used to find references to SkTypefaces
231     * during unflattening. Should be reset to the set provided to
232     * setTypefaceSet.
233     */
234    void setTypefacePlayback(SkTypefacePlayback*);
235
236    /**
237     * Set an SkNamedFactorySet to be used to store Factorys and their
238     * corresponding names during flattening. Ref counted. Returns the same
239     * set as a convenience.
240     */
241    SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*);
242
243    /**
244     * Set the flags to be used during flattening.
245     */
246    void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; }
247
248private:
249    SkBitmapHeap*       fBitmapHeap;
250    SkRefCntSet*        fTypefaceSet;
251    SkTypefacePlayback* fTypefacePlayback;
252    SkNamedFactorySet*  fFactorySet;
253    uint32_t            fWriteBufferFlags;
254
255    typedef SkRefCnt INHERITED;
256};
257
258class SkFlatData {
259public:
260    /**
261     *  Compare two SkFlatData ptrs, returning -1, 0, 1 to allow them to be
262     *  sorted.
263     *
264     *  Note: this assumes that a and b have different sentinel values, either
265     *  InCache or AsCandidate, otherwise the loop will go beyond the end of
266     *  the buffers.
267     *
268     *  dataToCompare() returns 2 fields before the flattened data:
269     *      - checksum
270     *      - size
271     *  This ensures that if we see two blocks of different length, we will
272     *  notice that right away, and not read any further. It also ensures that
273     *  we see the checksum right away, so that most of the time it is enough
274     *  to short-circuit our comparison.
275     */
276    static int Compare(const SkFlatData* a, const SkFlatData* b) {
277        const uint32_t* stop = a->dataStop();
278        const uint32_t* a_ptr = a->dataToCompare() - 1;
279        const uint32_t* b_ptr = b->dataToCompare() - 1;
280        // We use -1 above, so we can pre-increment our pointers in the loop
281        while (*++a_ptr == *++b_ptr) {}
282
283        if (a_ptr == stop) {    // sentinel
284            SkASSERT(b->dataStop() == b_ptr);
285            return 0;
286        }
287        SkASSERT(a_ptr < a->dataStop());
288        SkASSERT(b_ptr < b->dataStop());
289        return (*a_ptr < *b_ptr) ? -1 : 1;
290    }
291
292    int index() const { return fIndex; }
293    const void* data() const { return (const char*)this + sizeof(*this); }
294    void* data() { return (char*)this + sizeof(*this); }
295    // Our data is always 32bit aligned, so we can offer this accessor
296    uint32_t* data32() { return (uint32_t*)this->data(); }
297    // Returns the size of the flattened data.
298    size_t flatSize() const { return fFlatSize; }
299
300    void setSentinelInCache() {
301        this->setSentinel(kInCache_Sentinel);
302    }
303    void setSentinelAsCandidate() {
304        this->setSentinel(kCandidate_Sentinel);
305    }
306
307    uint32_t checksum() const { return fChecksum; }
308
309#ifdef SK_DEBUG_SIZE
310    // returns the logical size of our data. Does not return any sentinel or
311    // padding we might have.
312    size_t size() const {
313        return sizeof(SkFlatData) + fFlatSize;
314    }
315#endif
316
317    static SkFlatData* Create(SkFlatController* controller, const void* obj, int index,
318                              void (*flattenProc)(SkOrderedWriteBuffer&, const void*));
319
320    void unflatten(void* result,
321                   void (*unflattenProc)(SkOrderedReadBuffer&, void*),
322                   SkBitmapHeap* bitmapHeap = NULL,
323                   SkTypefacePlayback* facePlayback = NULL) const;
324
325    // When we purge an entry, we want to reuse an old index for the new entry,
326    // so we expose this setter.
327    void setIndex(int index) { fIndex = index; }
328
329    // for unittesting
330    friend bool operator==(const SkFlatData& a, const SkFlatData& b) {
331        size_t N = (const char*)a.dataStop() - (const char*)a.dataToCompare();
332        return !memcmp(a.dataToCompare(), b.dataToCompare(), N);
333    }
334
335    // returns true if fTopBot[] has been recorded
336    bool isTopBotWritten() const {
337        return !SkScalarIsNaN(fTopBot[0]);
338    }
339
340    // Returns fTopBot array, so it can be passed to a routine to compute them.
341    // For efficiency, we assert that fTopBot have not been recorded yet.
342    SkScalar* writableTopBot() const {
343        SkASSERT(!this->isTopBotWritten());
344        return fTopBot;
345    }
346
347    // return the topbot[] after it has been recorded
348    const SkScalar* topBot() const {
349        SkASSERT(this->isTopBotWritten());
350        return fTopBot;
351    }
352
353private:
354    // This is *not* part of the key for search/sort
355    int fIndex;
356
357    // Cache of paint's FontMetrics fTop,fBottom
358    // initialied to [NaN,NaN] as a sentinel that they have not been recorded yet
359    //
360    // This is *not* part of the key for search/sort
361    mutable SkScalar fTopBot[2];
362
363    // marks fTopBot[] as unrecorded
364    void setTopBotUnwritten() {
365        this->fTopBot[0] = this->fTopBot[1] = SK_ScalarNaN; // initial to sentinel values
366    }
367
368    // From here down is the data we look at in the search/sort. We always begin
369    // with the checksum and then length.
370    uint32_t fChecksum;
371    int32_t  fFlatSize;  // size of flattened data
372    // uint32_t flattenedData[]
373    // uint32_t sentinelValue
374
375    const uint32_t* dataToCompare() const {
376        return (const uint32_t*)&fChecksum;
377    }
378    const uint32_t* dataStop() const {
379        SkASSERT(SkIsAlign4(fFlatSize));
380        return (const uint32_t*)((const char*)this->data() + fFlatSize);
381    }
382
383    enum {
384        kInCache_Sentinel = 0,
385        kCandidate_Sentinel = ~0U,
386    };
387    void setSentinel(uint32_t value) {
388        SkASSERT(SkIsAlign4(fFlatSize));
389        this->data32()[fFlatSize >> 2] = value;
390    }
391};
392
393template <class T>
394class SkFlatDictionary {
395public:
396    SkFlatDictionary(SkFlatController* controller)
397    : fController(controller) {
398        fFlattenProc = NULL;
399        fUnflattenProc = NULL;
400        SkASSERT(controller);
401        fController->ref();
402        // set to 1 since returning a zero from find() indicates failure
403        fNextIndex = 1;
404        sk_bzero(fHash, sizeof(fHash));
405    }
406
407    virtual ~SkFlatDictionary() {
408        fController->unref();
409    }
410
411    int count() const { return fData.count(); }
412
413    const SkFlatData*  operator[](int index) const {
414        SkASSERT(index >= 0 && index < fData.count());
415        return fData[index];
416    }
417
418    /**
419     * Clears the dictionary of all entries. However, it does NOT free the
420     * memory that was allocated for each entry.
421     */
422    void reset() {
423        fData.reset();
424        fNextIndex = 1;
425        sk_bzero(fHash, sizeof(fHash));
426    }
427
428    /**
429     * Similar to find. Allows the caller to specify an SkFlatData to replace in
430     * the case of an add. Also tells the caller whether a new SkFlatData was
431     * added and whether the old one was replaced. The parameters added and
432     * replaced are required to be non-NULL. Rather than returning the index of
433     * the entry in the dictionary, it returns the actual SkFlatData.
434     */
435    const SkFlatData* findAndReplace(const T& element,
436                                     const SkFlatData* toReplace, bool* added,
437                                     bool* replaced) {
438        SkASSERT(added != NULL && replaced != NULL);
439        int oldCount = fData.count();
440        const SkFlatData* flat = this->findAndReturnFlat(element);
441        *added = fData.count() == oldCount + 1;
442        *replaced = false;
443        if (*added && toReplace != NULL) {
444            // First, find the index of the one to replace
445            int indexToReplace = fData.find(toReplace);
446            if (indexToReplace >= 0) {
447                // findAndReturnFlat set the index to fNextIndex and increased
448                // fNextIndex by one. Reuse the index from the one being
449                // replaced and reset fNextIndex to the proper value.
450                const_cast<SkFlatData*>(flat)->setIndex(toReplace->index());
451                fNextIndex--;
452                // Remove from the array.
453                fData.remove(indexToReplace);
454                // Remove from the hash table.
455                int oldHash = ChecksumToHashIndex(toReplace->checksum());
456                if (fHash[oldHash] == toReplace) {
457                    fHash[oldHash] = NULL;
458                }
459                // Delete the actual object.
460                fController->unalloc((void*)toReplace);
461                *replaced = true;
462            }
463        }
464        return flat;
465    }
466
467    /**
468     * Given an element of type T return its 1-based index in the dictionary. If
469     * the element wasn't previously in the dictionary it is automatically
470     * added.
471     *
472     * To make the Compare function fast, we write a sentinel value at the end
473     * of each block. The blocks in our fData[] all have a 0 sentinel. The
474     * newly created block we're comparing against has a -1 in the sentinel.
475     *
476     * This trick allows Compare to always loop until failure. If it fails on
477     * the sentinal value, we know the blocks are equal.
478     */
479    int find(const T& element) {
480        return this->findAndReturnFlat(element)->index();
481    }
482
483    /**
484     *  Unflatten the objects and return them in SkTRefArray, or return NULL
485     *  if there no objects (instead of an empty array).
486     */
487    SkTRefArray<T>* unflattenToArray() const {
488        int count = fData.count();
489        SkTRefArray<T>* array = NULL;
490        if (count > 0) {
491            array = SkTRefArray<T>::Create(count);
492            this->unflattenIntoArray(&array->writableAt(0));
493        }
494        return array;
495    }
496
497    const SkFlatData* findAndReturnFlat(const T& element) {
498        SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc);
499
500        int hashIndex = ChecksumToHashIndex(flat->checksum());
501        const SkFlatData* candidate = fHash[hashIndex];
502        if (candidate && !SkFlatData::Compare(flat, candidate)) {
503            fController->unalloc(flat);
504            return candidate;
505        }
506
507        int index = SkTSearch<SkFlatData>((const SkFlatData**) fData.begin(),
508                                          fData.count(), flat, sizeof(flat),
509                                          &SkFlatData::Compare);
510        if (index >= 0) {
511            fController->unalloc(flat);
512            fHash[hashIndex] = fData[index];
513            return fData[index];
514        }
515
516        index = ~index;
517        *fData.insert(index) = flat;
518        SkASSERT(fData.count() == fNextIndex);
519        fNextIndex++;
520        flat->setSentinelInCache();
521        fHash[hashIndex] = flat;
522        return flat;
523    }
524
525protected:
526    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
527    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
528
529private:
530    void unflattenIntoArray(T* array) const {
531        const int count = fData.count();
532        const SkFlatData** iter = fData.begin();
533        for (int i = 0; i < count; ++i) {
534            const SkFlatData* element = iter[i];
535            int index = element->index() - 1;
536            SkASSERT((unsigned)index < (unsigned)count);
537            element->unflatten(&array[index], fUnflattenProc,
538                               fController->getBitmapHeap(),
539                               fController->getTypefacePlayback());
540        }
541    }
542
543    SkFlatController * const     fController;
544    int                          fNextIndex;
545    SkTDArray<const SkFlatData*> fData;
546
547    enum {
548        // Determined by trying diff values on picture-recording benchmarks
549        // (e.g. PictureRecordBench.cpp), choosing the smallest value that
550        // showed a big improvement. Even better would be to benchmark diff
551        // values on recording representative web-pages or other "real" content.
552        HASH_BITS   = 7,
553        HASH_MASK   = (1 << HASH_BITS) - 1,
554        HASH_COUNT  = 1 << HASH_BITS
555    };
556    const SkFlatData* fHash[HASH_COUNT];
557
558    static int ChecksumToHashIndex(uint32_t checksum) {
559        int n = checksum;
560        if (HASH_BITS < 32) {
561            n ^= n >> 16;
562        }
563        if (HASH_BITS < 16) {
564            n ^= n >> 8;
565        }
566        if (HASH_BITS < 8) {
567            n ^= n >> 4;
568        }
569        return n & HASH_MASK;
570    }
571};
572
573///////////////////////////////////////////////////////////////////////////////
574// Some common dictionaries are defined here for both reference and convenience
575///////////////////////////////////////////////////////////////////////////////
576
577template <class T>
578static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) {
579    ((T*)obj)->flatten(buffer);
580}
581
582template <class T>
583static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) {
584    ((T*)obj)->unflatten(buffer);
585}
586
587class SkChunkFlatController : public SkFlatController {
588public:
589    SkChunkFlatController(size_t minSize)
590    : fHeap(minSize)
591    , fTypefaceSet(SkNEW(SkRefCntSet)) {
592        this->setTypefaceSet(fTypefaceSet);
593        this->setTypefacePlayback(&fTypefacePlayback);
594    }
595
596    ~SkChunkFlatController() {
597        fTypefaceSet->unref();
598    }
599
600    virtual void* allocThrow(size_t bytes) SK_OVERRIDE {
601        return fHeap.allocThrow(bytes);
602    }
603
604    virtual void unalloc(void* ptr) SK_OVERRIDE {
605        (void) fHeap.unalloc(ptr);
606    }
607
608    void setupPlaybacks() const {
609        fTypefacePlayback.reset(fTypefaceSet);
610    }
611
612    void setBitmapStorage(SkBitmapHeap* heap) {
613        this->setBitmapHeap(heap);
614    }
615
616private:
617    SkChunkAlloc               fHeap;
618    SkRefCntSet*               fTypefaceSet;
619    mutable SkTypefacePlayback fTypefacePlayback;
620};
621
622class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> {
623public:
624    SkBitmapDictionary(SkFlatController* controller)
625    : SkFlatDictionary<SkBitmap>(controller) {
626        fFlattenProc = &SkFlattenObjectProc<SkBitmap>;
627        fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>;
628    }
629};
630
631class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> {
632 public:
633    SkMatrixDictionary(SkFlatController* controller)
634    : SkFlatDictionary<SkMatrix>(controller) {
635        fFlattenProc = &flattenMatrix;
636        fUnflattenProc = &unflattenMatrix;
637    }
638
639    static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) {
640        buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj);
641    }
642
643    static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) {
644        buffer.getReader32()->readMatrix((SkMatrix*)obj);
645    }
646};
647
648class SkPaintDictionary : public SkFlatDictionary<SkPaint> {
649 public:
650    SkPaintDictionary(SkFlatController* controller)
651    : SkFlatDictionary<SkPaint>(controller) {
652        fFlattenProc = &SkFlattenObjectProc<SkPaint>;
653        fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
654    }
655};
656
657class SkRegionDictionary : public SkFlatDictionary<SkRegion> {
658 public:
659    SkRegionDictionary(SkFlatController* controller)
660    : SkFlatDictionary<SkRegion>(controller) {
661        fFlattenProc = &flattenRegion;
662        fUnflattenProc = &unflattenRegion;
663    }
664
665    static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) {
666        buffer.getWriter32()->writeRegion(*(SkRegion*)obj);
667    }
668
669    static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) {
670        buffer.getReader32()->readRegion((SkRegion*)obj);
671    }
672};
673
674#endif
675