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