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