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