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 "SkDevice.h"
14#include "SkPictureStateTree.h"
15
16#define MIN_WRITER_SIZE 16384
17#define HEAP_BLOCK_SIZE 4096
18
19enum {
20    // just need a value that save or getSaveCount would never return
21    kNoInitialSave = -1,
22};
23
24// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
25static int const kUInt32Size = 4;
26
27static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
28static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
29
30SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
31        INHERITED(device),
32        fBoundingHierarchy(NULL),
33        fStateTree(NULL),
34        fFlattenableHeap(HEAP_BLOCK_SIZE),
35        fMatrices(&fFlattenableHeap),
36        fPaints(&fFlattenableHeap),
37        fRegions(&fFlattenableHeap),
38        fWriter(MIN_WRITER_SIZE),
39        fRecordFlags(flags) {
40#ifdef SK_DEBUG_SIZE
41    fPointBytes = fRectBytes = fTextBytes = 0;
42    fPointWrites = fRectWrites = fTextWrites = 0;
43#endif
44
45    fRestoreOffsetStack.setReserve(32);
46
47    fBitmapHeap = SkNEW(SkBitmapHeap);
48    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
49    fPathHeap = NULL;   // lazy allocate
50    fFirstSavedLayerIndex = kNoSavedLayerIndex;
51
52    fInitialSaveCount = kNoInitialSave;
53}
54
55SkPictureRecord::~SkPictureRecord() {
56    SkSafeUnref(fBitmapHeap);
57    SkSafeUnref(fPathHeap);
58    SkSafeUnref(fBoundingHierarchy);
59    SkSafeUnref(fStateTree);
60    fFlattenableHeap.setBitmapStorage(NULL);
61    fPictureRefs.unrefAll();
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66// Return the offset of the paint inside a given op's byte stream. A zero
67// return value means there is no paint (and you really shouldn't be calling
68// this method)
69static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) {
70    // These offsets are where the paint would be if the op size doesn't overflow
71    static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
72        0,  // UNUSED - no paint
73        0,  // CLIP_PATH - no paint
74        0,  // CLIP_REGION - no paint
75        0,  // CLIP_RECT - no paint
76        0,  // CLIP_RRECT - no paint
77        0,  // CONCAT - no paint
78        1,  // DRAW_BITMAP - right after op code
79        1,  // DRAW_BITMAP_MATRIX - right after op code
80        1,  // DRAW_BITMAP_NINE - right after op code
81        1,  // DRAW_BITMAP_RECT_TO_RECT - right after op code
82        0,  // DRAW_CLEAR - no paint
83        0,  // DRAW_DATA - no paint
84        1,  // DRAW_OVAL - right after op code
85        1,  // DRAW_PAINT - right after op code
86        1,  // DRAW_PATH - right after op code
87        0,  // DRAW_PICTURE - no paint
88        1,  // DRAW_POINTS - right after op code
89        1,  // DRAW_POS_TEXT - right after op code
90        1,  // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
91        1,  // DRAW_POS_TEXT_H - right after op code
92        1,  // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
93        1,  // DRAW_RECT - right after op code
94        1,  // DRAW_RRECT - right after op code
95        1,  // DRAW_SPRITE - right after op code
96        1,  // DRAW_TEXT - right after op code
97        1,  // DRAW_TEXT_ON_PATH - right after op code
98        1,  // DRAW_TEXT_TOP_BOTTOM - right after op code
99        1,  // DRAW_VERTICES - right after op code
100        0,  // RESTORE - no paint
101        0,  // ROTATE - no paint
102        0,  // SAVE - no paint
103        0,  // SAVE_LAYER - see below - this paint's location varies
104        0,  // SCALE - no paint
105        0,  // SET_MATRIX - no paint
106        0,  // SKEW - no paint
107        0,  // TRANSLATE - no paint
108        0,  // NOOP - no paint
109        0,  // BEGIN_GROUP - no paint
110        0,  // COMMENT - no paint
111        0,  // END_GROUP - no paint
112    };
113
114    SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
115    SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
116
117    int overflow = 0;
118    if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
119        // This op's size overflows so an extra uint32_t will be written
120        // after the op code
121        overflow = sizeof(uint32_t);
122    }
123
124    if (SAVE_LAYER == op) {
125        static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
126        static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
127
128        if (kSaveLayerNoBoundsSize == opSize) {
129            return kSaveLayerNoBoundsPaintOffset + overflow;
130        } else {
131            SkASSERT(kSaveLayerWithBoundsSize == opSize);
132            return kSaveLayerWithBoundsPaintOffset + overflow;
133        }
134    }
135
136    SkASSERT(0 != gPaintOffsets[op]);   // really shouldn't be calling this method
137    return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
138}
139
140SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
141    SkASSERT(!"eeek, don't try to change the device on a recording canvas");
142    return this->INHERITED::setDevice(device);
143}
144
145int SkPictureRecord::save(SaveFlags flags) {
146    // record the offset to us, making it non-positive to distinguish a save
147    // from a clip entry.
148    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
149
150    // op + flags
151    uint32_t size = 2 * kUInt32Size;
152    uint32_t initialOffset = this->addDraw(SAVE, &size);
153    addInt(flags);
154
155    validate(initialOffset, size);
156    return this->INHERITED::save(flags);
157}
158
159int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
160                               SaveFlags flags) {
161    // record the offset to us, making it non-positive to distinguish a save
162    // from a clip entry.
163    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
164
165    // op + bool for 'bounds'
166    uint32_t size = 2 * kUInt32Size;
167    if (NULL != bounds) {
168        size += sizeof(*bounds); // + rect
169    }
170    // + paint index + flags
171    size += 2 * kUInt32Size;
172
173    SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
174
175    uint32_t initialOffset = this->addDraw(SAVE_LAYER, &size);
176    addRectPtr(bounds);
177    SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size());
178    addPaintPtr(paint);
179    addInt(flags);
180
181    if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
182        fFirstSavedLayerIndex = fRestoreOffsetStack.count();
183    }
184
185    validate(initialOffset, size);
186    /*  Don't actually call saveLayer, because that will try to allocate an
187        offscreen device (potentially very big) which we don't actually need
188        at this time (and may not be able to afford since during record our
189        clip starts out the size of the picture, which is often much larger
190        than the size of the actual device we'll use during playback).
191     */
192    int count = this->INHERITED::save(flags);
193    this->clipRectBounds(bounds, flags, NULL);
194    return count;
195}
196
197bool SkPictureRecord::isDrawingToLayer() const {
198    return fFirstSavedLayerIndex != kNoSavedLayerIndex;
199}
200
201/*
202 * Read the op code from 'offset' in 'writer' and extract the size too.
203 */
204static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
205    uint32_t* peek = writer->peek32(offset);
206
207    uint32_t op;
208    UNPACK_8_24(*peek, op, *size);
209    if (MASK_24 == *size) {
210        // size required its own slot right after the op code
211        *size = *writer->peek32(offset+kUInt32Size);
212    }
213    return (DrawType) op;
214}
215
216#ifdef TRACK_COLLAPSE_STATS
217    static int gCollapseCount, gCollapseCalls;
218#endif
219
220// Is the supplied paint simply a color?
221static bool is_simple(const SkPaint& p) {
222    intptr_t orAccum = (intptr_t)p.getPathEffect()  |
223                       (intptr_t)p.getShader()      |
224                       (intptr_t)p.getXfermode()    |
225                       (intptr_t)p.getMaskFilter()  |
226                       (intptr_t)p.getColorFilter() |
227                       (intptr_t)p.getRasterizer()  |
228                       (intptr_t)p.getLooper()      |
229                       (intptr_t)p.getImageFilter();
230    return 0 == orAccum;
231}
232
233// CommandInfos are fed to the 'match' method and filled in with command
234// information.
235struct CommandInfo {
236    DrawType fActualOp;
237    uint32_t fOffset;
238    uint32_t fSize;
239};
240
241/*
242 * Attempt to match the provided pattern of commands starting at 'offset'
243 * in the byte stream and stopping at the end of the stream. Upon success,
244 * return true with all the pattern information filled out in the result
245 * array (i.e., actual ops, offsets and sizes).
246 * Note this method skips any NOOPs seen in the stream
247 */
248static bool match(SkWriter32* writer, uint32_t offset,
249                  int* pattern, CommandInfo* result, int numCommands) {
250    SkASSERT(offset < writer->size());
251
252    uint32_t curOffset = offset;
253    uint32_t curSize = 0;
254    int numMatched;
255    for (numMatched = 0; numMatched < numCommands && curOffset < writer->size(); ++numMatched) {
256        DrawType op = peek_op_and_size(writer, curOffset, &curSize);
257        while (NOOP == op && curOffset < writer->size()) {
258            curOffset += curSize;
259            op = peek_op_and_size(writer, curOffset, &curSize);
260        }
261
262        if (curOffset >= writer->size()) {
263            return false; // ran out of byte stream
264        }
265
266        if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
267            if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
268                DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
269                return false;
270            }
271        } else if (op != pattern[numMatched]) {
272            return false;
273        }
274
275        result[numMatched].fActualOp = op;
276        result[numMatched].fOffset = curOffset;
277        result[numMatched].fSize = curSize;
278
279        curOffset += curSize;
280    }
281
282    if (numMatched != numCommands) {
283        return false;
284    }
285
286    curOffset += curSize;
287    if (curOffset < writer->size()) {
288        // Something else between the last command and the end of the stream
289        return false;
290    }
291
292    return true;
293}
294
295// temporarily here to make code review easier
296static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
297                                                 SkPaintDictionary* paintDict,
298                                                 const CommandInfo& saveLayerInfo,
299                                                 const CommandInfo& dbmInfo);
300
301/*
302 * Restore has just been called (but not recorded), look back at the
303 * matching save* and see if we are in the configuration:
304 *   SAVE_LAYER
305 *       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
306 *   RESTORE
307 * where the saveLayer's color can be moved into the drawBitmap*'s paint
308 */
309static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
310                               SkPaintDictionary* paintDict) {
311    // back up to the save block
312    // TODO: add a stack to track save*/restore offsets rather than searching backwards
313    while (offset > 0) {
314        offset = *writer->peek32(offset);
315    }
316
317    int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
318    CommandInfo result[SK_ARRAY_COUNT(pattern)];
319
320    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
321        return false;
322    }
323
324    if (kSaveLayerWithBoundsSize == result[0].fSize) {
325        // The saveLayer's bound can offset where the dbm is drawn
326        return false;
327    }
328
329
330    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
331                                                result[0], result[1]);
332}
333
334/*
335 * Convert the command code located at 'offset' to a NOOP. Leave the size
336 * field alone so the NOOP can be skipped later.
337 */
338static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
339    uint32_t* ptr = writer->peek32(offset);
340    *ptr = (*ptr & MASK_24) | (NOOP << 24);
341}
342
343/*
344 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
345 * Return true on success; false otherwise.
346 */
347static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
348                                                 SkPaintDictionary* paintDict,
349                                                 const CommandInfo& saveLayerInfo,
350                                                 const CommandInfo& dbmInfo) {
351    SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
352    SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
353             DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
354             DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
355             DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
356
357    uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
358    uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
359
360    // we have a match, now we need to get the paints involved
361    uint32_t dbmPaintId = *writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
362    uint32_t saveLayerPaintId = *writer->peek32(saveLayerInfo.fOffset+slPaintOffset);
363
364    if (0 == saveLayerPaintId) {
365        // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
366        // and signal the caller (by returning true) to not add the RESTORE op
367        convert_command_to_noop(writer, saveLayerInfo.fOffset);
368        return true;
369    }
370
371    if (0 == dbmPaintId) {
372        // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
373        // and signal the caller (by returning true) to not add the RESTORE op
374        convert_command_to_noop(writer, saveLayerInfo.fOffset);
375        uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
376        SkASSERT(0 == *ptr);
377        *ptr = saveLayerPaintId;
378        return true;
379    }
380
381    SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
382    if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
383        return false;
384    }
385
386    // For this optimization we only fold the saveLayer and drawBitmapRect
387    // together if the saveLayer's draw is simple (i.e., no fancy effects) and
388    // and the only difference in the colors is that the saveLayer's can have
389    // an alpha while the drawBitmapRect's is opaque.
390    // TODO: it should be possible to fold them together even if they both
391    // have different non-255 alphas
392    SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
393
394    SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
395    if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) {
396        return false;
397    }
398
399    SkColor newColor = SkColorSetA(dbmPaint->getColor(),
400                                   SkColorGetA(saveLayerPaint->getColor()));
401    dbmPaint->setColor(newColor);
402
403    const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
404    if (NULL == data) {
405        return false;
406    }
407
408    // kill the saveLayer and alter the DBMR2R's paint to be the modified one
409    convert_command_to_noop(writer, saveLayerInfo.fOffset);
410    uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
411    SkASSERT(dbmPaintId == *ptr);
412    *ptr = data->index();
413    return true;
414}
415
416/*
417 * Restore has just been called (but not recorded), look back at the
418 * matching save* and see if we are in the configuration:
419 *   SAVE_LAYER (with NULL == bounds)
420 *      SAVE
421 *         CLIP_RECT
422 *         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
423 *      RESTORE
424 *   RESTORE
425 * where the saveLayer's color can be moved into the drawBitmap*'s paint
426 */
427static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
428                               SkPaintDictionary* paintDict) {
429
430    // back up to the save block
431    // TODO: add a stack to track save*/restore offsets rather than searching backwards
432    while (offset > 0) {
433        offset = *writer->peek32(offset);
434    }
435
436    int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
437    CommandInfo result[SK_ARRAY_COUNT(pattern)];
438
439    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
440        return false;
441    }
442
443    if (kSaveLayerWithBoundsSize == result[0].fSize) {
444        // The saveLayer's bound can offset where the dbm is drawn
445        return false;
446    }
447
448    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
449                                                result[0], result[3]);
450}
451
452/*
453 *  Restore has just been called (but not recorded), so look back at the
454 *  matching save(), and see if we can eliminate the pair of them, due to no
455 *  intervening matrix/clip calls.
456 *
457 *  If so, update the writer and return true, in which case we won't even record
458 *  the restore() call. If we still need the restore(), return false.
459 */
460static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
461                                       SkPaintDictionary* paintDict) {
462#ifdef TRACK_COLLAPSE_STATS
463    gCollapseCalls += 1;
464#endif
465
466    int32_t restoreOffset = (int32_t)writer->size();
467
468    // back up to the save block
469    while (offset > 0) {
470        offset = *writer->peek32(offset);
471    }
472
473    // now offset points to a save
474    offset = -offset;
475    uint32_t opSize;
476    DrawType op = peek_op_and_size(writer, offset, &opSize);
477    if (SAVE_LAYER == op) {
478        // not ready to cull these out yet (mrr)
479        return false;
480    }
481    SkASSERT(SAVE == op);
482
483    // Walk forward until we get back to either a draw-verb (abort) or we hit
484    // our restore (success).
485    int32_t saveOffset = offset;
486
487    offset += opSize;
488    while (offset < restoreOffset) {
489        op = peek_op_and_size(writer, offset, &opSize);
490        if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
491            // drawing verb, abort
492            return false;
493        }
494        offset += opSize;
495    }
496
497#ifdef TRACK_COLLAPSE_STATS
498    gCollapseCount += 1;
499    SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
500             (double)gCollapseCount / gCollapseCalls, "%");
501#endif
502
503    writer->rewindToOffset(saveOffset);
504    return true;
505}
506
507typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
508                                     SkPaintDictionary* paintDict);
509enum PictureRecordOptType {
510    kRewind_OptType,  // Optimization rewinds the command stream
511    kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
512};
513
514struct PictureRecordOpt {
515    PictureRecordOptProc fProc;
516    PictureRecordOptType fType;
517};
518/*
519 * A list of the optimizations that are tried upon seeing a restore
520 * TODO: add a real API for such optimizations
521 *       Add the ability to fire optimizations on any op (not just RESTORE)
522 */
523static const PictureRecordOpt gPictureRecordOpts[] = {
524    { collapse_save_clip_restore, kRewind_OptType },
525    { remove_save_layer1,         kCollapseSaveLayer_OptType },
526    { remove_save_layer2,         kCollapseSaveLayer_OptType }
527};
528
529// This is called after an optimization has been applied to the command stream
530// in order to adjust the contents and state of the bounding box hierarchy and
531// state tree to reflect the optimization.
532static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
533                                      SkBBoxHierarchy* boundingHierarchy) {
534    switch (opt) {
535    case kCollapseSaveLayer_OptType:
536        if (NULL != stateTree) {
537            stateTree->saveCollapsed();
538        }
539        break;
540    case kRewind_OptType:
541        if (NULL != boundingHierarchy) {
542            boundingHierarchy->rewindInserts();
543        }
544        // Note: No need to touch the state tree for this to work correctly.
545        // Unused branches do not burden the playback, and pruning the tree
546        // would be O(N^2), so it is best to leave it alone.
547        break;
548    default:
549        SkASSERT(0);
550    }
551}
552
553void SkPictureRecord::restore() {
554    // FIXME: SkDeferredCanvas needs to be refactored to respect
555    // save/restore balancing so that the following test can be
556    // turned on permanently.
557#if 0
558    SkASSERT(fRestoreOffsetStack.count() > 1);
559#endif
560
561    // check for underflow
562    if (fRestoreOffsetStack.count() == 0) {
563        return;
564    }
565
566    if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
567        fFirstSavedLayerIndex = kNoSavedLayerIndex;
568    }
569
570    uint32_t initialOffset, size;
571    size_t opt = 0;
572    if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
573        for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
574            if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
575                // Some optimization fired so don't add the RESTORE
576                size = 0;
577                initialOffset = fWriter.size();
578                apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
579                                          fStateTree, fBoundingHierarchy);
580                break;
581            }
582        }
583    }
584
585    if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
586        SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
587        // No optimization fired so add the RESTORE
588        fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
589        size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
590        initialOffset = this->addDraw(RESTORE, &size);
591    }
592
593    fRestoreOffsetStack.pop();
594
595    validate(initialOffset, size);
596    return this->INHERITED::restore();
597}
598
599bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
600    // op + dx + dy
601    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
602    uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
603    addScalar(dx);
604    addScalar(dy);
605    validate(initialOffset, size);
606    return this->INHERITED::translate(dx, dy);
607}
608
609bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
610    // op + sx + sy
611    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
612    uint32_t initialOffset = this->addDraw(SCALE, &size);
613    addScalar(sx);
614    addScalar(sy);
615    validate(initialOffset, size);
616    return this->INHERITED::scale(sx, sy);
617}
618
619bool SkPictureRecord::rotate(SkScalar degrees) {
620    // op + degrees
621    uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
622    uint32_t initialOffset = this->addDraw(ROTATE, &size);
623    addScalar(degrees);
624    validate(initialOffset, size);
625    return this->INHERITED::rotate(degrees);
626}
627
628bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
629    // op + sx + sy
630    uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
631    uint32_t initialOffset = this->addDraw(SKEW, &size);
632    addScalar(sx);
633    addScalar(sy);
634    validate(initialOffset, size);
635    return this->INHERITED::skew(sx, sy);
636}
637
638bool SkPictureRecord::concat(const SkMatrix& matrix) {
639    validate(fWriter.size(), 0);
640    // op + matrix index
641    uint32_t size = 2 * kUInt32Size;
642    uint32_t initialOffset = this->addDraw(CONCAT, &size);
643    addMatrix(matrix);
644    validate(initialOffset, size);
645    return this->INHERITED::concat(matrix);
646}
647
648void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
649    validate(fWriter.size(), 0);
650    // op + matrix index
651    uint32_t size = 2 * kUInt32Size;
652    uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
653    addMatrix(matrix);
654    validate(initialOffset, size);
655    this->INHERITED::setMatrix(matrix);
656}
657
658static bool regionOpExpands(SkRegion::Op op) {
659    switch (op) {
660        case SkRegion::kUnion_Op:
661        case SkRegion::kXOR_Op:
662        case SkRegion::kReverseDifference_Op:
663        case SkRegion::kReplace_Op:
664            return true;
665        case SkRegion::kIntersect_Op:
666        case SkRegion::kDifference_Op:
667            return false;
668        default:
669            SkDEBUGFAIL("unknown region op");
670            return false;
671    }
672}
673
674void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
675    int32_t offset = fRestoreOffsetStack.top();
676    while (offset > 0) {
677        uint32_t* peek = fWriter.peek32(offset);
678        offset = *peek;
679        *peek = restoreOffset;
680    }
681
682#ifdef SK_DEBUG
683    // assert that the final offset value points to a save verb
684    uint32_t opSize;
685    DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
686    SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
687#endif
688}
689
690void SkPictureRecord::beginRecording() {
691    // we have to call this *after* our constructor, to ensure that it gets
692    // recorded. This is balanced by restoreToCount() call from endRecording,
693    // which in-turn calls our overridden restore(), so those get recorded too.
694    fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
695}
696
697void SkPictureRecord::endRecording() {
698    SkASSERT(kNoInitialSave != fInitialSaveCount);
699    this->restoreToCount(fInitialSaveCount);
700}
701
702void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
703    if (fRestoreOffsetStack.isEmpty()) {
704        return;
705    }
706
707    if (regionOpExpands(op)) {
708        // Run back through any previous clip ops, and mark their offset to
709        // be 0, disabling their ability to trigger a jump-to-restore, otherwise
710        // they could hide this clips ability to expand the clip (i.e. go from
711        // empty to non-empty).
712        fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
713    }
714
715    size_t offset = fWriter.size();
716    // The RestoreOffset field is initially filled with a placeholder
717    // value that points to the offset of the previous RestoreOffset
718    // in the current stack level, thus forming a linked list so that
719    // the restore offsets can be filled in when the corresponding
720    // restore command is recorded.
721    addInt(fRestoreOffsetStack.top());
722    fRestoreOffsetStack.top() = offset;
723}
724
725bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
726    // id + rect + clip params
727    uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
728    // recordRestoreOffsetPlaceholder doesn't always write an offset
729    if (!fRestoreOffsetStack.isEmpty()) {
730        // + restore offset
731        size += kUInt32Size;
732    }
733    uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
734    addRect(rect);
735    addInt(ClipParams_pack(op, doAA));
736    recordRestoreOffsetPlaceholder(op);
737
738    validate(initialOffset, size);
739    return this->INHERITED::clipRect(rect, op, doAA);
740}
741
742bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
743    if (rrect.isRect()) {
744        return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
745    }
746
747    // op + rrect + clip params
748    uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
749    // recordRestoreOffsetPlaceholder doesn't always write an offset
750    if (!fRestoreOffsetStack.isEmpty()) {
751        // + restore offset
752        size += kUInt32Size;
753    }
754    uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
755    addRRect(rrect);
756    addInt(ClipParams_pack(op, doAA));
757    recordRestoreOffsetPlaceholder(op);
758
759    validate(initialOffset, size);
760
761    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
762        return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
763    } else {
764        return this->INHERITED::clipRRect(rrect, op, doAA);
765    }
766}
767
768bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
769
770    SkRect r;
771    if (!path.isInverseFillType() && path.isRect(&r)) {
772        return this->clipRect(r, op, doAA);
773    }
774
775    // op + path index + clip params
776    uint32_t size = 3 * kUInt32Size;
777    // recordRestoreOffsetPlaceholder doesn't always write an offset
778    if (!fRestoreOffsetStack.isEmpty()) {
779        // + restore offset
780        size += kUInt32Size;
781    }
782    uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
783    addPath(path);
784    addInt(ClipParams_pack(op, doAA));
785    recordRestoreOffsetPlaceholder(op);
786
787    validate(initialOffset, size);
788
789    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
790        return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
791                                                         path.isInverseFillType());
792    } else {
793        return this->INHERITED::clipPath(path, op, doAA);
794    }
795}
796
797bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
798    // op + region index + clip params
799    uint32_t size = 3 * kUInt32Size;
800    // recordRestoreOffsetPlaceholder doesn't always write an offset
801    if (!fRestoreOffsetStack.isEmpty()) {
802        // + restore offset
803        size += kUInt32Size;
804    }
805    uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
806    addRegion(region);
807    addInt(ClipParams_pack(op, false));
808    recordRestoreOffsetPlaceholder(op);
809
810    validate(initialOffset, size);
811    return this->INHERITED::clipRegion(region, op);
812}
813
814void SkPictureRecord::clear(SkColor color) {
815    // op + color
816    uint32_t size = 2 * kUInt32Size;
817    uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
818    addInt(color);
819    validate(initialOffset, size);
820}
821
822void SkPictureRecord::drawPaint(const SkPaint& paint) {
823    // op + paint index
824    uint32_t size = 2 * kUInt32Size;
825    uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
826    SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
827    addPaint(paint);
828    validate(initialOffset, size);
829}
830
831void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
832                                 const SkPaint& paint) {
833    // op + paint index + mode + count + point data
834    uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
835    uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
836    SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
837    addPaint(paint);
838    addInt(mode);
839    addInt(count);
840    fWriter.writeMul4(pts, count * sizeof(SkPoint));
841    validate(initialOffset, size);
842}
843
844void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
845    // op + paint index + rect
846    uint32_t size = 2 * kUInt32Size + sizeof(oval);
847    uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
848    SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
849    addPaint(paint);
850    addRect(oval);
851    validate(initialOffset, size);
852}
853
854void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
855    // op + paint index + rect
856    uint32_t size = 2 * kUInt32Size + sizeof(rect);
857    uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
858    SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
859    addPaint(paint);
860    addRect(rect);
861    validate(initialOffset, size);
862}
863
864void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
865    if (rrect.isRect()) {
866        this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
867    } else if (rrect.isOval()) {
868        this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
869    } else {
870        // op + paint index + rrect
871        uint32_t initialOffset, size;
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        validate(initialOffset, size);
878    }
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
1212void SkPictureRecord::beginCommentGroup(const char* description) {
1213    // op/size + length of string + \0 terminated chars
1214    int length = strlen(description);
1215    uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1216    uint32_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1217    fWriter.writeString(description, length);
1218    validate(initialOffset, size);
1219}
1220
1221void SkPictureRecord::addComment(const char* kywd, const char* value) {
1222    // op/size + 2x length of string + 2x \0 terminated chars
1223    int kywdLen = strlen(kywd);
1224    int valueLen = strlen(value);
1225    uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1226    uint32_t initialOffset = this->addDraw(COMMENT, &size);
1227    fWriter.writeString(kywd, kywdLen);
1228    fWriter.writeString(value, valueLen);
1229    validate(initialOffset, size);
1230}
1231
1232void SkPictureRecord::endCommentGroup() {
1233    // op/size
1234    uint32_t size = 1 * kUInt32Size;
1235    uint32_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1236    validate(initialOffset, size);
1237}
1238
1239///////////////////////////////////////////////////////////////////////////////
1240
1241void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
1242    const int index = fBitmapHeap->insert(bitmap);
1243    // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1244    // release builds, the invalid value will be recorded so that the reader will know that there
1245    // was a problem.
1246    SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1247    addInt(index);
1248}
1249
1250void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1251    addMatrixPtr(&matrix);
1252}
1253
1254void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
1255    this->addInt(matrix ? fMatrices.find(*matrix) : 0);
1256}
1257
1258const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1259    const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1260    int index = data ? data->index() : 0;
1261    this->addInt(index);
1262    return data;
1263}
1264
1265void SkPictureRecord::addPath(const SkPath& path) {
1266    if (NULL == fPathHeap) {
1267        fPathHeap = SkNEW(SkPathHeap);
1268    }
1269    addInt(fPathHeap->append(path));
1270}
1271
1272void SkPictureRecord::addPicture(SkPicture& picture) {
1273    int index = fPictureRefs.find(&picture);
1274    if (index < 0) {    // not found
1275        index = fPictureRefs.count();
1276        *fPictureRefs.append() = &picture;
1277        picture.ref();
1278    }
1279    // follow the convention of recording a 1-based index
1280    addInt(index + 1);
1281}
1282
1283void SkPictureRecord::addPoint(const SkPoint& point) {
1284#ifdef SK_DEBUG_SIZE
1285    size_t start = fWriter.size();
1286#endif
1287    fWriter.writePoint(point);
1288#ifdef SK_DEBUG_SIZE
1289    fPointBytes += fWriter.size() - start;
1290    fPointWrites++;
1291#endif
1292}
1293
1294void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1295    fWriter.writeMul4(pts, count * sizeof(SkPoint));
1296#ifdef SK_DEBUG_SIZE
1297    fPointBytes += count * sizeof(SkPoint);
1298    fPointWrites++;
1299#endif
1300}
1301
1302void SkPictureRecord::addRect(const SkRect& rect) {
1303#ifdef SK_DEBUG_SIZE
1304    size_t start = fWriter.size();
1305#endif
1306    fWriter.writeRect(rect);
1307#ifdef SK_DEBUG_SIZE
1308    fRectBytes += fWriter.size() - start;
1309    fRectWrites++;
1310#endif
1311}
1312
1313void SkPictureRecord::addRectPtr(const SkRect* rect) {
1314    if (fWriter.writeBool(rect != NULL)) {
1315        fWriter.writeRect(*rect);
1316    }
1317}
1318
1319void SkPictureRecord::addIRect(const SkIRect& rect) {
1320    fWriter.write(&rect, sizeof(rect));
1321}
1322
1323void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1324    if (fWriter.writeBool(rect != NULL)) {
1325        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1326    }
1327}
1328
1329void SkPictureRecord::addRRect(const SkRRect& rrect) {
1330    fWriter.writeRRect(rrect);
1331}
1332
1333void SkPictureRecord::addRegion(const SkRegion& region) {
1334    addInt(fRegions.find(region));
1335}
1336
1337void SkPictureRecord::addText(const void* text, size_t byteLength) {
1338#ifdef SK_DEBUG_SIZE
1339    size_t start = fWriter.size();
1340#endif
1341    addInt(byteLength);
1342    fWriter.writePad(text, byteLength);
1343#ifdef SK_DEBUG_SIZE
1344    fTextBytes += fWriter.size() - start;
1345    fTextWrites++;
1346#endif
1347}
1348
1349///////////////////////////////////////////////////////////////////////////////
1350
1351#ifdef SK_DEBUG_SIZE
1352size_t SkPictureRecord::size() const {
1353    size_t result = 0;
1354    size_t sizeData;
1355    bitmaps(&sizeData);
1356    result += sizeData;
1357    matrices(&sizeData);
1358    result += sizeData;
1359    paints(&sizeData);
1360    result += sizeData;
1361    paths(&sizeData);
1362    result += sizeData;
1363    pictures(&sizeData);
1364    result += sizeData;
1365    regions(&sizeData);
1366    result += sizeData;
1367    result += streamlen();
1368    return result;
1369}
1370
1371int SkPictureRecord::bitmaps(size_t* size) const {
1372    size_t result = 0;
1373    int count = fBitmaps.count();
1374    for (int index = 0; index < count; index++)
1375        result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1376    *size = result;
1377    return count;
1378}
1379
1380int SkPictureRecord::matrices(size_t* size) const {
1381    int count = fMatrices.count();
1382    *size = sizeof(fMatrices[0]) * count;
1383    return count;
1384}
1385
1386int SkPictureRecord::paints(size_t* size) const {
1387    size_t result = 0;
1388    int count = fPaints.count();
1389    for (int index = 0; index < count; index++)
1390        result += sizeof(fPaints[index]) + fPaints[index]->size();
1391    *size = result;
1392    return count;
1393}
1394
1395int SkPictureRecord::paths(size_t* size) const {
1396    size_t result = 0;
1397    int count = fPaths.count();
1398    for (int index = 0; index < count; index++)
1399        result += sizeof(fPaths[index]) + fPaths[index]->size();
1400    *size = result;
1401    return count;
1402}
1403
1404int SkPictureRecord::regions(size_t* size) const {
1405    size_t result = 0;
1406    int count = fRegions.count();
1407    for (int index = 0; index < count; index++)
1408        result += sizeof(fRegions[index]) + fRegions[index]->size();
1409    *size = result;
1410    return count;
1411}
1412
1413size_t SkPictureRecord::streamlen() const {
1414    return fWriter.size();
1415}
1416#endif
1417
1418#ifdef SK_DEBUG_VALIDATE
1419void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1420    SkASSERT(fWriter.size() == initialOffset + size);
1421
1422    validateBitmaps();
1423    validateMatrices();
1424    validatePaints();
1425    validatePaths();
1426    validateRegions();
1427}
1428
1429void SkPictureRecord::validateBitmaps() const {
1430    int count = fBitmapHeap->count();
1431    SkASSERT((unsigned) count < 0x1000);
1432    for (int index = 0; index < count; index++) {
1433        const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
1434        SkASSERT(bitPtr);
1435        bitPtr->validate();
1436    }
1437}
1438
1439void SkPictureRecord::validateMatrices() const {
1440    int count = fMatrices.count();
1441    SkASSERT((unsigned) count < 0x1000);
1442    for (int index = 0; index < count; index++) {
1443        const SkFlatData* matrix = fMatrices[index];
1444        SkASSERT(matrix);
1445//        matrix->validate();
1446    }
1447}
1448
1449void SkPictureRecord::validatePaints() const {
1450    int count = fPaints.count();
1451    SkASSERT((unsigned) count < 0x1000);
1452    for (int index = 0; index < count; index++) {
1453        const SkFlatData* paint = fPaints[index];
1454        SkASSERT(paint);
1455//            paint->validate();
1456    }
1457}
1458
1459void SkPictureRecord::validatePaths() const {
1460    if (NULL == fPathHeap) {
1461        return;
1462    }
1463
1464    int count = fPathHeap->count();
1465    SkASSERT((unsigned) count < 0x1000);
1466    for (int index = 0; index < count; index++) {
1467        const SkPath& path = (*fPathHeap)[index];
1468        path.validate();
1469    }
1470}
1471
1472void SkPictureRecord::validateRegions() const {
1473    int count = fRegions.count();
1474    SkASSERT((unsigned) count < 0x1000);
1475    for (int index = 0; index < count; index++) {
1476        const SkFlatData* region = fRegions[index];
1477        SkASSERT(region);
1478//        region->validate();
1479    }
1480}
1481#endif
1482