SkPictureRecord.cpp revision 4a8126e7f81384526629b1e21bf89b632ea13cd9
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.
22static const bool kBeClever =
23#ifdef SK_RECORD_LITERAL_PICTURES
24    false;
25#else
26    true;
27#endif
28
29enum {
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.
35static int const kUInt32Size = 4;
36
37static const uint32_t kSaveSize = kUInt32Size;
38static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
39static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
40
41SkPictureRecord::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
57SkPictureRecord::~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)
71static inline size_t getPaintOffset(DrawType op, size_t opSize) {
72    // These offsets are where the paint would be if the op size doesn't overflow
73    static const uint8_t gPaintOffsets[] = {
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
149void 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
158void 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
168SkCanvas::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
187void 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
210bool SkPictureRecord::isDrawingToLayer() const {
211    return fFirstSavedLayerIndex != kNoSavedLayerIndex;
212}
213
214/*
215 * Read the op code from 'offset' in 'writer'.
216 */
217#ifdef SK_DEBUG
218static 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 */
226static 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?
239static 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.
253struct 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 */
266static 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
312static 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 */
325static 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 */
353static 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 */
362static 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 */
438static 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
462static 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 */
480static 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
518typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
519                                     SkPaintDictionary* paintDict);
520enum PictureRecordOptType {
521    kRewind_OptType,  // Optimization rewinds the command stream
522    kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
523};
524
525enum 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
532struct 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 */
542static 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.
557static 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
578void 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
626void 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
637void 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
648void 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
659void 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
674void 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
683void 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
693static 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
709void 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
725void 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
732void SkPictureRecord::endRecording() {
733    SkASSERT(kNoInitialSave != fInitialSaveCount);
734    this->restoreToCount(fInitialSaveCount);
735}
736
737size_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
767void 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
772size_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
789void 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
794size_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
810void 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
816size_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
832void SkPictureRecord::onClipRegion(const SkRegion& region, SkRegion::Op op) {
833    this->recordClipRegion(region, op);
834    this->INHERITED::onClipRegion(region, op);
835}
836
837size_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
854void 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
862void 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
871void 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
887void 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
897void 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
907void 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
923void 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
935void 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
947void 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
964void 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
989void 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
1005void 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
1022void 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
1039void 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
1051void 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
1058void 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
1082void 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
1159void 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
1165void 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
1198void 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
1212void 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
1228void 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
1250void 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
1313void 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
1356void 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
1365void 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
1374void 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
1385void 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]
1393static const uint32_t kPushCullOpSize = 2 * kUInt32Size + sizeof(SkRect);
1394void 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
1406void 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
1432SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info, const SkSurfaceProps&) {
1433    return NULL;
1434}
1435
1436int 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
1446void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1447    fWriter.writeMatrix(matrix);
1448}
1449
1450const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
1451    return fPaints.findAndReturnFlat(paint);
1452}
1453
1454const 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
1462void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
1463    int index = flatPaint ? flatPaint->index() : 0;
1464    this->addInt(index);
1465}
1466
1467int 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
1478void SkPictureRecord::addPath(const SkPath& path) {
1479    this->addInt(this->addPathToHeap(path));
1480}
1481
1482void SkPictureRecord::addPatch(const SkPoint cubics[12]) {
1483    fWriter.write(cubics, SkPatchUtils::kNumCtrlPts * sizeof(SkPoint));
1484}
1485
1486void 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
1497void SkPictureRecord::addPoint(const SkPoint& point) {
1498    fWriter.writePoint(point);
1499}
1500
1501void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1502    fWriter.writeMul4(pts, count * sizeof(SkPoint));
1503}
1504
1505void SkPictureRecord::addNoOp() {
1506    size_t size = kUInt32Size; // op
1507    this->addDraw(NOOP, &size);
1508}
1509
1510void SkPictureRecord::addRect(const SkRect& rect) {
1511    fWriter.writeRect(rect);
1512}
1513
1514void SkPictureRecord::addRectPtr(const SkRect* rect) {
1515    if (fWriter.writeBool(rect != NULL)) {
1516        fWriter.writeRect(*rect);
1517    }
1518}
1519
1520void SkPictureRecord::addIRect(const SkIRect& rect) {
1521    fWriter.write(&rect, sizeof(rect));
1522}
1523
1524void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1525    if (fWriter.writeBool(rect != NULL)) {
1526        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1527    }
1528}
1529
1530void SkPictureRecord::addRRect(const SkRRect& rrect) {
1531    fWriter.writeRRect(rrect);
1532}
1533
1534void SkPictureRecord::addRegion(const SkRegion& region) {
1535    fWriter.writeRegion(region);
1536}
1537
1538void SkPictureRecord::addText(const void* text, size_t byteLength) {
1539    fContentInfo.onDrawText();
1540    addInt(SkToInt(byteLength));
1541    fWriter.writePad(text, byteLength);
1542}
1543
1544void 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