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