SkPictureRecord.cpp revision 4b32bd53c63b245707822ae83e3215863303bf43
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#include "SkPictureRecord.h"
9#include "SkTSearch.h"
10#include "SkPixelRef.h"
11#include "SkRRect.h"
12#include "SkBBoxHierarchy.h"
13#include "SkPictureStateTree.h"
14
15#define MIN_WRITER_SIZE 16384
16#define HEAP_BLOCK_SIZE 4096
17
18enum {
19    // just need a value that save or getSaveCount would never return
20    kNoInitialSave = -1,
21};
22
23// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
24static int const kUInt32Size = 4;
25
26static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
27static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
28
29SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
30        INHERITED(device),
31        fBoundingHierarchy(NULL),
32        fStateTree(NULL),
33        fFlattenableHeap(HEAP_BLOCK_SIZE),
34        fMatrices(&fFlattenableHeap),
35        fPaints(&fFlattenableHeap),
36        fRegions(&fFlattenableHeap),
37        fWriter(MIN_WRITER_SIZE),
38        fRecordFlags(flags) {
39#ifdef SK_DEBUG_SIZE
40    fPointBytes = fRectBytes = fTextBytes = 0;
41    fPointWrites = fRectWrites = fTextWrites = 0;
42#endif
43
44    fRestoreOffsetStack.setReserve(32);
45
46    fBitmapHeap = SkNEW(SkBitmapHeap);
47    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
48    fPathHeap = NULL;   // lazy allocate
49    fFirstSavedLayerIndex = kNoSavedLayerIndex;
50
51    fInitialSaveCount = kNoInitialSave;
52}
53
54SkPictureRecord::~SkPictureRecord() {
55    SkSafeUnref(fBitmapHeap);
56    SkSafeUnref(fPathHeap);
57    SkSafeUnref(fBoundingHierarchy);
58    SkSafeUnref(fStateTree);
59    fFlattenableHeap.setBitmapStorage(NULL);
60    fPictureRefs.unrefAll();
61}
62
63///////////////////////////////////////////////////////////////////////////////
64
65// Return the offset of the paint inside a given op's byte stream. A zero
66// return value means there is no paint (and you really shouldn't be calling
67// this method)
68static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) {
69    // These offsets are where the paint would be if the op size doesn't overflow
70    static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
71        0,  // UNUSED - no paint
72        0,  // CLIP_PATH - no paint
73        0,  // CLIP_REGION - no paint
74        0,  // CLIP_RECT - no paint
75        0,  // CLIP_RRECT - no paint
76        0,  // CONCAT - no paint
77        1,  // DRAW_BITMAP - right after op code
78        1,  // DRAW_BITMAP_MATRIX - right after op code
79        1,  // DRAW_BITMAP_NINE - right after op code
80        1,  // DRAW_BITMAP_RECT_TO_RECT - right after op code
81        0,  // DRAW_CLEAR - no paint
82        0,  // DRAW_DATA - no paint
83        1,  // DRAW_OVAL - right after op code
84        1,  // DRAW_PAINT - right after op code
85        1,  // DRAW_PATH - right after op code
86        0,  // DRAW_PICTURE - no paint
87        1,  // DRAW_POINTS - right after op code
88        1,  // DRAW_POS_TEXT - right after op code
89        1,  // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
90        1,  // DRAW_POS_TEXT_H - right after op code
91        1,  // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
92        1,  // DRAW_RECT - right after op code
93        1,  // DRAW_RRECT - right after op code
94        1,  // DRAW_SPRITE - right after op code
95        1,  // DRAW_TEXT - right after op code
96        1,  // DRAW_TEXT_ON_PATH - right after op code
97        1,  // DRAW_TEXT_TOP_BOTTOM - right after op code
98        1,  // DRAW_VERTICES - right after op code
99        0,  // RESTORE - no paint
100        0,  // ROTATE - no paint
101        0,  // SAVE - no paint
102        0,  // SAVE_LAYER - see below - this paint's location varies
103        0,  // SCALE - no paint
104        0,  // SET_MATRIX - no paint
105        0,  // SKEW - no paint
106        0,  // TRANSLATE - no paint
107        0,  // NOOP - no paint
108    };
109
110    SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
111    SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
112
113    int overflow = 0;
114    if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
115        // This op's size overflows so an extra uint32_t will be written
116        // after the op code
117        overflow = sizeof(uint32_t);
118    }
119
120    if (SAVE_LAYER == op) {
121        static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
122        static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
123
124        if (kSaveLayerNoBoundsSize == opSize) {
125            return kSaveLayerNoBoundsPaintOffset + overflow;
126        } else {
127            SkASSERT(kSaveLayerWithBoundsSize == opSize);
128            return kSaveLayerWithBoundsPaintOffset + overflow;
129        }
130    }
131
132    SkASSERT(0 != gPaintOffsets[op]);   // really shouldn't be calling this method
133    return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
134}
135
136SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
137    SkASSERT(!"eeek, don't try to change the device on a recording canvas");
138    return this->INHERITED::setDevice(device);
139}
140
141int SkPictureRecord::save(SaveFlags flags) {
142    // record the offset to us, making it non-positive to distinguish a save
143    // from a clip entry.
144    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
145
146    // op + flags
147    uint32_t size = 2 * kUInt32Size;
148    uint32_t initialOffset = this->addDraw(SAVE, &size);
149    addInt(flags);
150
151    validate(initialOffset, size);
152    return this->INHERITED::save(flags);
153}
154
155int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
156                               SaveFlags flags) {
157    // record the offset to us, making it non-positive to distinguish a save
158    // from a clip entry.
159    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
160
161    // op + bool for 'bounds'
162    uint32_t size = 2 * kUInt32Size;
163    if (NULL != bounds) {
164        size += sizeof(*bounds); // + rect
165    }
166    // + paint index + flags
167    size += 2 * kUInt32Size;
168
169    SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
170
171    uint32_t initialOffset = this->addDraw(SAVE_LAYER, &size);
172    addRectPtr(bounds);
173    SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size());
174    addPaintPtr(paint);
175    addInt(flags);
176
177    if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
178        fFirstSavedLayerIndex = fRestoreOffsetStack.count();
179    }
180
181    validate(initialOffset, size);
182    /*  Don't actually call saveLayer, because that will try to allocate an
183        offscreen device (potentially very big) which we don't actually need
184        at this time (and may not be able to afford since during record our
185        clip starts out the size of the picture, which is often much larger
186        than the size of the actual device we'll use during playback).
187     */
188    int count = this->INHERITED::save(flags);
189    this->clipRectBounds(bounds, flags, NULL);
190    return count;
191}
192
193bool SkPictureRecord::isDrawingToLayer() const {
194    return fFirstSavedLayerIndex != kNoSavedLayerIndex;
195}
196
197/*
198 * Read the op code from 'offset' in 'writer' and extract the size too.
199 */
200static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
201    uint32_t* peek = writer->peek32(offset);
202
203    uint32_t op;
204    UNPACK_8_24(*peek, op, *size);
205    if (MASK_24 == *size) {
206        // size required its own slot right after the op code
207        *size = *writer->peek32(offset+kUInt32Size);
208    }
209    return (DrawType) op;
210}
211
212#ifdef TRACK_COLLAPSE_STATS
213    static int gCollapseCount, gCollapseCalls;
214#endif
215
216// Is the supplied paint simply a color?
217static bool is_simple(const SkPaint& p) {
218    intptr_t orAccum = (intptr_t)p.getPathEffect()  |
219                       (intptr_t)p.getShader()      |
220                       (intptr_t)p.getXfermode()    |
221                       (intptr_t)p.getMaskFilter()  |
222                       (intptr_t)p.getColorFilter() |
223                       (intptr_t)p.getRasterizer()  |
224                       (intptr_t)p.getLooper()      |
225                       (intptr_t)p.getImageFilter();
226    return 0 == orAccum;
227}
228
229// CommandInfos are fed to the 'match' method and filled in with command
230// information.
231struct CommandInfo {
232    DrawType fActualOp;
233    uint32_t fOffset;
234    uint32_t fSize;
235};
236
237/*
238 * Attempt to match the provided pattern of commands starting at 'offset'
239 * in the byte stream and stopping at the end of the stream. Upon success,
240 * return true with all the pattern information filled out in the result
241 * array (i.e., actual ops, offsets and sizes).
242 * Note this method skips any NOOPs seen in the stream
243 */
244static bool match(SkWriter32* writer, uint32_t offset,
245                  int* pattern, CommandInfo* result, int numCommands) {
246    SkASSERT(offset < writer->size());
247
248    uint32_t curOffset = offset;
249    uint32_t curSize = 0;
250    int numMatched;
251    for (numMatched = 0; numMatched < numCommands && curOffset < writer->size(); ++numMatched) {
252        DrawType op = peek_op_and_size(writer, curOffset, &curSize);
253        while (NOOP == op && curOffset < writer->size()) {
254            curOffset += curSize;
255            op = peek_op_and_size(writer, curOffset, &curSize);
256        }
257
258        if (curOffset >= writer->size()) {
259            return false; // ran out of byte stream
260        }
261
262        if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
263            if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
264                DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
265                return false;
266            }
267        } else if (op != pattern[numMatched]) {
268            return false;
269        }
270
271        result[numMatched].fActualOp = op;
272        result[numMatched].fOffset = curOffset;
273        result[numMatched].fSize = curSize;
274
275        curOffset += curSize;
276    }
277
278    if (numMatched != numCommands) {
279        return false;
280    }
281
282    curOffset += curSize;
283    if (curOffset < writer->size()) {
284        // Something else between the last command and the end of the stream
285        return false;
286    }
287
288    return true;
289}
290
291// temporarily here to make code review easier
292static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
293                                                 SkPaintDictionary* paintDict,
294                                                 const CommandInfo& saveLayerInfo,
295                                                 const CommandInfo& dbmInfo);
296
297/*
298 * Restore has just been called (but not recorded), look back at the
299 * matching save* and see if we are in the configuration:
300 *   SAVE_LAYER
301 *       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
302 *   RESTORE
303 * where the saveLayer's color can be moved into the drawBitmap*'s paint
304 */
305static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
306                               SkPaintDictionary* paintDict) {
307    // back up to the save block
308    // TODO: add a stack to track save*/restore offsets rather than searching backwards
309    while (offset > 0) {
310        offset = *writer->peek32(offset);
311    }
312
313    int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
314    CommandInfo result[SK_ARRAY_COUNT(pattern)];
315
316    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
317        return false;
318    }
319
320    if (kSaveLayerWithBoundsSize == result[0].fSize) {
321        // The saveLayer's bound can offset where the dbm is drawn
322        return false;
323    }
324
325
326    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
327                                                result[0], result[1]);
328}
329
330/*
331 * Convert the command code located at 'offset' to a NOOP. Leave the size
332 * field alone so the NOOP can be skipped later.
333 */
334static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
335    uint32_t* ptr = writer->peek32(offset);
336    *ptr = (*ptr & MASK_24) | (NOOP << 24);
337}
338
339/*
340 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
341 * Return true on success; false otherwise.
342 */
343static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
344                                                 SkPaintDictionary* paintDict,
345                                                 const CommandInfo& saveLayerInfo,
346                                                 const CommandInfo& dbmInfo) {
347    SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
348    SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
349             DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
350             DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
351             DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
352
353    uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
354    uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
355
356    // we have a match, now we need to get the paints involved
357    uint32_t dbmPaintId = *writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
358    uint32_t saveLayerPaintId = *writer->peek32(saveLayerInfo.fOffset+slPaintOffset);
359
360    if (0 == saveLayerPaintId) {
361        // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
362        // and signal the caller (by returning true) to not add the RESTORE op
363        convert_command_to_noop(writer, saveLayerInfo.fOffset);
364        return true;
365    }
366
367    if (0 == dbmPaintId) {
368        // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
369        // and signal the caller (by returning true) to not add the RESTORE op
370        convert_command_to_noop(writer, saveLayerInfo.fOffset);
371        uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
372        SkASSERT(0 == *ptr);
373        *ptr = saveLayerPaintId;
374        return true;
375    }
376
377    SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
378    if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
379        return false;
380    }
381
382    // For this optimization we only fold the saveLayer and drawBitmapRect
383    // together if the saveLayer's draw is simple (i.e., no fancy effects) and
384    // and the only difference in the colors is that the saveLayer's can have
385    // an alpha while the drawBitmapRect's is opaque.
386    // TODO: it should be possible to fold them together even if they both
387    // have different non-255 alphas
388    SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
389
390    SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
391    if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) {
392        return false;
393    }
394
395    SkColor newColor = SkColorSetA(dbmPaint->getColor(),
396                                   SkColorGetA(saveLayerPaint->getColor()));
397    dbmPaint->setColor(newColor);
398
399    const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
400    if (NULL == data) {
401        return false;
402    }
403
404    // kill the saveLayer and alter the DBMR2R's paint to be the modified one
405    convert_command_to_noop(writer, saveLayerInfo.fOffset);
406    uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
407    SkASSERT(dbmPaintId == *ptr);
408    *ptr = data->index();
409    return true;
410}
411
412/*
413 * Restore has just been called (but not recorded), look back at the
414 * matching save* and see if we are in the configuration:
415 *   SAVE_LAYER
416 *      SAVE
417 *         CLIP_RECT
418 *         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
419 *      RESTORE
420 *   RESTORE
421 * where the saveLayer's color can be moved into the drawBitmap*'s paint
422 */
423static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
424                               SkPaintDictionary* paintDict) {
425
426    // back up to the save block
427    // TODO: add a stack to track save*/restore offsets rather than searching backwards
428    while (offset > 0) {
429        offset = *writer->peek32(offset);
430    }
431
432    int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
433    CommandInfo result[SK_ARRAY_COUNT(pattern)];
434
435    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
436        return false;
437    }
438
439    if (kSaveLayerWithBoundsSize == result[0].fSize) {
440        // The saveLayer's bound can offset where the dbm is drawn
441        return false;
442    }
443
444    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
445                                                result[0], result[3]);
446}
447
448/*
449 *  Restore has just been called (but not recorded), so look back at the
450 *  matching save(), and see if we can eliminate the pair of them, due to no
451 *  intervening matrix/clip calls.
452 *
453 *  If so, update the writer and return true, in which case we won't even record
454 *  the restore() call. If we still need the restore(), return false.
455 */
456static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
457                                       SkPaintDictionary* paintDict) {
458#ifdef TRACK_COLLAPSE_STATS
459    gCollapseCalls += 1;
460#endif
461
462    int32_t restoreOffset = (int32_t)writer->size();
463
464    // back up to the save block
465    while (offset > 0) {
466        offset = *writer->peek32(offset);
467    }
468
469    // now offset points to a save
470    offset = -offset;
471    uint32_t opSize;
472    DrawType op = peek_op_and_size(writer, offset, &opSize);
473    if (SAVE_LAYER == op) {
474        // not ready to cull these out yet (mrr)
475        return false;
476    }
477    SkASSERT(SAVE == op);
478
479    // Walk forward until we get back to either a draw-verb (abort) or we hit
480    // our restore (success).
481    int32_t saveOffset = offset;
482
483    offset += opSize;
484    while (offset < restoreOffset) {
485        op = peek_op_and_size(writer, offset, &opSize);
486        if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
487            // drawing verb, abort
488            return false;
489        }
490        offset += opSize;
491    }
492
493#ifdef TRACK_COLLAPSE_STATS
494    gCollapseCount += 1;
495    SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
496             (double)gCollapseCount / gCollapseCalls, "%");
497#endif
498
499    writer->rewindToOffset(saveOffset);
500    return true;
501}
502
503typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
504                                     SkPaintDictionary* paintDict);
505enum PictureRecordOptType {
506    kRewind_OptType,  // Optimization rewinds the command stream
507    kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
508};
509
510struct PictureRecordOpt {
511    PictureRecordOptProc fProc;
512    PictureRecordOptType fType;
513};
514/*
515 * A list of the optimizations that are tried upon seeing a restore
516 * TODO: add a real API for such optimizations
517 *       Add the ability to fire optimizations on any op (not just RESTORE)
518 */
519static const PictureRecordOpt gPictureRecordOpts[] = {
520    { collapse_save_clip_restore, kRewind_OptType },
521    { remove_save_layer1,         kCollapseSaveLayer_OptType },
522    { remove_save_layer2,         kCollapseSaveLayer_OptType }
523};
524
525// This is called after an optimization has been applied to the command stream
526// in order to adjust the contents and state of the bounding box hierarchy and
527// state tree to reflect the optimization.
528static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
529                                      SkBBoxHierarchy* boundingHierarchy) {
530    switch (opt) {
531    case kCollapseSaveLayer_OptType:
532        stateTree->saveCollapsed();
533        break;
534    case kRewind_OptType:
535        if (NULL != boundingHierarchy) {
536            boundingHierarchy->rewindInserts();
537        }
538        // Note: No need to touch the state tree for this to work correctly.
539        // Unused branches do not burden the playback, and pruning the tree
540        // would be O(N^2), so it is best to leave it alone.
541        break;
542    default:
543        SkASSERT(0);
544    }
545}
546
547void SkPictureRecord::restore() {
548    // FIXME: SkDeferredCanvas needs to be refactored to respect
549    // save/restore balancing so that the following test can be
550    // turned on permanently.
551#if 0
552    SkASSERT(fRestoreOffsetStack.count() > 1);
553#endif
554
555    // check for underflow
556    if (fRestoreOffsetStack.count() == 0) {
557        return;
558    }
559
560    if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
561        fFirstSavedLayerIndex = kNoSavedLayerIndex;
562    }
563
564    uint32_t initialOffset, size;
565    size_t opt;
566    for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
567        if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
568            // Some optimization fired so don't add the RESTORE
569            size = 0;
570            initialOffset = fWriter.size();
571            apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
572                                      fStateTree, fBoundingHierarchy);
573            break;
574        }
575    }
576
577    if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
578        // No optimization fired so add the RESTORE
579        fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
580        size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
581        initialOffset = this->addDraw(RESTORE, &size);
582    }
583
584    fRestoreOffsetStack.pop();
585
586    validate(initialOffset, size);
587    return this->INHERITED::restore();
588}
589
590bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
591    // op + dx + dy
592    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
593    uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
594    addScalar(dx);
595    addScalar(dy);
596    validate(initialOffset, size);
597    return this->INHERITED::translate(dx, dy);
598}
599
600bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
601    // op + sx + sy
602    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
603    uint32_t initialOffset = this->addDraw(SCALE, &size);
604    addScalar(sx);
605    addScalar(sy);
606    validate(initialOffset, size);
607    return this->INHERITED::scale(sx, sy);
608}
609
610bool SkPictureRecord::rotate(SkScalar degrees) {
611    // op + degrees
612    uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
613    uint32_t initialOffset = this->addDraw(ROTATE, &size);
614    addScalar(degrees);
615    validate(initialOffset, size);
616    return this->INHERITED::rotate(degrees);
617}
618
619bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
620    // op + sx + sy
621    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
622    uint32_t initialOffset = this->addDraw(SKEW, &size);
623    addScalar(sx);
624    addScalar(sy);
625    validate(initialOffset, size);
626    return this->INHERITED::skew(sx, sy);
627}
628
629bool SkPictureRecord::concat(const SkMatrix& matrix) {
630    validate(fWriter.size(), 0);
631    // op + matrix index
632    uint32_t size = 2 * kUInt32Size;
633    uint32_t initialOffset = this->addDraw(CONCAT, &size);
634    addMatrix(matrix);
635    validate(initialOffset, size);
636    return this->INHERITED::concat(matrix);
637}
638
639void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
640    validate(fWriter.size(), 0);
641    // op + matrix index
642    uint32_t size = 2 * kUInt32Size;
643    uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
644    addMatrix(matrix);
645    validate(initialOffset, size);
646    this->INHERITED::setMatrix(matrix);
647}
648
649static bool regionOpExpands(SkRegion::Op op) {
650    switch (op) {
651        case SkRegion::kUnion_Op:
652        case SkRegion::kXOR_Op:
653        case SkRegion::kReverseDifference_Op:
654        case SkRegion::kReplace_Op:
655            return true;
656        case SkRegion::kIntersect_Op:
657        case SkRegion::kDifference_Op:
658            return false;
659        default:
660            SkDEBUGFAIL("unknown region op");
661            return false;
662    }
663}
664
665void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
666    int32_t offset = fRestoreOffsetStack.top();
667    while (offset > 0) {
668        uint32_t* peek = fWriter.peek32(offset);
669        offset = *peek;
670        *peek = restoreOffset;
671    }
672
673#ifdef SK_DEBUG
674    // assert that the final offset value points to a save verb
675    uint32_t opSize;
676    DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
677    SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
678#endif
679}
680
681void SkPictureRecord::beginRecording() {
682    // we have to call this *after* our constructor, to ensure that it gets
683    // recorded. This is balanced by restoreToCount() call from endRecording,
684    // which in-turn calls our overridden restore(), so those get recorded too.
685    fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
686}
687
688void SkPictureRecord::endRecording() {
689    SkASSERT(kNoInitialSave != fInitialSaveCount);
690    this->restoreToCount(fInitialSaveCount);
691}
692
693void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
694    if (fRestoreOffsetStack.isEmpty()) {
695        return;
696    }
697
698    if (regionOpExpands(op)) {
699        // Run back through any previous clip ops, and mark their offset to
700        // be 0, disabling their ability to trigger a jump-to-restore, otherwise
701        // they could hide this clips ability to expand the clip (i.e. go from
702        // empty to non-empty).
703        fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
704    }
705
706    size_t offset = fWriter.size();
707    // The RestoreOffset field is initially filled with a placeholder
708    // value that points to the offset of the previous RestoreOffset
709    // in the current stack level, thus forming a linked list so that
710    // the restore offsets can be filled in when the corresponding
711    // restore command is recorded.
712    addInt(fRestoreOffsetStack.top());
713    fRestoreOffsetStack.top() = offset;
714}
715
716bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
717    // id + rect + clip params
718    uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
719    // recordRestoreOffsetPlaceholder doesn't always write an offset
720    if (!fRestoreOffsetStack.isEmpty()) {
721        // + restore offset
722        size += kUInt32Size;
723    }
724    uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
725    addRect(rect);
726    addInt(ClipParams_pack(op, doAA));
727    recordRestoreOffsetPlaceholder(op);
728
729    validate(initialOffset, size);
730    return this->INHERITED::clipRect(rect, op, doAA);
731}
732
733bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
734    if (rrect.isRect()) {
735        return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
736    }
737
738    // op + rrect + clip params
739    uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
740    // recordRestoreOffsetPlaceholder doesn't always write an offset
741    if (!fRestoreOffsetStack.isEmpty()) {
742        // + restore offset
743        size += kUInt32Size;
744    }
745    uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
746    addRRect(rrect);
747    addInt(ClipParams_pack(op, doAA));
748    recordRestoreOffsetPlaceholder(op);
749
750    validate(initialOffset, size);
751
752    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
753        return this->INHERITED::clipRect(rrect.getBounds(), op, doAA);
754    } else {
755        return this->INHERITED::clipRRect(rrect, op, doAA);
756    }
757}
758
759bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
760
761    SkRect r;
762    if (!path.isInverseFillType() && path.isRect(&r)) {
763        return this->clipRect(r, op, doAA);
764    }
765
766    // op + path index + clip params
767    uint32_t size = 3 * kUInt32Size;
768    // recordRestoreOffsetPlaceholder doesn't always write an offset
769    if (!fRestoreOffsetStack.isEmpty()) {
770        // + restore offset
771        size += kUInt32Size;
772    }
773    uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
774    addPath(path);
775    addInt(ClipParams_pack(op, doAA));
776    recordRestoreOffsetPlaceholder(op);
777
778    validate(initialOffset, size);
779
780    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
781        return this->INHERITED::clipRect(path.getBounds(), op, doAA);
782    } else {
783        return this->INHERITED::clipPath(path, op, doAA);
784    }
785}
786
787bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
788    // op + region index + clip params
789    uint32_t size = 3 * kUInt32Size;
790    // recordRestoreOffsetPlaceholder doesn't always write an offset
791    if (!fRestoreOffsetStack.isEmpty()) {
792        // + restore offset
793        size += kUInt32Size;
794    }
795    uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
796    addRegion(region);
797    addInt(ClipParams_pack(op, false));
798    recordRestoreOffsetPlaceholder(op);
799
800    validate(initialOffset, size);
801    return this->INHERITED::clipRegion(region, op);
802}
803
804void SkPictureRecord::clear(SkColor color) {
805    // op + color
806    uint32_t size = 2 * kUInt32Size;
807    uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
808    addInt(color);
809    validate(initialOffset, size);
810}
811
812void SkPictureRecord::drawPaint(const SkPaint& paint) {
813    // op + paint index
814    uint32_t size = 2 * kUInt32Size;
815    uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
816    SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
817    addPaint(paint);
818    validate(initialOffset, size);
819}
820
821void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
822                                 const SkPaint& paint) {
823    // op + paint index + mode + count + point data
824    uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
825    uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
826    SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
827    addPaint(paint);
828    addInt(mode);
829    addInt(count);
830    fWriter.writeMul4(pts, count * sizeof(SkPoint));
831    validate(initialOffset, size);
832}
833
834void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
835    // op + paint index + rect
836    uint32_t size = 2 * kUInt32Size + sizeof(oval);
837    uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
838    SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
839    addPaint(paint);
840    addRect(oval);
841    validate(initialOffset, size);
842}
843
844void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
845    // op + paint index + rect
846    uint32_t size = 2 * kUInt32Size + sizeof(rect);
847    uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
848    SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
849    addPaint(paint);
850    addRect(rect);
851    validate(initialOffset, size);
852}
853
854void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
855    uint32_t initialOffset, size;
856    if (rrect.isRect()) {
857        // op + paint index + rect
858        size = 2 * kUInt32Size + sizeof(SkRect);
859        initialOffset = this->addDraw(DRAW_RECT, &size);
860        SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
861        addPaint(paint);
862        addRect(rrect.getBounds());
863    } else if (rrect.isOval()) {
864        // op + paint index + rect
865        size = 2 * kUInt32Size + sizeof(SkRect);
866        initialOffset = this->addDraw(DRAW_OVAL, &size);
867        SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
868        addPaint(paint);
869        addRect(rrect.getBounds());
870    } else {
871        // op + paint index + rrect
872        size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
873        initialOffset = this->addDraw(DRAW_RRECT, &size);
874        SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
875        addPaint(paint);
876        addRRect(rrect);
877    }
878    validate(initialOffset, size);
879}
880
881void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
882    // op + paint index + path index
883    uint32_t size = 3 * kUInt32Size;
884    uint32_t initialOffset = this->addDraw(DRAW_PATH, &size);
885    SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
886    addPaint(paint);
887    addPath(path);
888    validate(initialOffset, size);
889}
890
891void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
892                        const SkPaint* paint = NULL) {
893    // op + paint index + bitmap index + left + top
894    uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
895    uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
896    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
897    addPaintPtr(paint);
898    addBitmap(bitmap);
899    addScalar(left);
900    addScalar(top);
901    validate(initialOffset, size);
902}
903
904void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
905                            const SkRect& dst, const SkPaint* paint) {
906    // id + paint index + bitmap index + bool for 'src'
907    uint32_t size = 4 * kUInt32Size;
908    if (NULL != src) {
909        size += sizeof(*src);   // + rect
910    }
911    size += sizeof(dst);        // + rect
912
913    uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
914    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
915    addPaintPtr(paint);
916    addBitmap(bitmap);
917    addRectPtr(src);  // may be null
918    addRect(dst);
919    validate(initialOffset, size);
920}
921
922void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
923                                       const SkPaint* paint) {
924    // id + paint index + bitmap index + matrix index
925    uint32_t size = 4 * kUInt32Size;
926    uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
927    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
928    addPaintPtr(paint);
929    addBitmap(bitmap);
930    addMatrix(matrix);
931    validate(initialOffset, size);
932}
933
934void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
935                                     const SkRect& dst, const SkPaint* paint) {
936    // op + paint index + bitmap id + center + dst rect
937    uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
938    uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
939    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
940    addPaintPtr(paint);
941    addBitmap(bitmap);
942    addIRect(center);
943    addRect(dst);
944    validate(initialOffset, size);
945}
946
947void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
948                        const SkPaint* paint = NULL) {
949    // op + paint index + bitmap index + left + top
950    uint32_t size = 5 * kUInt32Size;
951    uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
952    SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
953    addPaintPtr(paint);
954    addBitmap(bitmap);
955    addInt(left);
956    addInt(top);
957    validate(initialOffset, size);
958}
959
960// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
961// tweaked by paint.computeFastBounds().
962//
963static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
964    SkPaint::FontMetrics metrics;
965    paint.getFontMetrics(&metrics);
966    SkRect bounds;
967    // construct a rect so we can see any adjustments from the paint.
968    // we use 0,1 for left,right, just so the rect isn't empty
969    bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
970    (void)paint.computeFastBounds(bounds, &bounds);
971    topbot[0] = bounds.fTop;
972    topbot[1] = bounds.fBottom;
973}
974
975void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
976                                              SkScalar minY, SkScalar maxY) {
977    if (!flat.isTopBotWritten()) {
978        computeFontMetricsTopBottom(paint, flat.writableTopBot());
979        SkASSERT(flat.isTopBotWritten());
980    }
981    addScalar(flat.topBot()[0] + minY);
982    addScalar(flat.topBot()[1] + maxY);
983}
984
985void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
986                      SkScalar y, const SkPaint& paint) {
987    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
988
989    // op + paint index + length + 'length' worth of chars + x + y
990    uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
991    if (fast) {
992        size += 2 * sizeof(SkScalar); // + top & bottom
993    }
994
995    DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
996    uint32_t initialOffset = this->addDraw(op, &size);
997    SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
998    const SkFlatData* flatPaintData = addPaint(paint);
999    SkASSERT(flatPaintData);
1000    addText(text, byteLength);
1001    addScalar(x);
1002    addScalar(y);
1003    if (fast) {
1004        addFontMetricsTopBottom(paint, *flatPaintData, y, y);
1005    }
1006    validate(initialOffset, size);
1007}
1008
1009void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
1010                         const SkPoint pos[], const SkPaint& paint) {
1011    size_t points = paint.countText(text, byteLength);
1012    if (0 == points)
1013        return;
1014
1015    bool canUseDrawH = true;
1016    SkScalar minY = pos[0].fY;
1017    SkScalar maxY = pos[0].fY;
1018    // check if the caller really should have used drawPosTextH()
1019    {
1020        const SkScalar firstY = pos[0].fY;
1021        for (size_t index = 1; index < points; index++) {
1022            if (pos[index].fY != firstY) {
1023                canUseDrawH = false;
1024                if (pos[index].fY < minY) {
1025                    minY = pos[index].fY;
1026                } else if (pos[index].fY > maxY) {
1027                    maxY = pos[index].fY;
1028                }
1029            }
1030        }
1031    }
1032
1033    bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
1034    bool fast = canUseDrawH && fastBounds;
1035
1036    // op + paint index + length + 'length' worth of data + num points
1037    uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1038    if (canUseDrawH) {
1039        if (fast) {
1040            size += 2 * sizeof(SkScalar); // + top & bottom
1041        }
1042        // + y-pos + actual x-point data
1043        size += sizeof(SkScalar) + points * sizeof(SkScalar);
1044    } else {
1045        // + x&y point data
1046        size += points * sizeof(SkPoint);
1047        if (fastBounds) {
1048            size += 2 * sizeof(SkScalar); // + top & bottom
1049        }
1050    }
1051
1052    DrawType op;
1053    if (fast) {
1054        op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1055    } else if (canUseDrawH) {
1056        op = DRAW_POS_TEXT_H;
1057    } else if (fastBounds) {
1058        op = DRAW_POS_TEXT_TOP_BOTTOM;
1059    } else {
1060        op = DRAW_POS_TEXT;
1061    }
1062    uint32_t initialOffset = this->addDraw(op, &size);
1063    SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
1064    const SkFlatData* flatPaintData = addPaint(paint);
1065    SkASSERT(flatPaintData);
1066    addText(text, byteLength);
1067    addInt(points);
1068
1069#ifdef SK_DEBUG_SIZE
1070    size_t start = fWriter.size();
1071#endif
1072    if (canUseDrawH) {
1073        if (fast) {
1074            addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
1075        }
1076        addScalar(pos[0].fY);
1077        SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
1078        for (size_t index = 0; index < points; index++)
1079            *xptr++ = pos[index].fX;
1080    } else {
1081        fWriter.writeMul4(pos, points * sizeof(SkPoint));
1082        if (fastBounds) {
1083            addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
1084        }
1085    }
1086#ifdef SK_DEBUG_SIZE
1087    fPointBytes += fWriter.size() - start;
1088    fPointWrites += points;
1089#endif
1090    validate(initialOffset, size);
1091}
1092
1093void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1094                          const SkScalar xpos[], SkScalar constY,
1095                          const SkPaint& paint) {
1096    size_t points = paint.countText(text, byteLength);
1097    if (0 == points)
1098        return;
1099
1100    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
1101
1102    // op + paint index + length + 'length' worth of data + num points
1103    uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1104    if (fast) {
1105        size += 2 * sizeof(SkScalar); // + top & bottom
1106    }
1107    // + y + the actual points
1108    size += 1 * kUInt32Size + points * sizeof(SkScalar);
1109
1110    uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
1111                                           &size);
1112    const SkFlatData* flatPaintData = addPaint(paint);
1113    SkASSERT(flatPaintData);
1114    addText(text, byteLength);
1115    addInt(points);
1116
1117#ifdef SK_DEBUG_SIZE
1118    size_t start = fWriter.size();
1119#endif
1120    if (fast) {
1121        addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
1122    }
1123    addScalar(constY);
1124    fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1125#ifdef SK_DEBUG_SIZE
1126    fPointBytes += fWriter.size() - start;
1127    fPointWrites += points;
1128#endif
1129    validate(initialOffset, size);
1130}
1131
1132void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1133                            const SkPath& path, const SkMatrix* matrix,
1134                            const SkPaint& paint) {
1135    // op + paint index + length + 'length' worth of data + path index + matrix index
1136    uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1137    uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
1138    SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
1139    addPaint(paint);
1140    addText(text, byteLength);
1141    addPath(path);
1142    addMatrixPtr(matrix);
1143    validate(initialOffset, size);
1144}
1145
1146void SkPictureRecord::drawPicture(SkPicture& picture) {
1147    // op + picture index
1148    uint32_t size = 2 * kUInt32Size;
1149    uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
1150    addPicture(picture);
1151    validate(initialOffset, size);
1152}
1153
1154void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1155                          const SkPoint vertices[], const SkPoint texs[],
1156                          const SkColor colors[], SkXfermode*,
1157                          const uint16_t indices[], int indexCount,
1158                          const SkPaint& paint) {
1159    uint32_t flags = 0;
1160    if (texs) {
1161        flags |= DRAW_VERTICES_HAS_TEXS;
1162    }
1163    if (colors) {
1164        flags |= DRAW_VERTICES_HAS_COLORS;
1165    }
1166    if (indexCount > 0) {
1167        flags |= DRAW_VERTICES_HAS_INDICES;
1168    }
1169
1170    // op + paint index + flags + vmode + vCount + vertices
1171    uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1172    if (flags & DRAW_VERTICES_HAS_TEXS) {
1173        size += vertexCount * sizeof(SkPoint);  // + uvs
1174    }
1175    if (flags & DRAW_VERTICES_HAS_COLORS) {
1176        size += vertexCount * sizeof(SkColor);  // + vert colors
1177    }
1178    if (flags & DRAW_VERTICES_HAS_INDICES) {
1179        // + num indices + indices
1180        size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1181    }
1182
1183    uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
1184    SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
1185    addPaint(paint);
1186    addInt(flags);
1187    addInt(vmode);
1188    addInt(vertexCount);
1189    addPoints(vertices, vertexCount);
1190    if (flags & DRAW_VERTICES_HAS_TEXS) {
1191        addPoints(texs, vertexCount);
1192    }
1193    if (flags & DRAW_VERTICES_HAS_COLORS) {
1194        fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1195    }
1196    if (flags & DRAW_VERTICES_HAS_INDICES) {
1197        addInt(indexCount);
1198        fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1199    }
1200    validate(initialOffset, size);
1201}
1202
1203void SkPictureRecord::drawData(const void* data, size_t length) {
1204    // op + length + 'length' worth of data
1205    uint32_t size = 2 * kUInt32Size + SkAlign4(length);
1206    uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
1207    addInt(length);
1208    fWriter.writePad(data, length);
1209    validate(initialOffset, size);
1210}
1211
1212///////////////////////////////////////////////////////////////////////////////
1213
1214void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
1215    const int index = fBitmapHeap->insert(bitmap);
1216    // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1217    // release builds, the invalid value will be recorded so that the reader will know that there
1218    // was a problem.
1219    SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1220    addInt(index);
1221}
1222
1223void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1224    addMatrixPtr(&matrix);
1225}
1226
1227void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
1228    this->addInt(matrix ? fMatrices.find(*matrix) : 0);
1229}
1230
1231const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1232    const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1233    int index = data ? data->index() : 0;
1234    this->addInt(index);
1235    return data;
1236}
1237
1238void SkPictureRecord::addPath(const SkPath& path) {
1239    if (NULL == fPathHeap) {
1240        fPathHeap = SkNEW(SkPathHeap);
1241    }
1242    addInt(fPathHeap->append(path));
1243}
1244
1245void SkPictureRecord::addPicture(SkPicture& picture) {
1246    int index = fPictureRefs.find(&picture);
1247    if (index < 0) {    // not found
1248        index = fPictureRefs.count();
1249        *fPictureRefs.append() = &picture;
1250        picture.ref();
1251    }
1252    // follow the convention of recording a 1-based index
1253    addInt(index + 1);
1254}
1255
1256void SkPictureRecord::addPoint(const SkPoint& point) {
1257#ifdef SK_DEBUG_SIZE
1258    size_t start = fWriter.size();
1259#endif
1260    fWriter.writePoint(point);
1261#ifdef SK_DEBUG_SIZE
1262    fPointBytes += fWriter.size() - start;
1263    fPointWrites++;
1264#endif
1265}
1266
1267void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1268    fWriter.writeMul4(pts, count * sizeof(SkPoint));
1269#ifdef SK_DEBUG_SIZE
1270    fPointBytes += count * sizeof(SkPoint);
1271    fPointWrites++;
1272#endif
1273}
1274
1275void SkPictureRecord::addRect(const SkRect& rect) {
1276#ifdef SK_DEBUG_SIZE
1277    size_t start = fWriter.size();
1278#endif
1279    fWriter.writeRect(rect);
1280#ifdef SK_DEBUG_SIZE
1281    fRectBytes += fWriter.size() - start;
1282    fRectWrites++;
1283#endif
1284}
1285
1286void SkPictureRecord::addRectPtr(const SkRect* rect) {
1287    if (fWriter.writeBool(rect != NULL)) {
1288        fWriter.writeRect(*rect);
1289    }
1290}
1291
1292void SkPictureRecord::addIRect(const SkIRect& rect) {
1293    fWriter.write(&rect, sizeof(rect));
1294}
1295
1296void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1297    if (fWriter.writeBool(rect != NULL)) {
1298        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1299    }
1300}
1301
1302void SkPictureRecord::addRRect(const SkRRect& rrect) {
1303    fWriter.writeRRect(rrect);
1304}
1305
1306void SkPictureRecord::addRegion(const SkRegion& region) {
1307    addInt(fRegions.find(region));
1308}
1309
1310void SkPictureRecord::addText(const void* text, size_t byteLength) {
1311#ifdef SK_DEBUG_SIZE
1312    size_t start = fWriter.size();
1313#endif
1314    addInt(byteLength);
1315    fWriter.writePad(text, byteLength);
1316#ifdef SK_DEBUG_SIZE
1317    fTextBytes += fWriter.size() - start;
1318    fTextWrites++;
1319#endif
1320}
1321
1322///////////////////////////////////////////////////////////////////////////////
1323
1324#ifdef SK_DEBUG_SIZE
1325size_t SkPictureRecord::size() const {
1326    size_t result = 0;
1327    size_t sizeData;
1328    bitmaps(&sizeData);
1329    result += sizeData;
1330    matrices(&sizeData);
1331    result += sizeData;
1332    paints(&sizeData);
1333    result += sizeData;
1334    paths(&sizeData);
1335    result += sizeData;
1336    pictures(&sizeData);
1337    result += sizeData;
1338    regions(&sizeData);
1339    result += sizeData;
1340    result += streamlen();
1341    return result;
1342}
1343
1344int SkPictureRecord::bitmaps(size_t* size) const {
1345    size_t result = 0;
1346    int count = fBitmaps.count();
1347    for (int index = 0; index < count; index++)
1348        result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1349    *size = result;
1350    return count;
1351}
1352
1353int SkPictureRecord::matrices(size_t* size) const {
1354    int count = fMatrices.count();
1355    *size = sizeof(fMatrices[0]) * count;
1356    return count;
1357}
1358
1359int SkPictureRecord::paints(size_t* size) const {
1360    size_t result = 0;
1361    int count = fPaints.count();
1362    for (int index = 0; index < count; index++)
1363        result += sizeof(fPaints[index]) + fPaints[index]->size();
1364    *size = result;
1365    return count;
1366}
1367
1368int SkPictureRecord::paths(size_t* size) const {
1369    size_t result = 0;
1370    int count = fPaths.count();
1371    for (int index = 0; index < count; index++)
1372        result += sizeof(fPaths[index]) + fPaths[index]->size();
1373    *size = result;
1374    return count;
1375}
1376
1377int SkPictureRecord::regions(size_t* size) const {
1378    size_t result = 0;
1379    int count = fRegions.count();
1380    for (int index = 0; index < count; index++)
1381        result += sizeof(fRegions[index]) + fRegions[index]->size();
1382    *size = result;
1383    return count;
1384}
1385
1386size_t SkPictureRecord::streamlen() const {
1387    return fWriter.size();
1388}
1389#endif
1390
1391#ifdef SK_DEBUG_VALIDATE
1392void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1393    SkASSERT(fWriter.size() == initialOffset + size);
1394
1395    validateBitmaps();
1396    validateMatrices();
1397    validatePaints();
1398    validatePaths();
1399    validateRegions();
1400}
1401
1402void SkPictureRecord::validateBitmaps() const {
1403    int count = fBitmapHeap->count();
1404    SkASSERT((unsigned) count < 0x1000);
1405    for (int index = 0; index < count; index++) {
1406        const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
1407        SkASSERT(bitPtr);
1408        bitPtr->validate();
1409    }
1410}
1411
1412void SkPictureRecord::validateMatrices() const {
1413    int count = fMatrices.count();
1414    SkASSERT((unsigned) count < 0x1000);
1415    for (int index = 0; index < count; index++) {
1416        const SkFlatData* matrix = fMatrices[index];
1417        SkASSERT(matrix);
1418//        matrix->validate();
1419    }
1420}
1421
1422void SkPictureRecord::validatePaints() const {
1423    int count = fPaints.count();
1424    SkASSERT((unsigned) count < 0x1000);
1425    for (int index = 0; index < count; index++) {
1426        const SkFlatData* paint = fPaints[index];
1427        SkASSERT(paint);
1428//            paint->validate();
1429    }
1430}
1431
1432void SkPictureRecord::validatePaths() const {
1433    if (NULL == fPathHeap) {
1434        return;
1435    }
1436
1437    int count = fPathHeap->count();
1438    SkASSERT((unsigned) count < 0x1000);
1439    for (int index = 0; index < count; index++) {
1440        const SkPath& path = (*fPathHeap)[index];
1441        path.validate();
1442    }
1443}
1444
1445void SkPictureRecord::validateRegions() const {
1446    int count = fRegions.count();
1447    SkASSERT((unsigned) count < 0x1000);
1448    for (int index = 0; index < count; index++) {
1449        const SkFlatData* region = fRegions[index];
1450        SkASSERT(region);
1451//        region->validate();
1452    }
1453}
1454#endif
1455