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