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