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