SkPicturePlayback.cpp revision c3c67dfc73519910f173bd7b4da01b8583040096
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include <new>
8#include "SkBBoxHierarchy.h"
9#include "SkOffsetTable.h"
10#include "SkPicturePlayback.h"
11#include "SkPictureRecord.h"
12#include "SkPictureStateTree.h"
13#include "SkReadBuffer.h"
14#include "SkTypeface.h"
15#include "SkTSort.h"
16#include "SkWriteBuffer.h"
17
18template <typename T> int SafeCount(const T* obj) {
19    return obj ? obj->count() : 0;
20}
21
22/*  Define this to spew out a debug statement whenever we skip the remainder of
23    a save/restore block because a clip... command returned false (empty).
24 */
25#define SPEW_CLIP_SKIPPINGx
26
27SkPicturePlayback::SkPicturePlayback(const SkPictInfo& info) : fInfo(info) {
28    this->init();
29}
30
31SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record,
32                                     const SkPictInfo& info,
33                                     bool deepCopy)
34    : fInfo(info) {
35#ifdef SK_DEBUG_SIZE
36    size_t overallBytes, bitmapBytes, matricesBytes,
37    paintBytes, pathBytes, pictureBytes, regionBytes;
38    int bitmaps = record.bitmaps(&bitmapBytes);
39    int matrices = record.matrices(&matricesBytes);
40    int paints = record.paints(&paintBytes);
41    int paths = record.paths(&pathBytes);
42    int pictures = record.pictures(&pictureBytes);
43    int regions = record.regions(&regionBytes);
44    SkDebugf("picture record mem used %zd (stream %zd) ", record.size(),
45             record.streamlen());
46    if (bitmaps != 0)
47        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
48    if (matrices != 0)
49        SkDebugf("matrices size %zd (matrices:%d) ", matricesBytes, matrices);
50    if (paints != 0)
51        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
52    if (paths != 0)
53        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
54    if (pictures != 0)
55        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
56    if (regions != 0)
57        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
58    if (record.fPointWrites != 0)
59        SkDebugf("points size %zd (points:%d) ", record.fPointBytes, record.fPointWrites);
60    if (record.fRectWrites != 0)
61        SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites);
62    if (record.fTextWrites != 0)
63        SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites);
64
65    SkDebugf("\n");
66#endif
67#ifdef SK_DEBUG_DUMP
68    record.dumpMatrices();
69    record.dumpPaints();
70#endif
71
72    record.validate(record.writeStream().bytesWritten(), 0);
73    const SkWriter32& writer = record.writeStream();
74    this->init();
75    SkASSERT(!fOpData);
76    if (writer.bytesWritten() == 0) {
77        fOpData = SkData::NewEmpty();
78        return;
79    }
80    fOpData = writer.snapshotAsData();
81
82    fBoundingHierarchy = record.fBoundingHierarchy;
83    fStateTree = record.fStateTree;
84
85    SkSafeRef(fBoundingHierarchy);
86    SkSafeRef(fStateTree);
87
88    if (NULL != fBoundingHierarchy) {
89        fBoundingHierarchy->flushDeferredInserts();
90    }
91
92    // copy over the refcnt dictionary to our reader
93    record.fFlattenableHeap.setupPlaybacks();
94
95    fBitmaps = record.fBitmapHeap->extractBitmaps();
96    fPaints = record.fPaints.unflattenToArray();
97
98    fBitmapHeap.reset(SkSafeRef(record.fBitmapHeap));
99    fPathHeap.reset(SkSafeRef(record.fPathHeap));
100
101    fBitmapUseOffsets.reset(SkSafeRef(record.fBitmapUseOffsets.get()));
102
103    // ensure that the paths bounds are pre-computed
104    if (fPathHeap.get()) {
105        for (int i = 0; i < fPathHeap->count(); i++) {
106            (*fPathHeap)[i].updateBoundsCache();
107        }
108    }
109
110    const SkTDArray<SkPicture* >& pictures = record.getPictureRefs();
111    fPictureCount = pictures.count();
112    if (fPictureCount > 0) {
113        fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
114        for (int i = 0; i < fPictureCount; i++) {
115            if (deepCopy) {
116                fPictureRefs[i] = pictures[i]->clone();
117            } else {
118                fPictureRefs[i] = pictures[i];
119                fPictureRefs[i]->ref();
120            }
121        }
122    }
123
124#ifdef SK_DEBUG_SIZE
125    int overall = fPlayback->size(&overallBytes);
126    bitmaps = fPlayback->bitmaps(&bitmapBytes);
127    paints = fPlayback->paints(&paintBytes);
128    paths = fPlayback->paths(&pathBytes);
129    pictures = fPlayback->pictures(&pictureBytes);
130    regions = fPlayback->regions(&regionBytes);
131    SkDebugf("playback size %zd (objects:%d) ", overallBytes, overall);
132    if (bitmaps != 0)
133        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
134    if (paints != 0)
135        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
136    if (paths != 0)
137        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
138    if (pictures != 0)
139        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
140    if (regions != 0)
141        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
142    SkDebugf("\n");
143#endif
144}
145
146static bool needs_deep_copy(const SkPaint& paint) {
147    /*
148     *  These fields are known to be immutable, and so can be shallow-copied
149     *
150     *  getTypeface()
151     *  getAnnotation()
152     *  paint.getColorFilter()
153     *  getXfermode()
154     *  getPathEffect()
155     *  getMaskFilter()
156     */
157
158    return paint.getShader() ||
159#ifdef SK_SUPPORT_LEGACY_LAYERRASTERIZER_API
160           paint.getRasterizer() ||
161#endif
162           paint.getLooper() || // needs to hide its addLayer...
163           paint.getImageFilter();
164}
165
166SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInfo* deepCopyInfo)
167    : fInfo(src.fInfo) {
168    this->init();
169
170    fBitmapHeap.reset(SkSafeRef(src.fBitmapHeap.get()));
171    fPathHeap.reset(SkSafeRef(src.fPathHeap.get()));
172
173    fOpData = SkSafeRef(src.fOpData);
174
175    fBoundingHierarchy = src.fBoundingHierarchy;
176    fStateTree = src.fStateTree;
177
178    SkSafeRef(fBoundingHierarchy);
179    SkSafeRef(fStateTree);
180
181    if (deepCopyInfo) {
182        int paintCount = SafeCount(src.fPaints);
183
184        if (src.fBitmaps) {
185            fBitmaps = SkTRefArray<SkBitmap>::Create(src.fBitmaps->begin(), src.fBitmaps->count());
186        }
187
188        if (!deepCopyInfo->initialized) {
189            /* The alternative to doing this is to have a clone method on the paint and have it make
190             * the deep copy of its internal structures as needed. The holdup to doing that is at
191             * this point we would need to pass the SkBitmapHeap so that we don't unnecessarily
192             * flatten the pixels in a bitmap shader.
193             */
194            deepCopyInfo->paintData.setCount(paintCount);
195
196            /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is one,
197             * use it. If this SkPicturePlayback was created from a stream, fBitmapHeap will be
198             * NULL, so create a new one.
199             */
200            if (fBitmapHeap.get() == NULL) {
201                // FIXME: Put this on the stack inside SkPicture::clone. Further, is it possible to
202                // do the rest of this initialization in SkPicture::clone as well?
203                SkBitmapHeap* heap = SkNEW(SkBitmapHeap);
204                deepCopyInfo->controller.setBitmapStorage(heap);
205                heap->unref();
206            } else {
207                deepCopyInfo->controller.setBitmapStorage(fBitmapHeap);
208            }
209
210            SkDEBUGCODE(int heapSize = SafeCount(fBitmapHeap.get());)
211            for (int i = 0; i < paintCount; i++) {
212                if (needs_deep_copy(src.fPaints->at(i))) {
213                    deepCopyInfo->paintData[i] =
214                        SkFlatData::Create<SkPaint::FlatteningTraits>(&deepCopyInfo->controller,
215                                                          src.fPaints->at(i), 0);
216
217                } else {
218                    // this is our sentinel, which we use in the unflatten loop
219                    deepCopyInfo->paintData[i] = NULL;
220                }
221            }
222            SkASSERT(SafeCount(fBitmapHeap.get()) == heapSize);
223
224            // needed to create typeface playback
225            deepCopyInfo->controller.setupPlaybacks();
226            deepCopyInfo->initialized = true;
227        }
228
229        fPaints = SkTRefArray<SkPaint>::Create(paintCount);
230        SkASSERT(deepCopyInfo->paintData.count() == paintCount);
231        SkBitmapHeap* bmHeap = deepCopyInfo->controller.getBitmapHeap();
232        SkTypefacePlayback* tfPlayback = deepCopyInfo->controller.getTypefacePlayback();
233        for (int i = 0; i < paintCount; i++) {
234            if (deepCopyInfo->paintData[i]) {
235                deepCopyInfo->paintData[i]->unflatten<SkPaint::FlatteningTraits>(
236                    &fPaints->writableAt(i), bmHeap, tfPlayback);
237            } else {
238                // needs_deep_copy was false, so just need to assign
239                fPaints->writableAt(i) = src.fPaints->at(i);
240            }
241        }
242
243    } else {
244        fBitmaps = SkSafeRef(src.fBitmaps);
245        fPaints = SkSafeRef(src.fPaints);
246    }
247
248    fPictureCount = src.fPictureCount;
249    fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
250    for (int i = 0; i < fPictureCount; i++) {
251        if (deepCopyInfo) {
252            fPictureRefs[i] = src.fPictureRefs[i]->clone();
253        } else {
254            fPictureRefs[i] = src.fPictureRefs[i];
255            fPictureRefs[i]->ref();
256        }
257    }
258}
259
260void SkPicturePlayback::init() {
261    fBitmaps = NULL;
262    fPaints = NULL;
263    fPictureRefs = NULL;
264    fPictureCount = 0;
265    fOpData = NULL;
266    fFactoryPlayback = NULL;
267    fBoundingHierarchy = NULL;
268    fStateTree = NULL;
269    fCachedActiveOps = NULL;
270    fCurOffset = 0;
271}
272
273SkPicturePlayback::~SkPicturePlayback() {
274    SkSafeUnref(fOpData);
275
276    SkSafeUnref(fBitmaps);
277    SkSafeUnref(fPaints);
278    SkSafeUnref(fBoundingHierarchy);
279    SkSafeUnref(fStateTree);
280
281    SkDELETE(fCachedActiveOps);
282
283    for (int i = 0; i < fPictureCount; i++) {
284        fPictureRefs[i]->unref();
285    }
286    SkDELETE_ARRAY(fPictureRefs);
287
288    SkDELETE(fFactoryPlayback);
289}
290
291void SkPicturePlayback::dumpSize() const {
292    SkDebugf("--- picture size: ops=%d bitmaps=%d [%d] paints=%d [%d] paths=%d\n",
293             fOpData->size(),
294             SafeCount(fBitmaps), SafeCount(fBitmaps) * sizeof(SkBitmap),
295             SafeCount(fPaints), SafeCount(fPaints) * sizeof(SkPaint),
296             SafeCount(fPathHeap.get()));
297}
298
299bool SkPicturePlayback::containsBitmaps() const {
300    if (fBitmaps && fBitmaps->count() > 0) {
301        return true;
302    }
303    for (int i = 0; i < fPictureCount; ++i) {
304        if (fPictureRefs[i]->willPlayBackBitmaps()) {
305            return true;
306        }
307    }
308    return false;
309}
310
311///////////////////////////////////////////////////////////////////////////////
312///////////////////////////////////////////////////////////////////////////////
313
314#include "SkStream.h"
315
316static void write_tag_size(SkWriteBuffer& buffer, uint32_t tag, uint32_t size) {
317    buffer.writeUInt(tag);
318    buffer.writeUInt(size);
319}
320
321static void write_tag_size(SkWStream* stream, uint32_t tag,  uint32_t size) {
322    stream->write32(tag);
323    stream->write32(size);
324}
325
326static size_t compute_chunk_size(SkFlattenable::Factory* array, int count) {
327    size_t size = 4;  // for 'count'
328
329    for (int i = 0; i < count; i++) {
330        const char* name = SkFlattenable::FactoryToName(array[i]);
331        if (NULL == name || 0 == *name) {
332            size += SkWStream::SizeOfPackedUInt(0);
333        } else {
334            size_t len = strlen(name);
335            size += SkWStream::SizeOfPackedUInt(len);
336            size += len;
337        }
338    }
339
340    return size;
341}
342
343static void write_factories(SkWStream* stream, const SkFactorySet& rec) {
344    int count = rec.count();
345
346    SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count);
347    SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get();
348    rec.copyToArray(array);
349
350    size_t size = compute_chunk_size(array, count);
351
352    // TODO: write_tag_size should really take a size_t
353    write_tag_size(stream, SK_PICT_FACTORY_TAG, (uint32_t) size);
354    SkDEBUGCODE(size_t start = stream->bytesWritten());
355    stream->write32(count);
356
357    for (int i = 0; i < count; i++) {
358        const char* name = SkFlattenable::FactoryToName(array[i]);
359//        SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name);
360        if (NULL == name || 0 == *name) {
361            stream->writePackedUInt(0);
362        } else {
363            uint32_t len = strlen(name);
364            stream->writePackedUInt(len);
365            stream->write(name, len);
366        }
367    }
368
369    SkASSERT(size == (stream->bytesWritten() - start));
370}
371
372static void write_typefaces(SkWStream* stream, const SkRefCntSet& rec) {
373    int count = rec.count();
374
375    write_tag_size(stream, SK_PICT_TYPEFACE_TAG, count);
376
377    SkAutoSTMalloc<16, SkTypeface*> storage(count);
378    SkTypeface** array = (SkTypeface**)storage.get();
379    rec.copyToArray((SkRefCnt**)array);
380
381    for (int i = 0; i < count; i++) {
382        array[i]->serialize(stream);
383    }
384}
385
386void SkPicturePlayback::flattenToBuffer(SkWriteBuffer& buffer) const {
387    int i, n;
388
389    if ((n = SafeCount(fBitmaps)) > 0) {
390        write_tag_size(buffer, SK_PICT_BITMAP_BUFFER_TAG, n);
391        for (i = 0; i < n; i++) {
392            buffer.writeBitmap((*fBitmaps)[i]);
393        }
394    }
395
396    if ((n = SafeCount(fPaints)) > 0) {
397        write_tag_size(buffer, SK_PICT_PAINT_BUFFER_TAG, n);
398        for (i = 0; i < n; i++) {
399            buffer.writePaint((*fPaints)[i]);
400        }
401    }
402
403    if ((n = SafeCount(fPathHeap.get())) > 0) {
404        write_tag_size(buffer, SK_PICT_PATH_BUFFER_TAG, n);
405        fPathHeap->flatten(buffer);
406    }
407}
408
409void SkPicturePlayback::serialize(SkWStream* stream,
410                                  SkPicture::EncodeBitmap encoder) const {
411    write_tag_size(stream, SK_PICT_READER_TAG, fOpData->size());
412    stream->write(fOpData->bytes(), fOpData->size());
413
414    if (fPictureCount > 0) {
415        write_tag_size(stream, SK_PICT_PICTURE_TAG, fPictureCount);
416        for (int i = 0; i < fPictureCount; i++) {
417            fPictureRefs[i]->serialize(stream, encoder);
418        }
419    }
420
421    // Write some of our data into a writebuffer, and then serialize that
422    // into our stream
423    {
424        SkRefCntSet  typefaceSet;
425        SkFactorySet factSet;
426
427        SkWriteBuffer buffer(SkWriteBuffer::kCrossProcess_Flag);
428        buffer.setTypefaceRecorder(&typefaceSet);
429        buffer.setFactoryRecorder(&factSet);
430        buffer.setBitmapEncoder(encoder);
431
432        this->flattenToBuffer(buffer);
433
434        // We have to write these two sets into the stream *before* we write
435        // the buffer, since parsing that buffer will require that we already
436        // have these sets available to use.
437        write_factories(stream, factSet);
438        write_typefaces(stream, typefaceSet);
439
440        write_tag_size(stream, SK_PICT_BUFFER_SIZE_TAG, buffer.bytesWritten());
441        buffer.writeToStream(stream);
442    }
443
444    stream->write32(SK_PICT_EOF_TAG);
445}
446
447void SkPicturePlayback::flatten(SkWriteBuffer& buffer) const {
448    write_tag_size(buffer, SK_PICT_READER_TAG, fOpData->size());
449    buffer.writeByteArray(fOpData->bytes(), fOpData->size());
450
451    if (fPictureCount > 0) {
452        write_tag_size(buffer, SK_PICT_PICTURE_TAG, fPictureCount);
453        for (int i = 0; i < fPictureCount; i++) {
454            fPictureRefs[i]->flatten(buffer);
455        }
456    }
457
458    // Write this picture playback's data into a writebuffer
459    this->flattenToBuffer(buffer);
460    buffer.write32(SK_PICT_EOF_TAG);
461}
462
463///////////////////////////////////////////////////////////////////////////////
464
465/**
466 *  Return the corresponding SkReadBuffer flags, given a set of
467 *  SkPictInfo flags.
468 */
469static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) {
470    static const struct {
471        uint32_t    fSrc;
472        uint32_t    fDst;
473    } gSD[] = {
474        { SkPictInfo::kCrossProcess_Flag,   SkReadBuffer::kCrossProcess_Flag },
475        { SkPictInfo::kScalarIsFloat_Flag,  SkReadBuffer::kScalarIsFloat_Flag },
476        { SkPictInfo::kPtrIs64Bit_Flag,     SkReadBuffer::kPtrIs64Bit_Flag },
477    };
478
479    uint32_t rbMask = 0;
480    for (size_t i = 0; i < SK_ARRAY_COUNT(gSD); ++i) {
481        if (pictInfoFlags & gSD[i].fSrc) {
482            rbMask |= gSD[i].fDst;
483        }
484    }
485    return rbMask;
486}
487
488bool SkPicturePlayback::parseStreamTag(SkStream* stream,
489                                       uint32_t tag,
490                                       size_t size,
491                                       SkPicture::InstallPixelRefProc proc) {
492    /*
493     *  By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
494     *  its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
495     *  but if they are present, they need to have been seen before the buffer.
496     *
497     *  We assert that if/when we see either of these, that we have not yet seen
498     *  the buffer tag, because if we have, then its too-late to deal with the
499     *  factories or typefaces.
500     */
501    SkDEBUGCODE(bool haveBuffer = false;)
502
503    switch (tag) {
504        case SK_PICT_READER_TAG: {
505            SkAutoMalloc storage(size);
506            if (stream->read(storage.get(), size) != size) {
507                return false;
508            }
509            SkASSERT(NULL == fOpData);
510            fOpData = SkData::NewFromMalloc(storage.detach(), size);
511        } break;
512        case SK_PICT_FACTORY_TAG: {
513            SkASSERT(!haveBuffer);
514        // Remove this code when v21 and below are no longer supported. At the
515        // same time add a new 'count' variable and use it rather then reusing 'size'.
516#ifndef DISABLE_V21_COMPATIBILITY_CODE
517            if (fInfo.fVersion >= 22) {
518                // in v22 this tag's size represents the size of the chunk in bytes
519                // and the number of factory strings is written out separately
520#endif
521                size = stream->readU32();
522#ifndef DISABLE_V21_COMPATIBILITY_CODE
523            }
524#endif
525            fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (size));
526            for (size_t i = 0; i < size; i++) {
527                SkString str;
528                const size_t len = stream->readPackedUInt();
529                str.resize(len);
530                if (stream->read(str.writable_str(), len) != len) {
531                    return false;
532                }
533                fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
534            }
535        } break;
536        case SK_PICT_TYPEFACE_TAG: {
537            SkASSERT(!haveBuffer);
538            fTFPlayback.setCount(size);
539            for (size_t i = 0; i < size; i++) {
540                SkAutoTUnref<SkTypeface> tf(SkTypeface::Deserialize(stream));
541                if (!tf.get()) {    // failed to deserialize
542                    // fTFPlayback asserts it never has a null, so we plop in
543                    // the default here.
544                    tf.reset(SkTypeface::RefDefault());
545                }
546                fTFPlayback.set(i, tf);
547            }
548        } break;
549        case SK_PICT_PICTURE_TAG: {
550            fPictureCount = size;
551            fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
552            bool success = true;
553            int i = 0;
554            for ( ; i < fPictureCount; i++) {
555                fPictureRefs[i] = SkPicture::CreateFromStream(stream, proc);
556                if (NULL == fPictureRefs[i]) {
557                    success = false;
558                    break;
559                }
560            }
561            if (!success) {
562                // Delete all of the pictures that were already created (up to but excluding i):
563                for (int j = 0; j < i; j++) {
564                    fPictureRefs[j]->unref();
565                }
566                // Delete the array
567                SkDELETE_ARRAY(fPictureRefs);
568                fPictureCount = 0;
569                return false;
570            }
571        } break;
572        case SK_PICT_BUFFER_SIZE_TAG: {
573            SkAutoMalloc storage(size);
574            if (stream->read(storage.get(), size) != size) {
575                return false;
576            }
577
578            SkReadBuffer buffer(storage.get(), size);
579            buffer.setFlags(pictInfoFlagsToReadBufferFlags(fInfo.fFlags));
580            buffer.setPictureVersion(fInfo.fVersion);
581
582            fFactoryPlayback->setupBuffer(buffer);
583            fTFPlayback.setupBuffer(buffer);
584            buffer.setBitmapDecoder(proc);
585
586            while (!buffer.eof()) {
587                tag = buffer.readUInt();
588                size = buffer.readUInt();
589                if (!this->parseBufferTag(buffer, tag, size)) {
590                    return false;
591                }
592            }
593            SkDEBUGCODE(haveBuffer = true;)
594        } break;
595    }
596    return true;    // success
597}
598
599bool SkPicturePlayback::parseBufferTag(SkReadBuffer& buffer,
600                                       uint32_t tag, size_t size) {
601    switch (tag) {
602        case SK_PICT_BITMAP_BUFFER_TAG: {
603            fBitmaps = SkTRefArray<SkBitmap>::Create(size);
604            for (size_t i = 0; i < size; ++i) {
605                SkBitmap* bm = &fBitmaps->writableAt(i);
606                buffer.readBitmap(bm);
607                bm->setImmutable();
608            }
609        } break;
610        case SK_PICT_PAINT_BUFFER_TAG: {
611            fPaints = SkTRefArray<SkPaint>::Create(size);
612            for (size_t i = 0; i < size; ++i) {
613                buffer.readPaint(&fPaints->writableAt(i));
614            }
615        } break;
616        case SK_PICT_PATH_BUFFER_TAG:
617            if (size > 0) {
618                fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer)));
619            }
620            break;
621        case SK_PICT_READER_TAG: {
622            SkAutoMalloc storage(size);
623            if (!buffer.readByteArray(storage.get(), size) ||
624                !buffer.validate(NULL == fOpData)) {
625                return false;
626            }
627            SkASSERT(NULL == fOpData);
628            fOpData = SkData::NewFromMalloc(storage.detach(), size);
629        } break;
630        case SK_PICT_PICTURE_TAG: {
631            if (!buffer.validate((0 == fPictureCount) && (NULL == fPictureRefs))) {
632                return false;
633            }
634            fPictureCount = size;
635            fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
636            bool success = true;
637            int i = 0;
638            for ( ; i < fPictureCount; i++) {
639                fPictureRefs[i] = SkPicture::CreateFromBuffer(buffer);
640                if (NULL == fPictureRefs[i]) {
641                    success = false;
642                    break;
643                }
644            }
645            if (!success) {
646                // Delete all of the pictures that were already created (up to but excluding i):
647                for (int j = 0; j < i; j++) {
648                    fPictureRefs[j]->unref();
649                }
650                // Delete the array
651                SkDELETE_ARRAY(fPictureRefs);
652                fPictureCount = 0;
653                return false;
654            }
655        } break;
656        default:
657            // The tag was invalid.
658            return false;
659    }
660    return true;    // success
661}
662
663SkPicturePlayback* SkPicturePlayback::CreateFromStream(SkStream* stream,
664                                                       const SkPictInfo& info,
665                                                       SkPicture::InstallPixelRefProc proc) {
666    SkAutoTDelete<SkPicturePlayback> playback(SkNEW_ARGS(SkPicturePlayback, (info)));
667
668    if (!playback->parseStream(stream, proc)) {
669        return NULL;
670    }
671    return playback.detach();
672}
673
674SkPicturePlayback* SkPicturePlayback::CreateFromBuffer(SkReadBuffer& buffer,
675                                                       const SkPictInfo& info) {
676    SkAutoTDelete<SkPicturePlayback> playback(SkNEW_ARGS(SkPicturePlayback, (info)));
677    buffer.setPictureVersion(info.fVersion);
678
679    if (!playback->parseBuffer(buffer)) {
680        return NULL;
681    }
682    return playback.detach();
683}
684
685bool SkPicturePlayback::parseStream(SkStream* stream,
686                                    SkPicture::InstallPixelRefProc proc) {
687    for (;;) {
688        uint32_t tag = stream->readU32();
689        if (SK_PICT_EOF_TAG == tag) {
690            break;
691        }
692
693        uint32_t size = stream->readU32();
694        if (!this->parseStreamTag(stream, tag, size, proc)) {
695            return false; // we're invalid
696        }
697    }
698    return true;
699}
700
701bool SkPicturePlayback::parseBuffer(SkReadBuffer& buffer) {
702    for (;;) {
703        uint32_t tag = buffer.readUInt();
704        if (SK_PICT_EOF_TAG == tag) {
705            break;
706        }
707
708        uint32_t size = buffer.readUInt();
709        if (!this->parseBufferTag(buffer, tag, size)) {
710            return false; // we're invalid
711        }
712    }
713    return true;
714}
715
716///////////////////////////////////////////////////////////////////////////////
717///////////////////////////////////////////////////////////////////////////////
718
719#ifdef SPEW_CLIP_SKIPPING
720struct SkipClipRec {
721    int     fCount;
722    size_t  fSize;
723
724    SkipClipRec() {
725        fCount = 0;
726        fSize = 0;
727    }
728
729    void recordSkip(size_t bytes) {
730        fCount += 1;
731        fSize += bytes;
732    }
733};
734#endif
735
736#ifdef SK_DEVELOPER
737bool SkPicturePlayback::preDraw(int opIndex, int type) {
738    return false;
739}
740
741void SkPicturePlayback::postDraw(int opIndex) {
742}
743#endif
744
745/*
746 * Read the next op code and chunk size from 'reader'. The returned size
747 * is the entire size of the chunk (including the opcode). Thus, the
748 * offset just prior to calling read_op_and_size + 'size' is the offset
749 * to the next chunk's op code. This also means that the size of a chunk
750 * with no arguments (just an opcode) will be 4.
751 */
752static DrawType read_op_and_size(SkReader32* reader, uint32_t* size) {
753    uint32_t temp = reader->readInt();
754    uint32_t op;
755    if (((uint8_t) temp) == temp) {
756        // old skp file - no size information
757        op = temp;
758        *size = 0;
759    } else {
760        UNPACK_8_24(temp, op, *size);
761        if (MASK_24 == *size) {
762            *size = reader->readInt();
763        }
764    }
765    return (DrawType) op;
766}
767
768uint32_t SkPicturePlayback::CachedOperationList::offset(int index) const {
769    SkASSERT(index < fOps.count());
770    return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
771}
772
773const SkMatrix& SkPicturePlayback::CachedOperationList::matrix(int index) const {
774    SkASSERT(index < fOps.count());
775    return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
776}
777
778const SkPicture::OperationList& SkPicturePlayback::getActiveOps(const SkIRect& query) {
779    if (NULL == fStateTree || NULL == fBoundingHierarchy) {
780        return SkPicture::OperationList::InvalidList();
781    }
782
783    if (NULL == fCachedActiveOps) {
784        fCachedActiveOps = SkNEW(CachedOperationList);
785    }
786
787    if (query == fCachedActiveOps->fCacheQueryRect) {
788        return *fCachedActiveOps;
789    }
790
791    fCachedActiveOps->fOps.rewind();
792
793    fBoundingHierarchy->search(query, &(fCachedActiveOps->fOps));
794    if (0 != fCachedActiveOps->fOps.count()) {
795        SkTQSort<SkPictureStateTree::Draw>(
796            reinterpret_cast<SkPictureStateTree::Draw**>(fCachedActiveOps->fOps.begin()),
797            reinterpret_cast<SkPictureStateTree::Draw**>(fCachedActiveOps->fOps.end()-1));
798    }
799
800    fCachedActiveOps->fCacheQueryRect = query;
801    return *fCachedActiveOps;
802}
803
804class SkAutoResetOpID {
805public:
806    SkAutoResetOpID(SkPicturePlayback* playback) : fPlayback(playback) { }
807    ~SkAutoResetOpID() {
808        if (NULL != fPlayback) {
809            fPlayback->resetOpID();
810        }
811    }
812
813private:
814    SkPicturePlayback* fPlayback;
815};
816
817void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) {
818    SkAutoResetOpID aroi(this);
819    SkASSERT(0 == fCurOffset);
820
821#ifdef ENABLE_TIME_DRAW
822    SkAutoTime  at("SkPicture::draw", 50);
823#endif
824
825#ifdef SPEW_CLIP_SKIPPING
826    SkipClipRec skipRect, skipRRect, skipRegion, skipPath, skipCull;
827    int opCount = 0;
828#endif
829
830#ifdef SK_BUILD_FOR_ANDROID
831    SkAutoMutexAcquire autoMutex(fDrawMutex);
832#endif
833
834    // kDrawComplete will be the signal that we have reached the end of
835    // the command stream
836    static const uint32_t kDrawComplete = SK_MaxU32;
837
838    SkReader32 reader(fOpData->bytes(), fOpData->size());
839    TextContainer text;
840    const SkTDArray<void*>* activeOps = NULL;
841
842    if (NULL != fStateTree && NULL != fBoundingHierarchy) {
843        SkRect clipBounds;
844        if (canvas.getClipBounds(&clipBounds)) {
845            SkIRect query;
846            clipBounds.roundOut(&query);
847
848            const SkPicture::OperationList& activeOpsList = this->getActiveOps(query);
849            if (activeOpsList.valid()) {
850                if (0 == activeOpsList.numOps()) {
851                    return;     // nothing to draw
852                }
853
854                // Since the opList is valid we know it is our derived class
855                activeOps = &((const CachedOperationList&)activeOpsList).fOps;
856            }
857        }
858    }
859
860    SkPictureStateTree::Iterator it = (NULL == activeOps) ?
861        SkPictureStateTree::Iterator() :
862        fStateTree->getIterator(*activeOps, &canvas);
863
864    if (it.isValid()) {
865        uint32_t skipTo = it.draw();
866        if (kDrawComplete == skipTo) {
867            return;
868        }
869        reader.setOffset(skipTo);
870    }
871
872    // Record this, so we can concat w/ it if we encounter a setMatrix()
873    SkMatrix initialMatrix = canvas.getTotalMatrix();
874    int originalSaveCount = canvas.getSaveCount();
875
876#ifdef SK_BUILD_FOR_ANDROID
877    fAbortCurrentPlayback = false;
878#endif
879
880#ifdef SK_DEVELOPER
881    int opIndex = -1;
882#endif
883
884    while (!reader.eof()) {
885        if (callback && callback->abortDrawing()) {
886            canvas.restoreToCount(originalSaveCount);
887            return;
888        }
889#ifdef SK_BUILD_FOR_ANDROID
890        if (fAbortCurrentPlayback) {
891            return;
892        }
893#endif
894
895#ifdef SPEW_CLIP_SKIPPING
896        opCount++;
897#endif
898
899        fCurOffset = reader.offset();
900        uint32_t size;
901        DrawType op = read_op_and_size(&reader, &size);
902        size_t skipTo = 0;
903        if (NOOP == op) {
904            // NOOPs are to be ignored - do not propagate them any further
905            skipTo = fCurOffset + size;
906#ifdef SK_DEVELOPER
907        } else {
908            opIndex++;
909            if (this->preDraw(opIndex, op)) {
910                skipTo = fCurOffset + size;
911            }
912#endif
913        }
914
915        if (0 != skipTo) {
916            if (it.isValid()) {
917                // If using a bounding box hierarchy, advance the state tree
918                // iterator until at or after skipTo
919                uint32_t adjustedSkipTo;
920                do {
921                    adjustedSkipTo = it.draw();
922                } while (adjustedSkipTo < skipTo);
923                skipTo = adjustedSkipTo;
924            }
925            if (kDrawComplete == skipTo) {
926                break;
927            }
928            reader.setOffset(skipTo);
929            continue;
930        }
931
932        switch (op) {
933            case CLIP_PATH: {
934                const SkPath& path = getPath(reader);
935                uint32_t packed = reader.readInt();
936                SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
937                bool doAA = ClipParams_unpackDoAA(packed);
938                size_t offsetToRestore = reader.readInt();
939                SkASSERT(!offsetToRestore || \
940                    offsetToRestore >= reader.offset());
941                canvas.clipPath(path, regionOp, doAA);
942                if (canvas.isClipEmpty() && offsetToRestore) {
943#ifdef SPEW_CLIP_SKIPPING
944                    skipPath.recordSkip(offsetToRestore - reader.offset());
945#endif
946                    reader.setOffset(offsetToRestore);
947                }
948            } break;
949            case CLIP_REGION: {
950                SkRegion region;
951                this->getRegion(reader, &region);
952                uint32_t packed = reader.readInt();
953                SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
954                size_t offsetToRestore = reader.readInt();
955                SkASSERT(!offsetToRestore || \
956                    offsetToRestore >= reader.offset());
957                canvas.clipRegion(region, regionOp);
958                if (canvas.isClipEmpty() && offsetToRestore) {
959#ifdef SPEW_CLIP_SKIPPING
960                    skipRegion.recordSkip(offsetToRestore - reader.offset());
961#endif
962                    reader.setOffset(offsetToRestore);
963                }
964            } break;
965            case CLIP_RECT: {
966                const SkRect& rect = reader.skipT<SkRect>();
967                uint32_t packed = reader.readInt();
968                SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
969                bool doAA = ClipParams_unpackDoAA(packed);
970                size_t offsetToRestore = reader.readInt();
971                SkASSERT(!offsetToRestore || \
972                         offsetToRestore >= reader.offset());
973                canvas.clipRect(rect, regionOp, doAA);
974                if (canvas.isClipEmpty() && offsetToRestore) {
975#ifdef SPEW_CLIP_SKIPPING
976                    skipRect.recordSkip(offsetToRestore - reader.offset());
977#endif
978                    reader.setOffset(offsetToRestore);
979                }
980            } break;
981            case CLIP_RRECT: {
982                SkRRect rrect;
983                reader.readRRect(&rrect);
984                uint32_t packed = reader.readInt();
985                SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
986                bool doAA = ClipParams_unpackDoAA(packed);
987                size_t offsetToRestore = reader.readInt();
988                SkASSERT(!offsetToRestore || \
989                         offsetToRestore >= reader.offset());
990                canvas.clipRRect(rrect, regionOp, doAA);
991                if (canvas.isClipEmpty() && offsetToRestore) {
992#ifdef SPEW_CLIP_SKIPPING
993                    skipRRect.recordSkip(offsetToRestore - reader.offset());
994#endif
995                    reader.setOffset(offsetToRestore);
996                }
997            } break;
998            case PUSH_CULL: {
999                const SkRect& cullRect = reader.skipT<SkRect>();
1000                size_t offsetToRestore = reader.readInt();
1001                if (offsetToRestore && canvas.quickReject(cullRect)) {
1002#ifdef SPEW_CLIP_SKIPPING
1003                    skipCull.recordSkip(offsetToRestore - reader.offset());
1004#endif
1005                    reader.setOffset(offsetToRestore);
1006                } else {
1007                    canvas.pushCull(cullRect);
1008                }
1009            } break;
1010            case POP_CULL:
1011                canvas.popCull();
1012                break;
1013            case CONCAT: {
1014                SkMatrix matrix;
1015                this->getMatrix(reader, &matrix);
1016                canvas.concat(matrix);
1017                break;
1018            }
1019            case DRAW_BITMAP: {
1020                const SkPaint* paint = this->getPaint(reader);
1021                const SkBitmap& bitmap = this->getBitmap(reader);
1022                const SkPoint& loc = reader.skipT<SkPoint>();
1023                canvas.drawBitmap(bitmap, loc.fX, loc.fY, paint);
1024            } break;
1025            case DRAW_BITMAP_RECT_TO_RECT: {
1026                const SkPaint* paint = this->getPaint(reader);
1027                const SkBitmap& bitmap = this->getBitmap(reader);
1028                const SkRect* src = this->getRectPtr(reader);   // may be null
1029                const SkRect& dst = reader.skipT<SkRect>();     // required
1030                SkCanvas::DrawBitmapRectFlags flags;
1031                flags = (SkCanvas::DrawBitmapRectFlags) reader.readInt();
1032                canvas.drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1033            } break;
1034            case DRAW_BITMAP_MATRIX: {
1035                const SkPaint* paint = this->getPaint(reader);
1036                const SkBitmap& bitmap = this->getBitmap(reader);
1037                SkMatrix matrix;
1038                this->getMatrix(reader, &matrix);
1039                canvas.drawBitmapMatrix(bitmap, matrix, paint);
1040            } break;
1041            case DRAW_BITMAP_NINE: {
1042                const SkPaint* paint = this->getPaint(reader);
1043                const SkBitmap& bitmap = this->getBitmap(reader);
1044                const SkIRect& src = reader.skipT<SkIRect>();
1045                const SkRect& dst = reader.skipT<SkRect>();
1046                canvas.drawBitmapNine(bitmap, src, dst, paint);
1047            } break;
1048            case DRAW_CLEAR:
1049                canvas.clear(reader.readInt());
1050                break;
1051            case DRAW_DATA: {
1052                size_t length = reader.readInt();
1053                canvas.drawData(reader.skip(length), length);
1054                // skip handles padding the read out to a multiple of 4
1055            } break;
1056            case DRAW_DRRECT: {
1057                const SkPaint& paint = *this->getPaint(reader);
1058                SkRRect outer, inner;
1059                reader.readRRect(&outer);
1060                reader.readRRect(&inner);
1061                canvas.drawDRRect(outer, inner, paint);
1062            } break;
1063            case BEGIN_COMMENT_GROUP: {
1064                const char* desc = reader.readString();
1065                canvas.beginCommentGroup(desc);
1066            } break;
1067            case COMMENT: {
1068                const char* kywd = reader.readString();
1069                const char* value = reader.readString();
1070                canvas.addComment(kywd, value);
1071            } break;
1072            case END_COMMENT_GROUP: {
1073                canvas.endCommentGroup();
1074            } break;
1075            case DRAW_OVAL: {
1076                const SkPaint& paint = *this->getPaint(reader);
1077                canvas.drawOval(reader.skipT<SkRect>(), paint);
1078            } break;
1079            case DRAW_PAINT:
1080                canvas.drawPaint(*this->getPaint(reader));
1081                break;
1082            case DRAW_PATH: {
1083                const SkPaint& paint = *this->getPaint(reader);
1084                canvas.drawPath(getPath(reader), paint);
1085            } break;
1086            case DRAW_PICTURE:
1087                canvas.drawPicture(this->getPicture(reader));
1088                break;
1089            case DRAW_POINTS: {
1090                const SkPaint& paint = *this->getPaint(reader);
1091                SkCanvas::PointMode mode = (SkCanvas::PointMode)reader.readInt();
1092                size_t count = reader.readInt();
1093                const SkPoint* pts = (const SkPoint*)reader.skip(sizeof(SkPoint) * count);
1094                canvas.drawPoints(mode, count, pts, paint);
1095            } break;
1096            case DRAW_POS_TEXT: {
1097                const SkPaint& paint = *this->getPaint(reader);
1098                getText(reader, &text);
1099                size_t points = reader.readInt();
1100                const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint));
1101                canvas.drawPosText(text.text(), text.length(), pos, paint);
1102            } break;
1103            case DRAW_POS_TEXT_TOP_BOTTOM: {
1104                const SkPaint& paint = *this->getPaint(reader);
1105                getText(reader, &text);
1106                size_t points = reader.readInt();
1107                const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint));
1108                const SkScalar top = reader.readScalar();
1109                const SkScalar bottom = reader.readScalar();
1110                if (!canvas.quickRejectY(top, bottom)) {
1111                    canvas.drawPosText(text.text(), text.length(), pos, paint);
1112                }
1113            } break;
1114            case DRAW_POS_TEXT_H: {
1115                const SkPaint& paint = *this->getPaint(reader);
1116                getText(reader, &text);
1117                size_t xCount = reader.readInt();
1118                const SkScalar constY = reader.readScalar();
1119                const SkScalar* xpos = (const SkScalar*)reader.skip(xCount * sizeof(SkScalar));
1120                canvas.drawPosTextH(text.text(), text.length(), xpos, constY,
1121                                    paint);
1122            } break;
1123            case DRAW_POS_TEXT_H_TOP_BOTTOM: {
1124                const SkPaint& paint = *this->getPaint(reader);
1125                getText(reader, &text);
1126                size_t xCount = reader.readInt();
1127                const SkScalar* xpos = (const SkScalar*)reader.skip((3 + xCount) * sizeof(SkScalar));
1128                const SkScalar top = *xpos++;
1129                const SkScalar bottom = *xpos++;
1130                const SkScalar constY = *xpos++;
1131                if (!canvas.quickRejectY(top, bottom)) {
1132                    canvas.drawPosTextH(text.text(), text.length(), xpos,
1133                                        constY, paint);
1134                }
1135            } break;
1136            case DRAW_RECT: {
1137                const SkPaint& paint = *this->getPaint(reader);
1138                canvas.drawRect(reader.skipT<SkRect>(), paint);
1139            } break;
1140            case DRAW_RRECT: {
1141                const SkPaint& paint = *this->getPaint(reader);
1142                SkRRect rrect;
1143                reader.readRRect(&rrect);
1144                canvas.drawRRect(rrect, paint);
1145            } break;
1146            case DRAW_SPRITE: {
1147                const SkPaint* paint = this->getPaint(reader);
1148                const SkBitmap& bitmap = this->getBitmap(reader);
1149                int left = reader.readInt();
1150                int top = reader.readInt();
1151                canvas.drawSprite(bitmap, left, top, paint);
1152            } break;
1153            case DRAW_TEXT: {
1154                const SkPaint& paint = *this->getPaint(reader);
1155                this->getText(reader, &text);
1156                SkScalar x = reader.readScalar();
1157                SkScalar y = reader.readScalar();
1158                canvas.drawText(text.text(), text.length(), x, y, paint);
1159            } break;
1160            case DRAW_TEXT_TOP_BOTTOM: {
1161                const SkPaint& paint = *this->getPaint(reader);
1162                this->getText(reader, &text);
1163                const SkScalar* ptr = (const SkScalar*)reader.skip(4 * sizeof(SkScalar));
1164                // ptr[0] == x
1165                // ptr[1] == y
1166                // ptr[2] == top
1167                // ptr[3] == bottom
1168                if (!canvas.quickRejectY(ptr[2], ptr[3])) {
1169                    canvas.drawText(text.text(), text.length(), ptr[0], ptr[1],
1170                                    paint);
1171                }
1172            } break;
1173            case DRAW_TEXT_ON_PATH: {
1174                const SkPaint& paint = *this->getPaint(reader);
1175                getText(reader, &text);
1176                const SkPath& path = this->getPath(reader);
1177                SkMatrix matrix;
1178                this->getMatrix(reader, &matrix);
1179                canvas.drawTextOnPath(text.text(), text.length(), path, &matrix, paint);
1180            } break;
1181            case DRAW_VERTICES: {
1182                SkAutoTUnref<SkXfermode> xfer;
1183                const SkPaint& paint = *this->getPaint(reader);
1184                DrawVertexFlags flags = (DrawVertexFlags)reader.readInt();
1185                SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader.readInt();
1186                int vCount = reader.readInt();
1187                const SkPoint* verts = (const SkPoint*)reader.skip(
1188                                                    vCount * sizeof(SkPoint));
1189                const SkPoint* texs = NULL;
1190                const SkColor* colors = NULL;
1191                const uint16_t* indices = NULL;
1192                int iCount = 0;
1193                if (flags & DRAW_VERTICES_HAS_TEXS) {
1194                    texs = (const SkPoint*)reader.skip(
1195                                                    vCount * sizeof(SkPoint));
1196                }
1197                if (flags & DRAW_VERTICES_HAS_COLORS) {
1198                    colors = (const SkColor*)reader.skip(
1199                                                    vCount * sizeof(SkColor));
1200                }
1201                if (flags & DRAW_VERTICES_HAS_INDICES) {
1202                    iCount = reader.readInt();
1203                    indices = (const uint16_t*)reader.skip(
1204                                                    iCount * sizeof(uint16_t));
1205                }
1206                if (flags & DRAW_VERTICES_HAS_XFER) {
1207                    int mode = reader.readInt();
1208                    if (mode < 0 || mode > SkXfermode::kLastMode) {
1209                        mode = SkXfermode::kModulate_Mode;
1210                    }
1211                    xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode));
1212                }
1213                canvas.drawVertices(vmode, vCount, verts, texs, colors, xfer,
1214                                    indices, iCount, paint);
1215            } break;
1216            case RESTORE:
1217                canvas.restore();
1218                break;
1219            case ROTATE:
1220                canvas.rotate(reader.readScalar());
1221                break;
1222            case SAVE:
1223                canvas.save((SkCanvas::SaveFlags) reader.readInt());
1224                break;
1225            case SAVE_LAYER: {
1226                const SkRect* boundsPtr = this->getRectPtr(reader);
1227                const SkPaint* paint = this->getPaint(reader);
1228                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader.readInt());
1229                } break;
1230            case SCALE: {
1231                SkScalar sx = reader.readScalar();
1232                SkScalar sy = reader.readScalar();
1233                canvas.scale(sx, sy);
1234            } break;
1235            case SET_MATRIX: {
1236                SkMatrix matrix;
1237                this->getMatrix(reader, &matrix);
1238                matrix.postConcat(initialMatrix);
1239                canvas.setMatrix(matrix);
1240            } break;
1241            case SKEW: {
1242                SkScalar sx = reader.readScalar();
1243                SkScalar sy = reader.readScalar();
1244                canvas.skew(sx, sy);
1245            } break;
1246            case TRANSLATE: {
1247                SkScalar dx = reader.readScalar();
1248                SkScalar dy = reader.readScalar();
1249                canvas.translate(dx, dy);
1250            } break;
1251            default:
1252                SkASSERT(0);
1253        }
1254
1255#ifdef SK_DEVELOPER
1256        this->postDraw(opIndex);
1257#endif
1258
1259        if (it.isValid()) {
1260            uint32_t skipTo = it.draw();
1261            if (kDrawComplete == skipTo) {
1262                break;
1263            }
1264            reader.setOffset(skipTo);
1265        }
1266    }
1267
1268#ifdef SPEW_CLIP_SKIPPING
1269    {
1270        size_t size =  skipRect.fSize + skipRRect.fSize + skipPath.fSize + skipRegion.fSize +
1271                skipCull.fSize;
1272        SkDebugf("--- Clip skips %d%% rect:%d rrect:%d path:%d rgn:%d cull:%d\n",
1273             size * 100 / reader.offset(), skipRect.fCount, skipRRect.fCount,
1274                 skipPath.fCount, skipRegion.fCount, skipCull.fCount);
1275        SkDebugf("--- Total ops: %d\n", opCount);
1276    }
1277#endif
1278//    this->dumpSize();
1279}
1280
1281///////////////////////////////////////////////////////////////////////////////
1282
1283#ifdef SK_DEBUG_SIZE
1284int SkPicturePlayback::size(size_t* sizePtr) {
1285    int objects = bitmaps(sizePtr);
1286    objects += paints(sizePtr);
1287    objects += paths(sizePtr);
1288    objects += pictures(sizePtr);
1289    objects += regions(sizePtr);
1290    *sizePtr = fOpData.size();
1291    return objects;
1292}
1293
1294int SkPicturePlayback::bitmaps(size_t* size) {
1295    size_t result = 0;
1296    for (int index = 0; index < fBitmapCount; index++) {
1297     //   const SkBitmap& bitmap = fBitmaps[index];
1298        result += sizeof(SkBitmap); // bitmap->size();
1299    }
1300    *size = result;
1301    return fBitmapCount;
1302}
1303
1304int SkPicturePlayback::paints(size_t* size) {
1305    size_t result = 0;
1306    for (int index = 0; index < fPaintCount; index++) {
1307    //    const SkPaint& paint = fPaints[index];
1308        result += sizeof(SkPaint); // paint->size();
1309    }
1310    *size = result;
1311    return fPaintCount;
1312}
1313
1314int SkPicturePlayback::paths(size_t* size) {
1315    size_t result = 0;
1316    for (int index = 0; index < fPathCount; index++) {
1317        const SkPath& path = fPaths[index];
1318        result += path.flatten(NULL);
1319    }
1320    *size = result;
1321    return fPathCount;
1322}
1323#endif
1324
1325#ifdef SK_DEBUG_DUMP
1326void SkPicturePlayback::dumpBitmap(const SkBitmap& bitmap) const {
1327    char pBuffer[DUMP_BUFFER_SIZE];
1328    char* bufferPtr = pBuffer;
1329    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1330        "BitmapData bitmap%p = {", &bitmap);
1331    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1332        "{kWidth, %d}, ", bitmap.width());
1333    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1334        "{kHeight, %d}, ", bitmap.height());
1335    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1336        "{kRowBytes, %d}, ", bitmap.rowBytes());
1337//        start here;
1338    SkDebugf("%s{0}};\n", pBuffer);
1339}
1340
1341void dumpMatrix(const SkMatrix& matrix) const {
1342    SkMatrix defaultMatrix;
1343    defaultMatrix.reset();
1344    char pBuffer[DUMP_BUFFER_SIZE];
1345    char* bufferPtr = pBuffer;
1346    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1347        "MatrixData matrix%p = {", &matrix);
1348    SkScalar scaleX = matrix.getScaleX();
1349    if (scaleX != defaultMatrix.getScaleX())
1350        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1351            "{kScaleX, %g}, ", SkScalarToFloat(scaleX));
1352    SkScalar scaleY = matrix.getScaleY();
1353    if (scaleY != defaultMatrix.getScaleY())
1354        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1355            "{kScaleY, %g}, ", SkScalarToFloat(scaleY));
1356    SkScalar skewX = matrix.getSkewX();
1357    if (skewX != defaultMatrix.getSkewX())
1358        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1359            "{kSkewX, %g}, ", SkScalarToFloat(skewX));
1360    SkScalar skewY = matrix.getSkewY();
1361    if (skewY != defaultMatrix.getSkewY())
1362        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1363            "{kSkewY, %g}, ", SkScalarToFloat(skewY));
1364    SkScalar translateX = matrix.getTranslateX();
1365    if (translateX != defaultMatrix.getTranslateX())
1366        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1367            "{kTranslateX, %g}, ", SkScalarToFloat(translateX));
1368    SkScalar translateY = matrix.getTranslateY();
1369    if (translateY != defaultMatrix.getTranslateY())
1370        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1371            "{kTranslateY, %g}, ", SkScalarToFloat(translateY));
1372    SkScalar perspX = matrix.getPerspX();
1373    if (perspX != defaultMatrix.getPerspX())
1374        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1375            "{kPerspX, %g}, ", perspX);
1376    SkScalar perspY = matrix.getPerspY();
1377    if (perspY != defaultMatrix.getPerspY())
1378        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1379            "{kPerspY, %g}, ", perspY);
1380    SkDebugf("%s{0}};\n", pBuffer);
1381}
1382
1383void dumpPaint(const SkPaint& paint) const {
1384    SkPaint defaultPaint;
1385    char pBuffer[DUMP_BUFFER_SIZE];
1386    char* bufferPtr = pBuffer;
1387    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1388        "PaintPointers paintPtrs%p = {", &paint);
1389    const SkTypeface* typeface = paint.getTypeface();
1390    if (typeface != defaultPaint.getTypeface())
1391        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1392            "{kTypeface, %p}, ", typeface);
1393    const SkPathEffect* pathEffect = paint.getPathEffect();
1394    if (pathEffect != defaultPaint.getPathEffect())
1395        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1396            "{kPathEffect, %p}, ", pathEffect);
1397    const SkShader* shader = paint.getShader();
1398    if (shader != defaultPaint.getShader())
1399        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1400            "{kShader, %p}, ", shader);
1401    const SkXfermode* xfermode = paint.getXfermode();
1402    if (xfermode != defaultPaint.getXfermode())
1403        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1404            "{kXfermode, %p}, ", xfermode);
1405    const SkMaskFilter* maskFilter = paint.getMaskFilter();
1406    if (maskFilter != defaultPaint.getMaskFilter())
1407        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1408            "{kMaskFilter, %p}, ", maskFilter);
1409    const SkColorFilter* colorFilter = paint.getColorFilter();
1410    if (colorFilter != defaultPaint.getColorFilter())
1411        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1412            "{kColorFilter, %p}, ", colorFilter);
1413    const SkRasterizer* rasterizer = paint.getRasterizer();
1414    if (rasterizer != defaultPaint.getRasterizer())
1415        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1416            "{kRasterizer, %p}, ", rasterizer);
1417    const SkDrawLooper* drawLooper = paint.getLooper();
1418    if (drawLooper != defaultPaint.getLooper())
1419        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1420            "{kDrawLooper, %p}, ", drawLooper);
1421    SkDebugf("%s{0}};\n", pBuffer);
1422    bufferPtr = pBuffer;
1423    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1424        "PaintScalars paintScalars%p = {", &paint);
1425    SkScalar textSize = paint.getTextSize();
1426    if (textSize != defaultPaint.getTextSize())
1427        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1428            "{kTextSize, %g}, ", SkScalarToFloat(textSize));
1429    SkScalar textScaleX = paint.getTextScaleX();
1430    if (textScaleX != defaultPaint.getTextScaleX())
1431        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1432            "{kTextScaleX, %g}, ", SkScalarToFloat(textScaleX));
1433    SkScalar textSkewX = paint.getTextSkewX();
1434    if (textSkewX != defaultPaint.getTextSkewX())
1435        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1436            "{kTextSkewX, %g}, ", SkScalarToFloat(textSkewX));
1437    SkScalar strokeWidth = paint.getStrokeWidth();
1438    if (strokeWidth != defaultPaint.getStrokeWidth())
1439        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1440            "{kStrokeWidth, %g}, ", SkScalarToFloat(strokeWidth));
1441    SkScalar strokeMiter = paint.getStrokeMiter();
1442    if (strokeMiter != defaultPaint.getStrokeMiter())
1443        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1444            "{kStrokeMiter, %g}, ", SkScalarToFloat(strokeMiter));
1445    SkDebugf("%s{0}};\n", pBuffer);
1446    bufferPtr = pBuffer;
1447    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1448        "PaintInts = paintInts%p = {", &paint);
1449    unsigned color = paint.getColor();
1450    if (color != defaultPaint.getColor())
1451        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1452            "{kColor, 0x%x}, ", color);
1453    unsigned flags = paint.getFlags();
1454    if (flags != defaultPaint.getFlags())
1455        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1456            "{kFlags, 0x%x}, ", flags);
1457    int align = paint.getTextAlign();
1458    if (align != defaultPaint.getTextAlign())
1459        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1460            "{kAlign, 0x%x}, ", align);
1461    int strokeCap = paint.getStrokeCap();
1462    if (strokeCap != defaultPaint.getStrokeCap())
1463        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1464            "{kStrokeCap, 0x%x}, ", strokeCap);
1465    int strokeJoin = paint.getStrokeJoin();
1466    if (strokeJoin != defaultPaint.getStrokeJoin())
1467        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1468            "{kAlign, 0x%x}, ", strokeJoin);
1469    int style = paint.getStyle();
1470    if (style != defaultPaint.getStyle())
1471        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1472            "{kStyle, 0x%x}, ", style);
1473    int textEncoding = paint.getTextEncoding();
1474    if (textEncoding != defaultPaint.getTextEncoding())
1475        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1476            "{kTextEncoding, 0x%x}, ", textEncoding);
1477    SkDebugf("%s{0}};\n", pBuffer);
1478
1479    SkDebugf("PaintData paint%p = {paintPtrs%p, paintScalars%p, paintInts%p};\n",
1480        &paint, &paint, &paint, &paint);
1481}
1482
1483void SkPicturePlayback::dumpPath(const SkPath& path) const {
1484    SkDebugf("path dump unimplemented\n");
1485}
1486
1487void SkPicturePlayback::dumpPicture(const SkPicture& picture) const {
1488    SkDebugf("picture dump unimplemented\n");
1489}
1490
1491void SkPicturePlayback::dumpRegion(const SkRegion& region) const {
1492    SkDebugf("region dump unimplemented\n");
1493}
1494
1495int SkPicturePlayback::dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType) {
1496    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1497        "k%s, ", DrawTypeToString(drawType));
1498}
1499
1500int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) {
1501    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1502        "%s:%d, ", name, getInt());
1503}
1504
1505int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) {
1506    const SkRect* rect = fReader.skipRect();
1507    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1508        "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft),
1509        SkScalarToFloat(rect.fTop),
1510        SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom));
1511}
1512
1513int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) {
1514    SkPoint pt;
1515    getPoint(&pt);
1516    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1517        "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX),
1518        SkScalarToFloat(pt.fY));
1519}
1520
1521void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int count) {
1522    char* bufferPtr = *bufferPtrPtr;
1523    const SkPoint* pts = (const SkPoint*)fReadStream.getAtPos();
1524    fReadStream.skip(sizeof(SkPoint) * count);
1525    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1526        "count:%d {", count);
1527    for (int index = 0; index < count; index++)
1528        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1529        "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX),
1530        SkScalarToFloat(pts[index].fY));
1531    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1532        "} ");
1533    *bufferPtrPtr = bufferPtr;
1534}
1535
1536int SkPicturePlayback::dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr) {
1537    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1538        "%s:%p, ", name, ptr);
1539}
1540
1541int SkPicturePlayback::dumpRectPtr(char* bufferPtr, char* buffer, char* name) {
1542    char result;
1543    fReadStream.read(&result, sizeof(result));
1544    if (result)
1545        return dumpRect(bufferPtr, buffer, name);
1546    else
1547        return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1548            "%s:NULL, ", name);
1549}
1550
1551int SkPicturePlayback::dumpScalar(char* bufferPtr, char* buffer, char* name) {
1552    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
1553        "%s:%d, ", name, getScalar());
1554}
1555
1556void SkPicturePlayback::dumpText(char** bufferPtrPtr, char* buffer) {
1557    char* bufferPtr = *bufferPtrPtr;
1558    int length = getInt();
1559    bufferPtr += dumpDrawType(bufferPtr, buffer);
1560    fReadStream.skipToAlign4();
1561    char* text = (char*) fReadStream.getAtPos();
1562    fReadStream.skip(length);
1563    bufferPtr += dumpInt(bufferPtr, buffer, "length");
1564    int limit = DUMP_BUFFER_SIZE - (bufferPtr - buffer) - 2;
1565    length >>= 1;
1566    if (limit > length)
1567        limit = length;
1568    if (limit > 0) {
1569        *bufferPtr++ = '"';
1570        for (int index = 0; index < limit; index++) {
1571            *bufferPtr++ = *(unsigned short*) text;
1572            text += sizeof(unsigned short);
1573        }
1574        *bufferPtr++ = '"';
1575    }
1576    *bufferPtrPtr = bufferPtr;
1577}
1578
1579#define DUMP_DRAWTYPE(drawType) \
1580    bufferPtr += dumpDrawType(bufferPtr, buffer, drawType)
1581
1582#define DUMP_INT(name) \
1583    bufferPtr += dumpInt(bufferPtr, buffer, #name)
1584
1585#define DUMP_RECT_PTR(name) \
1586    bufferPtr += dumpRectPtr(bufferPtr, buffer, #name)
1587
1588#define DUMP_POINT(name) \
1589    bufferPtr += dumpRect(bufferPtr, buffer, #name)
1590
1591#define DUMP_RECT(name) \
1592    bufferPtr += dumpRect(bufferPtr, buffer, #name)
1593
1594#define DUMP_POINT_ARRAY(count) \
1595    dumpPointArray(&bufferPtr, buffer, count)
1596
1597#define DUMP_PTR(name, ptr) \
1598    bufferPtr += dumpPtr(bufferPtr, buffer, #name, (void*) ptr)
1599
1600#define DUMP_SCALAR(name) \
1601    bufferPtr += dumpScalar(bufferPtr, buffer, #name)
1602
1603#define DUMP_TEXT() \
1604    dumpText(&bufferPtr, buffer)
1605
1606void SkPicturePlayback::dumpStream() {
1607    SkDebugf("RecordStream stream = {\n");
1608    DrawType drawType;
1609    TextContainer text;
1610    fReadStream.rewind();
1611    char buffer[DUMP_BUFFER_SIZE], * bufferPtr;
1612    while (fReadStream.read(&drawType, sizeof(drawType))) {
1613        bufferPtr = buffer;
1614        DUMP_DRAWTYPE(drawType);
1615        switch (drawType) {
1616            case CLIP_PATH: {
1617                DUMP_PTR(SkPath, &getPath());
1618                DUMP_INT(SkRegion::Op);
1619                DUMP_INT(offsetToRestore);
1620                } break;
1621            case CLIP_REGION: {
1622                DUMP_INT(SkRegion::Op);
1623                DUMP_INT(offsetToRestore);
1624            } break;
1625            case CLIP_RECT: {
1626                DUMP_RECT(rect);
1627                DUMP_INT(SkRegion::Op);
1628                DUMP_INT(offsetToRestore);
1629                } break;
1630            case CONCAT:
1631                break;
1632            case DRAW_BITMAP: {
1633                DUMP_PTR(SkPaint, getPaint());
1634                DUMP_PTR(SkBitmap, &getBitmap());
1635                DUMP_SCALAR(left);
1636                DUMP_SCALAR(top);
1637                } break;
1638            case DRAW_PAINT:
1639                DUMP_PTR(SkPaint, getPaint());
1640                break;
1641            case DRAW_PATH: {
1642                DUMP_PTR(SkPaint, getPaint());
1643                DUMP_PTR(SkPath, &getPath());
1644                } break;
1645            case DRAW_PICTURE: {
1646                DUMP_PTR(SkPicture, &getPicture());
1647                } break;
1648            case DRAW_POINTS: {
1649                DUMP_PTR(SkPaint, getPaint());
1650                (void)getInt(); // PointMode
1651                size_t count = getInt();
1652                fReadStream.skipToAlign4();
1653                DUMP_POINT_ARRAY(count);
1654                } break;
1655            case DRAW_POS_TEXT: {
1656                DUMP_PTR(SkPaint, getPaint());
1657                DUMP_TEXT();
1658                size_t points = getInt();
1659                fReadStream.skipToAlign4();
1660                DUMP_POINT_ARRAY(points);
1661                } break;
1662            case DRAW_POS_TEXT_H: {
1663                DUMP_PTR(SkPaint, getPaint());
1664                DUMP_TEXT();
1665                size_t points = getInt();
1666                fReadStream.skipToAlign4();
1667                DUMP_SCALAR(top);
1668                DUMP_SCALAR(bottom);
1669                DUMP_SCALAR(constY);
1670                DUMP_POINT_ARRAY(points);
1671                } break;
1672            case DRAW_RECT: {
1673                DUMP_PTR(SkPaint, getPaint());
1674                DUMP_RECT(rect);
1675                } break;
1676            case DRAW_SPRITE: {
1677                DUMP_PTR(SkPaint, getPaint());
1678                DUMP_PTR(SkBitmap, &getBitmap());
1679                DUMP_SCALAR(left);
1680                DUMP_SCALAR(top);
1681                } break;
1682            case DRAW_TEXT: {
1683                DUMP_PTR(SkPaint, getPaint());
1684                DUMP_TEXT();
1685                DUMP_SCALAR(x);
1686                DUMP_SCALAR(y);
1687                } break;
1688            case DRAW_TEXT_ON_PATH: {
1689                DUMP_PTR(SkPaint, getPaint());
1690                DUMP_TEXT();
1691                DUMP_PTR(SkPath, &getPath());
1692                } break;
1693            case RESTORE:
1694                break;
1695            case ROTATE:
1696                DUMP_SCALAR(rotate);
1697                break;
1698            case SAVE:
1699                DUMP_INT(SkCanvas::SaveFlags);
1700                break;
1701            case SAVE_LAYER: {
1702                DUMP_RECT_PTR(layer);
1703                DUMP_PTR(SkPaint, getPaint());
1704                DUMP_INT(SkCanvas::SaveFlags);
1705                } break;
1706            case SCALE: {
1707                DUMP_SCALAR(sx);
1708                DUMP_SCALAR(sy);
1709                } break;
1710            case SKEW: {
1711                DUMP_SCALAR(sx);
1712                DUMP_SCALAR(sy);
1713                } break;
1714            case TRANSLATE: {
1715                DUMP_SCALAR(dx);
1716                DUMP_SCALAR(dy);
1717                } break;
1718            default:
1719                SkASSERT(0);
1720        }
1721        SkDebugf("%s\n", buffer);
1722    }
1723}
1724
1725void SkPicturePlayback::dump() const {
1726    char pBuffer[DUMP_BUFFER_SIZE];
1727    char* bufferPtr = pBuffer;
1728    int index;
1729    if (fBitmapCount > 0)
1730        SkDebugf("// bitmaps (%d)\n", fBitmapCount);
1731    for (index = 0; index < fBitmapCount; index++) {
1732        const SkBitmap& bitmap = fBitmaps[index];
1733        dumpBitmap(bitmap);
1734    }
1735    if (fBitmapCount > 0)
1736        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1737            "Bitmaps bitmaps = {");
1738    for (index = 0; index < fBitmapCount; index++)
1739        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1740            "bitmap%p, ", &fBitmaps[index]);
1741    if (fBitmapCount > 0)
1742        SkDebugf("%s0};\n", pBuffer);
1743
1744
1745    if (fPaintCount > 0)
1746        SkDebugf("// paints (%d)\n", fPaintCount);
1747    for (index = 0; index < fPaintCount; index++) {
1748        const SkPaint& paint = fPaints[index];
1749        dumpPaint(paint);
1750    }
1751    bufferPtr = pBuffer;
1752    if (fPaintCount > 0)
1753        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1754            "Paints paints = {");
1755    for (index = 0; index < fPaintCount; index++)
1756        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1757            "paint%p, ", &fPaints[index]);
1758    if (fPaintCount > 0)
1759        SkDebugf("%s0};\n", pBuffer);
1760
1761    for (index = 0; index < fPathCount; index++) {
1762        const SkPath& path = fPaths[index];
1763        dumpPath(path);
1764    }
1765    bufferPtr = pBuffer;
1766    if (fPathCount > 0)
1767        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1768            "Paths paths = {");
1769    for (index = 0; index < fPathCount; index++)
1770        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1771            "path%p, ", &fPaths[index]);
1772    if (fPathCount > 0)
1773        SkDebugf("%s0};\n", pBuffer);
1774
1775    for (index = 0; index < fPictureCount; index++) {
1776        dumpPicture(*fPictureRefs[index]);
1777    }
1778    bufferPtr = pBuffer;
1779    if (fPictureCount > 0)
1780        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1781            "Pictures pictures = {");
1782    for (index = 0; index < fPictureCount; index++)
1783        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
1784            "picture%p, ", fPictureRefs[index]);
1785    if (fPictureCount > 0)
1786        SkDebugf("%s0};\n", pBuffer);
1787
1788    const_cast<SkPicturePlayback*>(this)->dumpStream();
1789}
1790
1791#endif
1792