Home | History | Annotate | Download | only in core
      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.
     20 static const bool kBeClever =
     21 #ifdef SK_RECORD_LITERAL_PICTURES
     22     false;
     23 #else
     24     true;
     25 #endif
     26 
     27 enum {
     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.
     33 static int const kUInt32Size = 4;
     34 
     35 static const uint32_t kSaveSize = 2 * kUInt32Size;
     36 static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
     37 static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
     38 
     39 SkPictureRecord::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 
     66 SkPictureRecord::~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)
     79 static 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 
    154 void 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 
    168 void 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 
    177 SkCanvas::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 
    201 void 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 
    222 bool 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
    234 static 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  */
    242 static 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?
    259 static 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.
    273 struct 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  */
    286 static 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
    333 static 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  */
    346 static 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  */
    374 static 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  */
    383 static 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  */
    459 static 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 
    484 static 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  */
    496 static 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 
    553 typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
    554                                      SkPaintDictionary* paintDict);
    555 enum PictureRecordOptType {
    556     kRewind_OptType,  // Optimization rewinds the command stream
    557     kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
    558 };
    559 
    560 enum PictureRecordOptFlags {
    561     kSkipIfBBoxHierarchy_Flag = 0x1,  // Optimization should be skipped if the
    562                                       // SkPicture has a bounding box hierarchy.
    563 };
    564 
    565 struct 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  */
    575 static 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.
    589 static 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 
    610 void 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 
    661 void 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 
    670 void 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 
    681 void 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 
    692 void 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 
    712 void 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 
    721 void 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 
    736 static 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
    753 void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
    754     fMCMgr.fillInSkips(&fWriter, restoreOffset);
    755 }
    756 #else
    757 void 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 
    774 void 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 
    781 void 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
    790 int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
    791     size_t offset = fWriter.bytesWritten();
    792     this->addInt(-1);
    793     return offset;
    794 }
    795 #else
    796 size_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 
    827 void 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 
    837 size_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 
    858 void 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 
    868 size_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 
    888 void 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 
    901 size_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 
    921 void 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 
    931 size_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 
    952 void 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 
    965 void 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 
    979 void 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 
   1005 void 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 
   1020 void 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 
   1035 void 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 
   1056 void 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 
   1073 void 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 
   1097 void 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 
   1118 void 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 
   1146 void 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 
   1166 void 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 
   1187 void 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 
   1208 void 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 
   1220 void 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 
   1227 void 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 
   1256 void 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 
   1345 void 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 
   1355 void 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 
   1395 void 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 
   1413 void 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 
   1426 void 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 
   1494 void 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 
   1508 void 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 
   1517 void 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 
   1528 void 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]
   1536 static const uint32_t kPushCullOpSize = 2 * kUInt32Size + sizeof(SkRect);
   1537 void 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 
   1549 void 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 
   1575 SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info) {
   1576     return NULL;
   1577 }
   1578 
   1579 int 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 
   1589 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
   1590     fWriter.writeMatrix(matrix);
   1591 }
   1592 
   1593 const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
   1594     return fPaints.findAndReturnFlat(paint);
   1595 }
   1596 
   1597 const 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 
   1607 void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
   1608     int index = flatPaint ? flatPaint->index() : 0;
   1609     this->addInt(index);
   1610 }
   1611 
   1612 int 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 
   1623 void SkPictureRecord::addPath(const SkPath& path) {
   1624     this->addInt(this->addPathToHeap(path));
   1625 }
   1626 
   1627 void 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 
   1638 void 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 
   1649 void 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 
   1657 void SkPictureRecord::addNoOp() {
   1658     size_t size = kUInt32Size; // op
   1659     this->addDraw(NOOP, &size);
   1660 }
   1661 
   1662 void 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 
   1673 void SkPictureRecord::addRectPtr(const SkRect* rect) {
   1674     if (fWriter.writeBool(rect != NULL)) {
   1675         fWriter.writeRect(*rect);
   1676     }
   1677 }
   1678 
   1679 void SkPictureRecord::addIRect(const SkIRect& rect) {
   1680     fWriter.write(&rect, sizeof(rect));
   1681 }
   1682 
   1683 void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
   1684     if (fWriter.writeBool(rect != NULL)) {
   1685         *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
   1686     }
   1687 }
   1688 
   1689 void SkPictureRecord::addRRect(const SkRRect& rrect) {
   1690     fWriter.writeRRect(rrect);
   1691 }
   1692 
   1693 void SkPictureRecord::addRegion(const SkRegion& region) {
   1694     fWriter.writeRegion(region);
   1695 }
   1696 
   1697 void 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
   1712 size_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 
   1731 int 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 
   1740 int SkPictureRecord::matrices(size_t* size) const {
   1741     int count = fMatrices.count();
   1742     *size = sizeof(fMatrices[0]) * count;
   1743     return count;
   1744 }
   1745 
   1746 int 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 
   1755 int 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 
   1764 int 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 
   1773 size_t SkPictureRecord::streamlen() const {
   1774     return fWriter.size();
   1775 }
   1776 #endif
   1777 
   1778 #ifdef SK_DEBUG_VALIDATE
   1779 void 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 
   1789 void 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 
   1799 void 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 
   1809 void 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 
   1819 void 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 
   1832 void 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