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