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