SkPictureRecord.cpp revision 9b14f26d0f3a974f3dd626c8354e1db1cfcd322f
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
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 HEAP_BLOCK_SIZE 4096
17
18// If SK_RECORD_LITERAL_PICTURES is defined, record our inputs as literally as possible.
19// Otherwise, we can be clever and record faster equivalents.  kBeClever is normally true.
20static const bool kBeClever =
21#ifdef SK_RECORD_LITERAL_PICTURES
22    false;
23#else
24    true;
25#endif
26
27enum {
28    // just need a value that save or getSaveCount would never return
29    kNoInitialSave = -1,
30};
31
32// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
33static int const kUInt32Size = 4;
34
35static const uint32_t kSaveSize = 2 * kUInt32Size;
36static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
37static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
38
39SkPictureRecord::SkPictureRecord(SkPicture* picture, const SkISize& dimensions, uint32_t flags)
40    : INHERITED(dimensions.width(), dimensions.height())
41    , fBoundingHierarchy(NULL)
42    , fStateTree(NULL)
43    , fFlattenableHeap(HEAP_BLOCK_SIZE)
44    , fPaints(&fFlattenableHeap)
45    , fRecordFlags(flags)
46    , fOptsEnabled(kBeClever) {
47#ifdef SK_DEBUG_SIZE
48    fPointBytes = fRectBytes = fTextBytes = 0;
49    fPointWrites = fRectWrites = fTextWrites = 0;
50#endif
51
52    fPicture = picture;
53    fBitmapHeap = SkNEW(SkBitmapHeap);
54    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
55
56#ifndef SK_COLLAPSE_MATRIX_CLIP_STATE
57    fFirstSavedLayerIndex = kNoSavedLayerIndex;
58#endif
59
60    fInitialSaveCount = kNoInitialSave;
61
62#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
63    fMCMgr.init(this);
64#endif
65}
66
67SkPictureRecord::~SkPictureRecord() {
68    SkSafeUnref(fBitmapHeap);
69    SkSafeUnref(fBoundingHierarchy);
70    SkSafeUnref(fStateTree);
71    fFlattenableHeap.setBitmapStorage(NULL);
72    fPictureRefs.unrefAll();
73}
74
75///////////////////////////////////////////////////////////////////////////////
76
77// Return the offset of the paint inside a given op's byte stream. A zero
78// return value means there is no paint (and you really shouldn't be calling
79// this method)
80static inline size_t getPaintOffset(DrawType op, size_t opSize) {
81    // These offsets are where the paint would be if the op size doesn't overflow
82    static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
83        0,  // UNUSED - no paint
84        0,  // CLIP_PATH - no paint
85        0,  // CLIP_REGION - no paint
86        0,  // CLIP_RECT - no paint
87        0,  // CLIP_RRECT - no paint
88        0,  // CONCAT - no paint
89        1,  // DRAW_BITMAP - right after op code
90        1,  // DRAW_BITMAP_MATRIX - right after op code
91        1,  // DRAW_BITMAP_NINE - right after op code
92        1,  // DRAW_BITMAP_RECT_TO_RECT - right after op code
93        0,  // DRAW_CLEAR - no paint
94        0,  // DRAW_DATA - no paint
95        1,  // DRAW_OVAL - right after op code
96        1,  // DRAW_PAINT - right after op code
97        1,  // DRAW_PATH - right after op code
98        0,  // DRAW_PICTURE - no paint
99        1,  // DRAW_POINTS - right after op code
100        1,  // DRAW_POS_TEXT - right after op code
101        1,  // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
102        1,  // DRAW_POS_TEXT_H - right after op code
103        1,  // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
104        1,  // DRAW_RECT - right after op code
105        1,  // DRAW_RRECT - right after op code
106        1,  // DRAW_SPRITE - right after op code
107        1,  // DRAW_TEXT - right after op code
108        1,  // DRAW_TEXT_ON_PATH - right after op code
109        1,  // DRAW_TEXT_TOP_BOTTOM - right after op code
110        1,  // DRAW_VERTICES - right after op code
111        0,  // RESTORE - no paint
112        0,  // ROTATE - no paint
113        0,  // SAVE - no paint
114        0,  // SAVE_LAYER - see below - this paint's location varies
115        0,  // SCALE - no paint
116        0,  // SET_MATRIX - no paint
117        0,  // SKEW - no paint
118        0,  // TRANSLATE - no paint
119        0,  // NOOP - no paint
120        0,  // BEGIN_GROUP - no paint
121        0,  // COMMENT - no paint
122        0,  // END_GROUP - no paint
123        1,  // DRAWDRRECT - right after op code
124        0,  // PUSH_CULL - no paint
125        0,  // POP_CULL - no paint
126    };
127
128    SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1,
129                      need_to_be_in_sync);
130    SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
131
132    int overflow = 0;
133    if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
134        // This op's size overflows so an extra uint32_t will be written
135        // after the op code
136        overflow = sizeof(uint32_t);
137    }
138
139    if (SAVE_LAYER == op) {
140        static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
141        static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
142
143        if (kSaveLayerNoBoundsSize == opSize) {
144            return kSaveLayerNoBoundsPaintOffset + overflow;
145        } else {
146            SkASSERT(kSaveLayerWithBoundsSize == opSize);
147            return kSaveLayerWithBoundsPaintOffset + overflow;
148        }
149    }
150
151    SkASSERT(0 != gPaintOffsets[op]);   // really shouldn't be calling this method
152    return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
153}
154
155void SkPictureRecord::willSave(SaveFlags flags) {
156
157#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
158    fMCMgr.save(flags);
159#else
160    // record the offset to us, making it non-positive to distinguish a save
161    // from a clip entry.
162    fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten());
163    this->recordSave(flags);
164#endif
165
166    this->INHERITED::willSave(flags);
167}
168
169void SkPictureRecord::recordSave(SaveFlags flags) {
170    // op + flags
171    size_t size = kSaveSize;
172    size_t initialOffset = this->addDraw(SAVE, &size);
173    this->addInt(flags);
174
175    this->validate(initialOffset, size);
176}
177
178SkCanvas::SaveLayerStrategy SkPictureRecord::willSaveLayer(const SkRect* bounds,
179                                                           const SkPaint* paint, SaveFlags flags) {
180
181#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
182    fMCMgr.saveLayer(bounds, paint, flags);
183#else
184    // record the offset to us, making it non-positive to distinguish a save
185    // from a clip entry.
186    fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten());
187    this->recordSaveLayer(bounds, paint, flags);
188    if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
189        fFirstSavedLayerIndex = fRestoreOffsetStack.count();
190    }
191#endif
192
193    this->INHERITED::willSaveLayer(bounds, paint, flags);
194    /*  No need for a (potentially very big) layer which we don't actually need
195        at this time (and may not be able to afford since during record our
196        clip starts out the size of the picture, which is often much larger
197        than the size of the actual device we'll use during playback).
198     */
199    return kNoLayer_SaveLayerStrategy;
200}
201
202void SkPictureRecord::recordSaveLayer(const SkRect* bounds, const SkPaint* paint,
203                                      SaveFlags flags) {
204    // op + bool for 'bounds'
205    size_t size = 2 * kUInt32Size;
206    if (NULL != bounds) {
207        size += sizeof(*bounds); // + rect
208    }
209    // + paint index + flags
210    size += 2 * kUInt32Size;
211
212    SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
213
214    size_t initialOffset = this->addDraw(SAVE_LAYER, &size);
215    this->addRectPtr(bounds);
216    SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.bytesWritten());
217    this->addPaintPtr(paint);
218    this->addInt(flags);
219
220    this->validate(initialOffset, size);
221}
222
223bool SkPictureRecord::isDrawingToLayer() const {
224#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
225    return fMCMgr.isDrawingToLayer();
226#else
227    return fFirstSavedLayerIndex != kNoSavedLayerIndex;
228#endif
229}
230
231/*
232 * Read the op code from 'offset' in 'writer'.
233 */
234#ifdef SK_DEBUG
235static DrawType peek_op(SkWriter32* writer, size_t offset) {
236    return (DrawType)(writer->readTAt<uint32_t>(offset) >> 24);
237}
238#endif
239
240/*
241 * Read the op code from 'offset' in 'writer' and extract the size too.
242 */
243static DrawType peek_op_and_size(SkWriter32* writer, size_t offset, uint32_t* size) {
244    uint32_t peek = writer->readTAt<uint32_t>(offset);
245
246    uint32_t op;
247    UNPACK_8_24(peek, op, *size);
248    if (MASK_24 == *size) {
249        // size required its own slot right after the op code
250        *size = writer->readTAt<uint32_t>(offset + kUInt32Size);
251    }
252    return (DrawType) op;
253}
254
255#ifdef TRACK_COLLAPSE_STATS
256    static int gCollapseCount, gCollapseCalls;
257#endif
258
259// Is the supplied paint simply a color?
260static bool is_simple(const SkPaint& p) {
261    intptr_t orAccum = (intptr_t)p.getPathEffect()  |
262                       (intptr_t)p.getShader()      |
263                       (intptr_t)p.getXfermode()    |
264                       (intptr_t)p.getMaskFilter()  |
265                       (intptr_t)p.getColorFilter() |
266                       (intptr_t)p.getRasterizer()  |
267                       (intptr_t)p.getLooper()      |
268                       (intptr_t)p.getImageFilter();
269    return 0 == orAccum;
270}
271
272// CommandInfos are fed to the 'match' method and filled in with command
273// information.
274struct CommandInfo {
275    DrawType fActualOp;
276    uint32_t fOffset;
277    uint32_t fSize;
278};
279
280/*
281 * Attempt to match the provided pattern of commands starting at 'offset'
282 * in the byte stream and stopping at the end of the stream. Upon success,
283 * return true with all the pattern information filled out in the result
284 * array (i.e., actual ops, offsets and sizes).
285 * Note this method skips any NOOPs seen in the stream
286 */
287static bool match(SkWriter32* writer, uint32_t offset,
288                  int* pattern, CommandInfo* result, int numCommands) {
289    SkASSERT(offset < writer->bytesWritten());
290
291    uint32_t curOffset = offset;
292    uint32_t curSize = 0;
293    int numMatched;
294    for (numMatched = 0; numMatched < numCommands && curOffset < writer->bytesWritten(); ++numMatched) {
295        DrawType op = peek_op_and_size(writer, curOffset, &curSize);
296        while (NOOP == op) {
297            curOffset += curSize;
298            if (curOffset >= writer->bytesWritten()) {
299                return false;
300            }
301            op = peek_op_and_size(writer, curOffset, &curSize);
302        }
303
304        if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
305            if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
306                DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
307                return false;
308            }
309        } else if (op != pattern[numMatched]) {
310            return false;
311        }
312
313        result[numMatched].fActualOp = op;
314        result[numMatched].fOffset = curOffset;
315        result[numMatched].fSize = curSize;
316
317        curOffset += curSize;
318    }
319
320    if (numMatched != numCommands) {
321        return false;
322    }
323
324    curOffset += curSize;
325    if (curOffset < writer->bytesWritten()) {
326        // Something else between the last command and the end of the stream
327        return false;
328    }
329
330    return true;
331}
332
333// temporarily here to make code review easier
334static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
335                                                 SkPaintDictionary* paintDict,
336                                                 const CommandInfo& saveLayerInfo,
337                                                 const CommandInfo& dbmInfo);
338
339/*
340 * Restore has just been called (but not recorded), look back at the
341 * matching save* and see if we are in the configuration:
342 *   SAVE_LAYER
343 *       DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
344 *   RESTORE
345 * where the saveLayer's color can be moved into the drawBitmap*'s paint
346 */
347static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
348                               SkPaintDictionary* paintDict) {
349    // back up to the save block
350    // TODO: add a stack to track save*/restore offsets rather than searching backwards
351    while (offset > 0) {
352        offset = writer->readTAt<uint32_t>(offset);
353    }
354
355    int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
356    CommandInfo result[SK_ARRAY_COUNT(pattern)];
357
358    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
359        return false;
360    }
361
362    if (kSaveLayerWithBoundsSize == result[0].fSize) {
363        // The saveLayer's bound can offset where the dbm is drawn
364        return false;
365    }
366
367    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
368                                                result[0], result[1]);
369}
370
371/*
372 * Convert the command code located at 'offset' to a NOOP. Leave the size
373 * field alone so the NOOP can be skipped later.
374 */
375static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
376    uint32_t command = writer->readTAt<uint32_t>(offset);
377    writer->overwriteTAt(offset, (command & MASK_24) | (NOOP << 24));
378}
379
380/*
381 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
382 * Return true on success; false otherwise.
383 */
384static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
385                                                 SkPaintDictionary* paintDict,
386                                                 const CommandInfo& saveLayerInfo,
387                                                 const CommandInfo& dbmInfo) {
388    SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
389    SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
390             DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
391             DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
392             DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
393
394    size_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
395    size_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
396
397    // we have a match, now we need to get the paints involved
398    uint32_t dbmPaintId = writer->readTAt<uint32_t>(dbmInfo.fOffset + dbmPaintOffset);
399    uint32_t saveLayerPaintId = writer->readTAt<uint32_t>(saveLayerInfo.fOffset + slPaintOffset);
400
401    if (0 == saveLayerPaintId) {
402        // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
403        // and signal the caller (by returning true) to not add the RESTORE op
404        convert_command_to_noop(writer, saveLayerInfo.fOffset);
405        return true;
406    }
407
408    if (0 == dbmPaintId) {
409        // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
410        // and signal the caller (by returning true) to not add the RESTORE op
411        convert_command_to_noop(writer, saveLayerInfo.fOffset);
412        writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, saveLayerPaintId);
413        return true;
414    }
415
416    SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
417    if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
418        return false;
419    }
420
421    // For this optimization we only fold the saveLayer and drawBitmapRect
422    // together if the saveLayer's draw is simple (i.e., no fancy effects) and
423    // and the only difference in the colors is that the saveLayer's can have
424    // an alpha while the drawBitmapRect's is opaque.
425    // TODO: it should be possible to fold them together even if they both
426    // have different non-255 alphas
427    SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
428
429    SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
430    if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor || !is_simple(*dbmPaint)) {
431        return false;
432    }
433
434    SkColor newColor = SkColorSetA(dbmPaint->getColor(),
435                                   SkColorGetA(saveLayerPaint->getColor()));
436    dbmPaint->setColor(newColor);
437
438    const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
439    if (NULL == data) {
440        return false;
441    }
442
443    // kill the saveLayer and alter the DBMR2R's paint to be the modified one
444    convert_command_to_noop(writer, saveLayerInfo.fOffset);
445    writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, data->index());
446    return true;
447}
448
449/*
450 * Restore has just been called (but not recorded), look back at the
451 * matching save* and see if we are in the configuration:
452 *   SAVE_LAYER (with NULL == bounds)
453 *      SAVE
454 *         CLIP_RECT
455 *         DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
456 *      RESTORE
457 *   RESTORE
458 * where the saveLayer's color can be moved into the drawBitmap*'s paint
459 */
460static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
461                               SkPaintDictionary* paintDict) {
462
463    // back up to the save block
464    // TODO: add a stack to track save*/restore offsets rather than searching backwards
465    while (offset > 0) {
466        offset = writer->readTAt<uint32_t>(offset);
467    }
468
469    int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
470    CommandInfo result[SK_ARRAY_COUNT(pattern)];
471
472    if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
473        return false;
474    }
475
476    if (kSaveLayerWithBoundsSize == result[0].fSize) {
477        // The saveLayer's bound can offset where the dbm is drawn
478        return false;
479    }
480
481    return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
482                                                result[0], result[3]);
483}
484
485static bool is_drawing_op(DrawType op) {
486    return (op > CONCAT && op < ROTATE) || DRAW_DRRECT == op;
487}
488
489/*
490 *  Restore has just been called (but not recorded), so look back at the
491 *  matching save(), and see if we can eliminate the pair of them, due to no
492 *  intervening matrix/clip calls.
493 *
494 *  If so, update the writer and return true, in which case we won't even record
495 *  the restore() call. If we still need the restore(), return false.
496 */
497static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
498                                       SkPaintDictionary* paintDict) {
499#ifdef TRACK_COLLAPSE_STATS
500    gCollapseCalls += 1;
501#endif
502
503    int32_t restoreOffset = (int32_t)writer->bytesWritten();
504
505    // back up to the save block
506    while (offset > 0) {
507        offset = writer->readTAt<uint32_t>(offset);
508    }
509
510    // now offset points to a save
511    offset = -offset;
512    uint32_t opSize;
513    DrawType op = peek_op_and_size(writer, offset, &opSize);
514    if (SAVE_LAYER == op) {
515        // not ready to cull these out yet (mrr)
516        return false;
517    }
518    SkASSERT(SAVE == op);
519    SkASSERT(kSaveSize == opSize);
520
521    // get the save flag (last 4-bytes of the space allocated for the opSize)
522    SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags) writer->readTAt<uint32_t>(offset + 4);
523    if (SkCanvas::kMatrixClip_SaveFlag != saveFlags) {
524        // This function's optimization is only correct for kMatrixClip style saves.
525        // TODO: set checkMatrix & checkClip booleans here and then check for the
526        // offending operations in the following loop.
527        return false;
528    }
529
530    // Walk forward until we get back to either a draw-verb (abort) or we hit
531    // our restore (success).
532    int32_t saveOffset = offset;
533
534    offset += opSize;
535    while (offset < restoreOffset) {
536        op = peek_op_and_size(writer, offset, &opSize);
537        if (is_drawing_op(op) || (SAVE_LAYER == op)) {
538            // drawing verb, abort
539            return false;
540        }
541        offset += opSize;
542    }
543
544#ifdef TRACK_COLLAPSE_STATS
545    gCollapseCount += 1;
546    SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
547             (double)gCollapseCount / gCollapseCalls, "%");
548#endif
549
550    writer->rewindToOffset(saveOffset);
551    return true;
552}
553
554typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
555                                     SkPaintDictionary* paintDict);
556enum PictureRecordOptType {
557    kRewind_OptType,  // Optimization rewinds the command stream
558    kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
559};
560
561enum PictureRecordOptFlags {
562    kSkipIfBBoxHierarchy_Flag = 0x1,  // Optimization should be skipped if the
563                                      // SkPicture has a bounding box hierarchy.
564};
565
566struct PictureRecordOpt {
567    PictureRecordOptProc fProc;
568    PictureRecordOptType fType;
569    unsigned fFlags;
570};
571/*
572 * A list of the optimizations that are tried upon seeing a restore
573 * TODO: add a real API for such optimizations
574 *       Add the ability to fire optimizations on any op (not just RESTORE)
575 */
576static const PictureRecordOpt gPictureRecordOpts[] = {
577    // 'collapse_save_clip_restore' is skipped if there is a BBoxHierarchy
578    // because it is redundant with the state traversal optimization in
579    // SkPictureStateTree, and applying the optimization introduces significant
580    // record time overhead because it requires rewinding contents that were
581    // recorded into the BBoxHierarchy.
582    { collapse_save_clip_restore, kRewind_OptType, kSkipIfBBoxHierarchy_Flag },
583    { remove_save_layer1,         kCollapseSaveLayer_OptType, 0 },
584    { remove_save_layer2,         kCollapseSaveLayer_OptType, 0 }
585};
586
587// This is called after an optimization has been applied to the command stream
588// in order to adjust the contents and state of the bounding box hierarchy and
589// state tree to reflect the optimization.
590static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
591                                      SkBBoxHierarchy* boundingHierarchy) {
592    switch (opt) {
593    case kCollapseSaveLayer_OptType:
594        if (NULL != stateTree) {
595            stateTree->saveCollapsed();
596        }
597        break;
598    case kRewind_OptType:
599        if (NULL != boundingHierarchy) {
600            boundingHierarchy->rewindInserts();
601        }
602        // Note: No need to touch the state tree for this to work correctly.
603        // Unused branches do not burden the playback, and pruning the tree
604        // would be O(N^2), so it is best to leave it alone.
605        break;
606    default:
607        SkASSERT(0);
608    }
609}
610
611void SkPictureRecord::willRestore() {
612    // FIXME: SkDeferredCanvas needs to be refactored to respect
613    // save/restore balancing so that the following test can be
614    // turned on permanently.
615#if 0
616    SkASSERT(fRestoreOffsetStack.count() > 1);
617#endif
618
619#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
620    if (fMCMgr.getSaveCount() == 1) {
621        return;
622    }
623
624    fMCMgr.restore();
625#else
626    // check for underflow
627    if (fRestoreOffsetStack.count() == 0) {
628        return;
629    }
630
631    if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
632        fFirstSavedLayerIndex = kNoSavedLayerIndex;
633    }
634
635    size_t opt = 0;
636    if (fOptsEnabled) {
637        for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
638            if (0 != (gPictureRecordOpts[opt].fFlags & kSkipIfBBoxHierarchy_Flag)
639                && NULL != fBoundingHierarchy) {
640                continue;
641            }
642            if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
643                // Some optimization fired so don't add the RESTORE
644                apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
645                                          fStateTree, fBoundingHierarchy);
646                break;
647            }
648        }
649    }
650
651    if (!fOptsEnabled || SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
652        // No optimization fired so add the RESTORE
653        this->recordRestore();
654    }
655
656    fRestoreOffsetStack.pop();
657#endif
658
659    this->INHERITED::willRestore();
660}
661
662void SkPictureRecord::recordRestore(bool fillInSkips) {
663    if (fillInSkips) {
664        this->fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.bytesWritten());
665    }
666    size_t size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
667    size_t initialOffset = this->addDraw(RESTORE, &size);
668    this->validate(initialOffset, size);
669}
670
671void SkPictureRecord::recordTranslate(const SkMatrix& m) {
672    SkASSERT(SkMatrix::kTranslate_Mask == m.getType());
673
674    // op + dx + dy
675    size_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
676    size_t initialOffset = this->addDraw(TRANSLATE, &size);
677    this->addScalar(m.getTranslateX());
678    this->addScalar(m.getTranslateY());
679    this->validate(initialOffset, size);
680}
681
682void SkPictureRecord::recordScale(const SkMatrix& m) {
683    SkASSERT(SkMatrix::kScale_Mask == m.getType());
684
685    // op + sx + sy
686    size_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
687    size_t initialOffset = this->addDraw(SCALE, &size);
688    this->addScalar(m.getScaleX());
689    this->addScalar(m.getScaleY());
690    this->validate(initialOffset, size);
691}
692
693void SkPictureRecord::didConcat(const SkMatrix& matrix) {
694
695#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
696    fMCMgr.concat(matrix);
697#else
698    switch (matrix.getType()) {
699        case SkMatrix::kTranslate_Mask:
700            this->recordTranslate(matrix);
701            break;
702        case SkMatrix::kScale_Mask:
703            this->recordScale(matrix);
704            break;
705        default:
706            this->recordConcat(matrix);
707            break;
708    }
709#endif
710    this->INHERITED::didConcat(matrix);
711}
712
713void SkPictureRecord::recordConcat(const SkMatrix& matrix) {
714    this->validate(fWriter.bytesWritten(), 0);
715    // op + matrix
716    size_t size = kUInt32Size + matrix.writeToMemory(NULL);
717    size_t initialOffset = this->addDraw(CONCAT, &size);
718    this->addMatrix(matrix);
719    this->validate(initialOffset, size);
720}
721
722void SkPictureRecord::didSetMatrix(const SkMatrix& matrix) {
723
724#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
725    fMCMgr.setMatrix(matrix);
726#else
727    this->validate(fWriter.bytesWritten(), 0);
728    // op + matrix
729    size_t size = kUInt32Size + matrix.writeToMemory(NULL);
730    size_t initialOffset = this->addDraw(SET_MATRIX, &size);
731    this->addMatrix(matrix);
732    this->validate(initialOffset, size);
733#endif
734    this->INHERITED::didSetMatrix(matrix);
735}
736
737static bool regionOpExpands(SkRegion::Op op) {
738    switch (op) {
739        case SkRegion::kUnion_Op:
740        case SkRegion::kXOR_Op:
741        case SkRegion::kReverseDifference_Op:
742        case SkRegion::kReplace_Op:
743            return true;
744        case SkRegion::kIntersect_Op:
745        case SkRegion::kDifference_Op:
746            return false;
747        default:
748            SkDEBUGFAIL("unknown region op");
749            return false;
750    }
751}
752
753#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
754void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
755    fMCMgr.fillInSkips(&fWriter, restoreOffset);
756}
757#else
758void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
759    int32_t offset = fRestoreOffsetStack.top();
760    while (offset > 0) {
761        uint32_t peek = fWriter.readTAt<uint32_t>(offset);
762        fWriter.overwriteTAt(offset, restoreOffset);
763        offset = peek;
764    }
765
766#ifdef SK_DEBUG
767    // assert that the final offset value points to a save verb
768    uint32_t opSize;
769    DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
770    SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
771#endif
772}
773#endif
774
775void SkPictureRecord::beginRecording() {
776    // we have to call this *after* our constructor, to ensure that it gets
777    // recorded. This is balanced by restoreToCount() call from endRecording,
778    // which in-turn calls our overridden restore(), so those get recorded too.
779    fInitialSaveCount = this->save();
780}
781
782void SkPictureRecord::endRecording() {
783    SkASSERT(kNoInitialSave != fInitialSaveCount);
784    this->restoreToCount(fInitialSaveCount);
785#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
786    fMCMgr.finish();
787#endif
788}
789
790#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
791int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
792    size_t offset = fWriter.bytesWritten();
793    this->addInt(-1);
794    return offset;
795}
796#else
797size_t SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
798    if (fRestoreOffsetStack.isEmpty()) {
799        return -1;
800    }
801
802    // The RestoreOffset field is initially filled with a placeholder
803    // value that points to the offset of the previous RestoreOffset
804    // in the current stack level, thus forming a linked list so that
805    // the restore offsets can be filled in when the corresponding
806    // restore command is recorded.
807    int32_t prevOffset = fRestoreOffsetStack.top();
808
809    if (regionOpExpands(op)) {
810        // Run back through any previous clip ops, and mark their offset to
811        // be 0, disabling their ability to trigger a jump-to-restore, otherwise
812        // they could hide this clips ability to expand the clip (i.e. go from
813        // empty to non-empty).
814        this->fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
815
816        // Reset the pointer back to the previous clip so that subsequent
817        // restores don't overwrite the offsets we just cleared.
818        prevOffset = 0;
819    }
820
821    size_t offset = fWriter.bytesWritten();
822    this->addInt(prevOffset);
823    fRestoreOffsetStack.top() = SkToU32(offset);
824    return offset;
825}
826#endif
827
828void SkPictureRecord::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
829
830#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
831    fMCMgr.clipRect(rect, op, doAA);
832#else
833    this->recordClipRect(rect, op, kSoft_ClipEdgeStyle == edgeStyle);
834#endif
835    this->INHERITED::onClipRect(rect, op, edgeStyle);
836}
837
838size_t SkPictureRecord::recordClipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
839    // id + rect + clip params
840    size_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
841#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
842    size += kUInt32Size;    // + restore offset
843#else
844    // recordRestoreOffsetPlaceholder doesn't always write an offset
845    if (!fRestoreOffsetStack.isEmpty()) {
846        // + restore offset
847        size += kUInt32Size;
848    }
849#endif
850    size_t initialOffset = this->addDraw(CLIP_RECT, &size);
851    this->addRect(rect);
852    this->addInt(ClipParams_pack(op, doAA));
853    size_t offset = this->recordRestoreOffsetPlaceholder(op);
854
855    this->validate(initialOffset, size);
856    return offset;
857}
858
859void SkPictureRecord::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
860
861#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
862    fMCMgr.clipRRect(rrect, op, doAA);
863#else
864    this->recordClipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle);
865#endif
866    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
867        this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
868    } else {
869        this->INHERITED::onClipRRect(rrect, op, edgeStyle);
870    }
871}
872
873size_t SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
874    // op + rrect + clip params
875    size_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
876#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
877    size += kUInt32Size;    // + restore offset
878#else
879    // recordRestoreOffsetPlaceholder doesn't always write an offset
880    if (!fRestoreOffsetStack.isEmpty()) {
881        // + restore offset
882        size += kUInt32Size;
883    }
884#endif
885    size_t initialOffset = this->addDraw(CLIP_RRECT, &size);
886    this->addRRect(rrect);
887    this->addInt(ClipParams_pack(op, doAA));
888    size_t offset = recordRestoreOffsetPlaceholder(op);
889    this->validate(initialOffset, size);
890    return offset;
891}
892
893void SkPictureRecord::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
894
895#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
896    fMCMgr.clipPath(path, op, doAA);
897#else
898    int pathID = this->addPathToHeap(path);
899    this->recordClipPath(pathID, op, kSoft_ClipEdgeStyle == edgeStyle);
900#endif
901
902    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
903        this->updateClipConservativelyUsingBounds(path.getBounds(), op,
904                                                  path.isInverseFillType());
905    } else {
906        this->INHERITED::onClipPath(path, op, edgeStyle);
907    }
908}
909
910size_t SkPictureRecord::recordClipPath(int pathID, SkRegion::Op op, bool doAA) {
911    // op + path index + clip params
912    size_t size = 3 * kUInt32Size;
913#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
914    size += kUInt32Size;    // + restore offset
915#else
916    // recordRestoreOffsetPlaceholder doesn't always write an offset
917    if (!fRestoreOffsetStack.isEmpty()) {
918        // + restore offset
919        size += kUInt32Size;
920    }
921#endif
922    size_t initialOffset = this->addDraw(CLIP_PATH, &size);
923    this->addInt(pathID);
924    this->addInt(ClipParams_pack(op, doAA));
925    size_t offset = recordRestoreOffsetPlaceholder(op);
926    this->validate(initialOffset, size);
927    return offset;
928}
929
930void SkPictureRecord::onClipRegion(const SkRegion& region, SkRegion::Op op) {
931
932#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
933    fMCMgr.clipRegion(region, op);
934#else
935    this->recordClipRegion(region, op);
936#endif
937    this->INHERITED::onClipRegion(region, op);
938}
939
940size_t SkPictureRecord::recordClipRegion(const SkRegion& region, SkRegion::Op op) {
941    // op + clip params + region
942    size_t size = 2 * kUInt32Size + region.writeToMemory(NULL);
943#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
944    size += kUInt32Size;    // + restore offset
945#else
946    // recordRestoreOffsetPlaceholder doesn't always write an offset
947    if (!fRestoreOffsetStack.isEmpty()) {
948        // + restore offset
949        size += kUInt32Size;
950    }
951#endif
952    size_t initialOffset = this->addDraw(CLIP_REGION, &size);
953    this->addRegion(region);
954    this->addInt(ClipParams_pack(op, false));
955    size_t offset = this->recordRestoreOffsetPlaceholder(op);
956
957    this->validate(initialOffset, size);
958    return offset;
959}
960
961void SkPictureRecord::clear(SkColor color) {
962
963#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
964    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
965#endif
966
967    // op + color
968    size_t size = 2 * kUInt32Size;
969    size_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
970    this->addInt(color);
971    this->validate(initialOffset, size);
972}
973
974void SkPictureRecord::drawPaint(const SkPaint& paint) {
975
976#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
977    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
978#endif
979
980    // op + paint index
981    size_t size = 2 * kUInt32Size;
982    size_t initialOffset = this->addDraw(DRAW_PAINT, &size);
983    SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.bytesWritten());
984    this->addPaint(paint);
985    this->validate(initialOffset, size);
986}
987
988void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
989                                 const SkPaint& paint) {
990
991#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
992    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
993#endif
994
995    // op + paint index + mode + count + point data
996    size_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
997    size_t initialOffset = this->addDraw(DRAW_POINTS, &size);
998    SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.bytesWritten());
999    this->addPaint(paint);
1000    this->addInt(mode);
1001    this->addInt(SkToInt(count));
1002    fWriter.writeMul4(pts, count * sizeof(SkPoint));
1003    this->validate(initialOffset, size);
1004}
1005
1006void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
1007
1008#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1009    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1010#endif
1011
1012    // op + paint index + rect
1013    size_t size = 2 * kUInt32Size + sizeof(oval);
1014    size_t initialOffset = this->addDraw(DRAW_OVAL, &size);
1015    SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.bytesWritten());
1016    this->addPaint(paint);
1017    this->addRect(oval);
1018    this->validate(initialOffset, size);
1019}
1020
1021void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
1022
1023#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1024    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1025#endif
1026
1027    // op + paint index + rect
1028    size_t size = 2 * kUInt32Size + sizeof(rect);
1029    size_t initialOffset = this->addDraw(DRAW_RECT, &size);
1030    SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.bytesWritten());
1031    this->addPaint(paint);
1032    this->addRect(rect);
1033    this->validate(initialOffset, size);
1034}
1035
1036void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1037
1038#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1039    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1040#endif
1041
1042    if (rrect.isRect() && kBeClever) {
1043        this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
1044    } else if (rrect.isOval() && kBeClever) {
1045        this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
1046    } else {
1047        // op + paint index + rrect
1048        size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
1049        size_t initialOffset = this->addDraw(DRAW_RRECT, &size);
1050        SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.bytesWritten());
1051        this->addPaint(paint);
1052        this->addRRect(rrect);
1053        this->validate(initialOffset, size);
1054    }
1055}
1056
1057void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1058                                   const SkPaint& paint) {
1059
1060#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1061    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1062#endif
1063
1064    // op + paint index + rrects
1065    size_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory * 2;
1066    size_t initialOffset = this->addDraw(DRAW_DRRECT, &size);
1067    SkASSERT(initialOffset+getPaintOffset(DRAW_DRRECT, size) == fWriter.bytesWritten());
1068    this->addPaint(paint);
1069    this->addRRect(outer);
1070    this->addRRect(inner);
1071    this->validate(initialOffset, size);
1072}
1073
1074void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
1075
1076    if (paint.isAntiAlias() && !path.isConvex()) {
1077        fPicture->incAAConcavePaths();
1078
1079        if (SkPaint::kStroke_Style == paint.getStyle() &&
1080            0 == paint.getStrokeWidth()) {
1081            fPicture->incAAHairlineConcavePaths();
1082        }
1083    }
1084
1085#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1086    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1087#endif
1088
1089    // op + paint index + path index
1090    size_t size = 3 * kUInt32Size;
1091    size_t initialOffset = this->addDraw(DRAW_PATH, &size);
1092    SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.bytesWritten());
1093    this->addPaint(paint);
1094    this->addPath(path);
1095    this->validate(initialOffset, size);
1096}
1097
1098void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
1099                                 const SkPaint* paint = NULL) {
1100    if (bitmap.drawsNothing() && kBeClever) {
1101        return;
1102    }
1103
1104#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1105    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1106#endif
1107
1108    // op + paint index + bitmap index + left + top
1109    size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
1110    size_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
1111    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.bytesWritten());
1112    this->addPaintPtr(paint);
1113    this->addBitmap(bitmap);
1114    this->addScalar(left);
1115    this->addScalar(top);
1116    this->validate(initialOffset, size);
1117}
1118
1119void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1120                                           const SkRect& dst, const SkPaint* paint,
1121                                           DrawBitmapRectFlags flags) {
1122    if (bitmap.drawsNothing() && kBeClever) {
1123        return;
1124    }
1125
1126#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1127    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1128#endif
1129    // id + paint index + bitmap index + bool for 'src' + flags
1130    size_t size = 5 * kUInt32Size;
1131    if (NULL != src) {
1132        size += sizeof(*src);   // + rect
1133    }
1134    size += sizeof(dst);        // + rect
1135
1136    size_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
1137    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size)
1138             == fWriter.bytesWritten());
1139    this->addPaintPtr(paint);
1140    this->addBitmap(bitmap);
1141    this->addRectPtr(src);  // may be null
1142    this->addRect(dst);
1143    this->addInt(flags);
1144    this->validate(initialOffset, size);
1145}
1146
1147void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1148                                       const SkPaint* paint) {
1149    if (bitmap.drawsNothing() && kBeClever) {
1150        return;
1151    }
1152
1153#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1154    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1155#endif
1156
1157    // id + paint index + bitmap index + matrix
1158    size_t size = 3 * kUInt32Size + matrix.writeToMemory(NULL);
1159    size_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
1160    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.bytesWritten());
1161    this->addPaintPtr(paint);
1162    this->addBitmap(bitmap);
1163    this->addMatrix(matrix);
1164    this->validate(initialOffset, size);
1165}
1166
1167void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1168                                     const SkRect& dst, const SkPaint* paint) {
1169    if (bitmap.drawsNothing() && kBeClever) {
1170        return;
1171    }
1172
1173#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1174    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1175#endif
1176
1177    // op + paint index + bitmap id + center + dst rect
1178    size_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
1179    size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
1180    SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.bytesWritten());
1181    this->addPaintPtr(paint);
1182    this->addBitmap(bitmap);
1183    this->addIRect(center);
1184    this->addRect(dst);
1185    this->validate(initialOffset, size);
1186}
1187
1188void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
1189                                 const SkPaint* paint = NULL) {
1190    if (bitmap.drawsNothing() && kBeClever) {
1191        return;
1192    }
1193
1194#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1195    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1196#endif
1197
1198    // op + paint index + bitmap index + left + top
1199    size_t size = 5 * kUInt32Size;
1200    size_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
1201    SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.bytesWritten());
1202    this->addPaintPtr(paint);
1203    this->addBitmap(bitmap);
1204    this->addInt(left);
1205    this->addInt(top);
1206    this->validate(initialOffset, size);
1207}
1208
1209void SkPictureRecord::ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
1210    SkPaint::FontMetrics metrics;
1211    paint.getFontMetrics(&metrics);
1212    SkRect bounds;
1213    // construct a rect so we can see any adjustments from the paint.
1214    // we use 0,1 for left,right, just so the rect isn't empty
1215    bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
1216    (void)paint.computeFastBounds(bounds, &bounds);
1217    topbot[0] = bounds.fTop;
1218    topbot[1] = bounds.fBottom;
1219}
1220
1221void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
1222                                              SkScalar minY, SkScalar maxY) {
1223    WriteTopBot(paint, flat);
1224    this->addScalar(flat.topBot()[0] + minY);
1225    this->addScalar(flat.topBot()[1] + maxY);
1226}
1227
1228void SkPictureRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
1229                                 const SkPaint& paint) {
1230
1231#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1232    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1233#endif
1234
1235    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
1236
1237    // op + paint index + length + 'length' worth of chars + x + y
1238    size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
1239    if (fast) {
1240        size += 2 * sizeof(SkScalar); // + top & bottom
1241    }
1242
1243    DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
1244    size_t initialOffset = this->addDraw(op, &size);
1245    SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten());
1246    const SkFlatData* flatPaintData = addPaint(paint);
1247    SkASSERT(flatPaintData);
1248    this->addText(text, byteLength);
1249    this->addScalar(x);
1250    this->addScalar(y);
1251    if (fast) {
1252        this->addFontMetricsTopBottom(paint, *flatPaintData, y, y);
1253    }
1254    this->validate(initialOffset, size);
1255}
1256
1257void SkPictureRecord::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
1258                                    const SkPaint& paint) {
1259
1260#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1261    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1262#endif
1263
1264    int points = paint.countText(text, byteLength);
1265    if (0 == points)
1266        return;
1267
1268    bool canUseDrawH = true;
1269    SkScalar minY = pos[0].fY;
1270    SkScalar maxY = pos[0].fY;
1271    // check if the caller really should have used drawPosTextH()
1272    {
1273        const SkScalar firstY = pos[0].fY;
1274        for (int index = 1; index < points; index++) {
1275            if (pos[index].fY != firstY) {
1276                canUseDrawH = false;
1277                if (pos[index].fY < minY) {
1278                    minY = pos[index].fY;
1279                } else if (pos[index].fY > maxY) {
1280                    maxY = pos[index].fY;
1281                }
1282            }
1283        }
1284    }
1285
1286    bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
1287    bool fast = canUseDrawH && fastBounds && kBeClever;
1288
1289    // op + paint index + length + 'length' worth of data + num points
1290    size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1291    if (canUseDrawH) {
1292        if (fast) {
1293            size += 2 * sizeof(SkScalar); // + top & bottom
1294        }
1295        // + y-pos + actual x-point data
1296        size += sizeof(SkScalar) + points * sizeof(SkScalar);
1297    } else {
1298        // + x&y point data
1299        size += points * sizeof(SkPoint);
1300        if (fastBounds) {
1301            size += 2 * sizeof(SkScalar); // + top & bottom
1302        }
1303    }
1304
1305    DrawType op;
1306    if (fast) {
1307        op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1308    } else if (canUseDrawH) {
1309        op = DRAW_POS_TEXT_H;
1310    } else if (fastBounds) {
1311        op = DRAW_POS_TEXT_TOP_BOTTOM;
1312    } else {
1313        op = DRAW_POS_TEXT;
1314    }
1315    size_t initialOffset = this->addDraw(op, &size);
1316    SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten());
1317    const SkFlatData* flatPaintData = this->addPaint(paint);
1318    SkASSERT(flatPaintData);
1319    this->addText(text, byteLength);
1320    this->addInt(points);
1321
1322#ifdef SK_DEBUG_SIZE
1323    size_t start = fWriter.bytesWritten();
1324#endif
1325    if (canUseDrawH) {
1326        if (fast) {
1327            this->addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
1328        }
1329        this->addScalar(pos[0].fY);
1330        SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
1331        for (int index = 0; index < points; index++)
1332            *xptr++ = pos[index].fX;
1333    } else {
1334        fWriter.writeMul4(pos, points * sizeof(SkPoint));
1335        if (fastBounds) {
1336            this->addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
1337        }
1338    }
1339#ifdef SK_DEBUG_SIZE
1340    fPointBytes += fWriter.bytesWritten() - start;
1341    fPointWrites += points;
1342#endif
1343    this->validate(initialOffset, size);
1344}
1345
1346void SkPictureRecord::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
1347                                     SkScalar constY, const SkPaint& paint) {
1348#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1349    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1350#endif
1351
1352    const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
1353    this->drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
1354}
1355
1356void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength,
1357                          const SkScalar xpos[], SkScalar constY,
1358                          const SkPaint& paint, const SkFlatData* flatPaintData) {
1359    int points = paint.countText(text, byteLength);
1360    if (0 == points && kBeClever) {
1361        return;
1362    }
1363
1364    bool fast = !paint.isVerticalText() && paint.canComputeFastBounds() && kBeClever;
1365
1366    // op + paint index + length + 'length' worth of data + num points
1367    size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1368    if (fast) {
1369        size += 2 * sizeof(SkScalar); // + top & bottom
1370    }
1371    // + y + the actual points
1372    size += 1 * kUInt32Size + points * sizeof(SkScalar);
1373    size_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
1374                                         &size);
1375    SkASSERT(flatPaintData);
1376    this->addFlatPaint(flatPaintData);
1377
1378    this->addText(text, byteLength);
1379    this->addInt(points);
1380
1381#ifdef SK_DEBUG_SIZE
1382    size_t start = fWriter.bytesWritten();
1383#endif
1384    if (fast) {
1385        this->addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
1386    }
1387    this->addScalar(constY);
1388    fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1389#ifdef SK_DEBUG_SIZE
1390    fPointBytes += fWriter.bytesWritten() - start;
1391    fPointWrites += points;
1392#endif
1393    this->validate(initialOffset, size);
1394}
1395
1396void SkPictureRecord::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
1397                                       const SkMatrix* matrix, const SkPaint& paint) {
1398#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1399    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1400#endif
1401
1402    // op + paint index + length + 'length' worth of data + path index + matrix
1403    const SkMatrix& m = matrix ? *matrix : SkMatrix::I();
1404    size_t size = 3 * kUInt32Size + SkAlign4(byteLength) + kUInt32Size + m.writeToMemory(NULL);
1405    size_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
1406    SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.bytesWritten());
1407    this->addPaint(paint);
1408    this->addText(text, byteLength);
1409    this->addPath(path);
1410    this->addMatrix(m);
1411    this->validate(initialOffset, size);
1412}
1413
1414void SkPictureRecord::onDrawPicture(const SkPicture* picture) {
1415
1416#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1417    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1418#endif
1419
1420    // op + picture index
1421    size_t size = 2 * kUInt32Size;
1422    size_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
1423    this->addPicture(picture);
1424    this->validate(initialOffset, size);
1425}
1426
1427void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1428                          const SkPoint vertices[], const SkPoint texs[],
1429                          const SkColor colors[], SkXfermode* xfer,
1430                          const uint16_t indices[], int indexCount,
1431                          const SkPaint& paint) {
1432
1433#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1434    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1435#endif
1436
1437    uint32_t flags = 0;
1438    if (texs) {
1439        flags |= DRAW_VERTICES_HAS_TEXS;
1440    }
1441    if (colors) {
1442        flags |= DRAW_VERTICES_HAS_COLORS;
1443    }
1444    if (indexCount > 0) {
1445        flags |= DRAW_VERTICES_HAS_INDICES;
1446    }
1447    if (NULL != xfer) {
1448        SkXfermode::Mode mode;
1449        if (xfer->asMode(&mode) && SkXfermode::kModulate_Mode != mode) {
1450            flags |= DRAW_VERTICES_HAS_XFER;
1451        }
1452    }
1453
1454    // op + paint index + flags + vmode + vCount + vertices
1455    size_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1456    if (flags & DRAW_VERTICES_HAS_TEXS) {
1457        size += vertexCount * sizeof(SkPoint);  // + uvs
1458    }
1459    if (flags & DRAW_VERTICES_HAS_COLORS) {
1460        size += vertexCount * sizeof(SkColor);  // + vert colors
1461    }
1462    if (flags & DRAW_VERTICES_HAS_INDICES) {
1463        // + num indices + indices
1464        size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1465    }
1466    if (flags & DRAW_VERTICES_HAS_XFER) {
1467        size += kUInt32Size;    // mode enum
1468    }
1469
1470    size_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
1471    SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.bytesWritten());
1472    this->addPaint(paint);
1473    this->addInt(flags);
1474    this->addInt(vmode);
1475    this->addInt(vertexCount);
1476    this->addPoints(vertices, vertexCount);
1477    if (flags & DRAW_VERTICES_HAS_TEXS) {
1478        this->addPoints(texs, vertexCount);
1479    }
1480    if (flags & DRAW_VERTICES_HAS_COLORS) {
1481        fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1482    }
1483    if (flags & DRAW_VERTICES_HAS_INDICES) {
1484        this->addInt(indexCount);
1485        fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1486    }
1487    if (flags & DRAW_VERTICES_HAS_XFER) {
1488        SkXfermode::Mode mode = SkXfermode::kModulate_Mode;
1489        (void)xfer->asMode(&mode);
1490        this->addInt(mode);
1491    }
1492    this->validate(initialOffset, size);
1493}
1494
1495void SkPictureRecord::drawData(const void* data, size_t length) {
1496
1497#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
1498    fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
1499#endif
1500
1501    // op + length + 'length' worth of data
1502    size_t size = 2 * kUInt32Size + SkAlign4(length);
1503    size_t initialOffset = this->addDraw(DRAW_DATA, &size);
1504    this->addInt(SkToInt(length));
1505    fWriter.writePad(data, length);
1506    this->validate(initialOffset, size);
1507}
1508
1509void SkPictureRecord::beginCommentGroup(const char* description) {
1510    // op/size + length of string + \0 terminated chars
1511    size_t length = strlen(description);
1512    size_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1513    size_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1514    fWriter.writeString(description, length);
1515    this->validate(initialOffset, size);
1516}
1517
1518void SkPictureRecord::addComment(const char* kywd, const char* value) {
1519    // op/size + 2x length of string + 2x \0 terminated chars
1520    size_t kywdLen = strlen(kywd);
1521    size_t valueLen = strlen(value);
1522    size_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1523    size_t initialOffset = this->addDraw(COMMENT, &size);
1524    fWriter.writeString(kywd, kywdLen);
1525    fWriter.writeString(value, valueLen);
1526    this->validate(initialOffset, size);
1527}
1528
1529void SkPictureRecord::endCommentGroup() {
1530    // op/size
1531    size_t size = 1 * kUInt32Size;
1532    size_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1533    this->validate(initialOffset, size);
1534}
1535
1536// [op/size] [rect] [skip offset]
1537static const uint32_t kPushCullOpSize = 2 * kUInt32Size + sizeof(SkRect);
1538void SkPictureRecord::onPushCull(const SkRect& cullRect) {
1539    size_t size = kPushCullOpSize;
1540    size_t initialOffset = this->addDraw(PUSH_CULL, &size);
1541    // PUSH_CULL's size should stay constant (used to rewind).
1542    SkASSERT(size == kPushCullOpSize);
1543
1544    this->addRect(cullRect);
1545    fCullOffsetStack.push(SkToU32(fWriter.bytesWritten()));
1546    this->addInt(0);
1547    this->validate(initialOffset, size);
1548}
1549
1550void SkPictureRecord::onPopCull() {
1551    SkASSERT(!fCullOffsetStack.isEmpty());
1552
1553    uint32_t cullSkipOffset = fCullOffsetStack.top();
1554    fCullOffsetStack.pop();
1555
1556    // Collapse empty push/pop pairs.
1557    if ((size_t)(cullSkipOffset + kUInt32Size) == fWriter.bytesWritten() && kBeClever) {
1558        SkASSERT(fWriter.bytesWritten() >= kPushCullOpSize);
1559        SkASSERT(PUSH_CULL == peek_op(&fWriter, fWriter.bytesWritten() - kPushCullOpSize));
1560        fWriter.rewindToOffset(fWriter.bytesWritten() - kPushCullOpSize);
1561        return;
1562    }
1563
1564    // op only
1565    size_t size = kUInt32Size;
1566    size_t initialOffset = this->addDraw(POP_CULL, &size);
1567
1568    // update the cull skip offset to point past this op.
1569    fWriter.overwriteTAt<uint32_t>(cullSkipOffset, SkToU32(fWriter.bytesWritten()));
1570
1571    this->validate(initialOffset, size);
1572}
1573
1574///////////////////////////////////////////////////////////////////////////////
1575
1576SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info) {
1577    return NULL;
1578}
1579
1580int SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
1581    const int index = fBitmapHeap->insert(bitmap);
1582    // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1583    // release builds, the invalid value will be recorded so that the reader will know that there
1584    // was a problem.
1585    SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1586    this->addInt(index);
1587    return index;
1588}
1589
1590void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1591    fWriter.writeMatrix(matrix);
1592}
1593
1594const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
1595    return fPaints.findAndReturnFlat(paint);
1596}
1597
1598const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1599    if (NULL != paint && NULL != paint->getPathEffect()) {
1600        fPicture->incPaintWithPathEffectUses();
1601    }
1602
1603    const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL;
1604    this->addFlatPaint(data);
1605    return data;
1606}
1607
1608void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
1609    int index = flatPaint ? flatPaint->index() : 0;
1610    this->addInt(index);
1611}
1612
1613int SkPictureRecord::addPathToHeap(const SkPath& path) {
1614    return fPicture->addPathToHeap(path);
1615}
1616
1617void SkPictureRecord::addPath(const SkPath& path) {
1618    this->addInt(this->addPathToHeap(path));
1619}
1620
1621void SkPictureRecord::addPicture(const SkPicture* picture) {
1622    int index = fPictureRefs.find(picture);
1623    if (index < 0) {    // not found
1624        index = fPictureRefs.count();
1625        *fPictureRefs.append() = picture;
1626        picture->ref();
1627    }
1628    // follow the convention of recording a 1-based index
1629    this->addInt(index + 1);
1630}
1631
1632void SkPictureRecord::addPoint(const SkPoint& point) {
1633#ifdef SK_DEBUG_SIZE
1634    size_t start = fWriter.bytesWritten();
1635#endif
1636    fWriter.writePoint(point);
1637#ifdef SK_DEBUG_SIZE
1638    fPointBytes += fWriter.bytesWritten() - start;
1639    fPointWrites++;
1640#endif
1641}
1642
1643void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1644    fWriter.writeMul4(pts, count * sizeof(SkPoint));
1645#ifdef SK_DEBUG_SIZE
1646    fPointBytes += count * sizeof(SkPoint);
1647    fPointWrites++;
1648#endif
1649}
1650
1651void SkPictureRecord::addNoOp() {
1652    size_t size = kUInt32Size; // op
1653    this->addDraw(NOOP, &size);
1654}
1655
1656void SkPictureRecord::addRect(const SkRect& rect) {
1657#ifdef SK_DEBUG_SIZE
1658    size_t start = fWriter.bytesWritten();
1659#endif
1660    fWriter.writeRect(rect);
1661#ifdef SK_DEBUG_SIZE
1662    fRectBytes += fWriter.bytesWritten() - start;
1663    fRectWrites++;
1664#endif
1665}
1666
1667void SkPictureRecord::addRectPtr(const SkRect* rect) {
1668    if (fWriter.writeBool(rect != NULL)) {
1669        fWriter.writeRect(*rect);
1670    }
1671}
1672
1673void SkPictureRecord::addIRect(const SkIRect& rect) {
1674    fWriter.write(&rect, sizeof(rect));
1675}
1676
1677void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1678    if (fWriter.writeBool(rect != NULL)) {
1679        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1680    }
1681}
1682
1683void SkPictureRecord::addRRect(const SkRRect& rrect) {
1684    fWriter.writeRRect(rrect);
1685}
1686
1687void SkPictureRecord::addRegion(const SkRegion& region) {
1688    fWriter.writeRegion(region);
1689}
1690
1691void SkPictureRecord::addText(const void* text, size_t byteLength) {
1692#ifdef SK_DEBUG_SIZE
1693    size_t start = fWriter.bytesWritten();
1694#endif
1695    addInt(SkToInt(byteLength));
1696    fWriter.writePad(text, byteLength);
1697#ifdef SK_DEBUG_SIZE
1698    fTextBytes += fWriter.bytesWritten() - start;
1699    fTextWrites++;
1700#endif
1701}
1702
1703///////////////////////////////////////////////////////////////////////////////
1704
1705#ifdef SK_DEBUG_SIZE
1706size_t SkPictureRecord::size() const {
1707    size_t result = 0;
1708    size_t sizeData;
1709    bitmaps(&sizeData);
1710    result += sizeData;
1711    matrices(&sizeData);
1712    result += sizeData;
1713    paints(&sizeData);
1714    result += sizeData;
1715    paths(&sizeData);
1716    result += sizeData;
1717    pictures(&sizeData);
1718    result += sizeData;
1719    regions(&sizeData);
1720    result += sizeData;
1721    result += streamlen();
1722    return result;
1723}
1724
1725int SkPictureRecord::bitmaps(size_t* size) const {
1726    size_t result = 0;
1727    int count = fBitmaps.count();
1728    for (int index = 0; index < count; index++)
1729        result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1730    *size = result;
1731    return count;
1732}
1733
1734int SkPictureRecord::matrices(size_t* size) const {
1735    int count = fMatrices.count();
1736    *size = sizeof(fMatrices[0]) * count;
1737    return count;
1738}
1739
1740int SkPictureRecord::paints(size_t* size) const {
1741    size_t result = 0;
1742    int count = fPaints.count();
1743    for (int index = 0; index < count; index++)
1744        result += sizeof(fPaints[index]) + fPaints[index]->size();
1745    *size = result;
1746    return count;
1747}
1748
1749int SkPictureRecord::paths(size_t* size) const {
1750    size_t result = 0;
1751    int count = fPaths.count();
1752    for (int index = 0; index < count; index++)
1753        result += sizeof(fPaths[index]) + fPaths[index]->size();
1754    *size = result;
1755    return count;
1756}
1757
1758int SkPictureRecord::regions(size_t* size) const {
1759    size_t result = 0;
1760    int count = fRegions.count();
1761    for (int index = 0; index < count; index++)
1762        result += sizeof(fRegions[index]) + fRegions[index]->size();
1763    *size = result;
1764    return count;
1765}
1766
1767size_t SkPictureRecord::streamlen() const {
1768    return fWriter.size();
1769}
1770#endif
1771
1772#ifdef SK_DEBUG_VALIDATE
1773void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1774    SkASSERT(fWriter.size() == initialOffset + size);
1775
1776    validateBitmaps();
1777    validateMatrices();
1778    validatePaints();
1779    validatePaths();
1780    validateRegions();
1781}
1782
1783void SkPictureRecord::validateBitmaps() const {
1784    int count = fBitmapHeap->count();
1785    SkASSERT((unsigned) count < 0x1000);
1786    for (int index = 0; index < count; index++) {
1787        const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
1788        SkASSERT(bitPtr);
1789        bitPtr->validate();
1790    }
1791}
1792
1793void SkPictureRecord::validateMatrices() const {
1794    int count = fMatrices.count();
1795    SkASSERT((unsigned) count < 0x1000);
1796    for (int index = 0; index < count; index++) {
1797        const SkFlatData* matrix = fMatrices[index];
1798        SkASSERT(matrix);
1799//        matrix->validate();
1800    }
1801}
1802
1803void SkPictureRecord::validatePaints() const {
1804    int count = fPaints.count();
1805    SkASSERT((unsigned) count < 0x1000);
1806    for (int index = 0; index < count; index++) {
1807        const SkFlatData* paint = fPaints[index];
1808        SkASSERT(paint);
1809//            paint->validate();
1810    }
1811}
1812
1813void SkPictureRecord::validatePaths() const {
1814    if (NULL == fPathHeap) {
1815        return;
1816    }
1817
1818    int count = fPathHeap->count();
1819    SkASSERT((unsigned) count < 0x1000);
1820    for (int index = 0; index < count; index++) {
1821        const SkPath& path = (*fPathHeap)[index];
1822        path.validate();
1823    }
1824}
1825
1826void SkPictureRecord::validateRegions() const {
1827    int count = fRegions.count();
1828    SkASSERT((unsigned) count < 0x1000);
1829    for (int index = 0; index < count; index++) {
1830        const SkFlatData* region = fRegions[index];
1831        SkASSERT(region);
1832//        region->validate();
1833    }
1834}
1835#endif
1836