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 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 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) 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 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 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 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 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 210 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 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 */ 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? 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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. 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 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 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 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 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 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 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 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 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 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 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 732 void SkPictureRecord::endRecording() { 733 SkASSERT(kNoInitialSave != fInitialSaveCount); 734 this->restoreToCount(fInitialSaveCount); 735 } 736 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 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 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 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 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 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 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 832 void SkPictureRecord::onClipRegion(const SkRegion& region, SkRegion::Op op) { 833 this->recordClipRegion(region, op); 834 this->INHERITED::onClipRegion(region, op); 835 } 836 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); 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 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 1432 SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info, const SkSurfaceProps&) { 1433 return NULL; 1434 } 1435 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 1446 void SkPictureRecord::addMatrix(const SkMatrix& matrix) { 1447 fWriter.writeMatrix(matrix); 1448 } 1449 1450 const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) { 1451 return fPaints.findAndReturnFlat(paint); 1452 } 1453 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 1462 void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) { 1463 int index = flatPaint ? flatPaint->index() : 0; 1464 this->addInt(index); 1465 } 1466 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 1478 void SkPictureRecord::addPath(const SkPath& path) { 1479 this->addInt(this->addPathToHeap(path)); 1480 } 1481 1482 void SkPictureRecord::addPatch(const SkPoint cubics[12]) { 1483 fWriter.write(cubics, SkPatchUtils::kNumCtrlPts * sizeof(SkPoint)); 1484 } 1485 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 1497 void SkPictureRecord::addPoint(const SkPoint& point) { 1498 fWriter.writePoint(point); 1499 } 1500 1501 void SkPictureRecord::addPoints(const SkPoint pts[], int count) { 1502 fWriter.writeMul4(pts, count * sizeof(SkPoint)); 1503 } 1504 1505 void SkPictureRecord::addNoOp() { 1506 size_t size = kUInt32Size; // op 1507 this->addDraw(NOOP, &size); 1508 } 1509 1510 void SkPictureRecord::addRect(const SkRect& rect) { 1511 fWriter.writeRect(rect); 1512 } 1513 1514 void SkPictureRecord::addRectPtr(const SkRect* rect) { 1515 if (fWriter.writeBool(rect != NULL)) { 1516 fWriter.writeRect(*rect); 1517 } 1518 } 1519 1520 void SkPictureRecord::addIRect(const SkIRect& rect) { 1521 fWriter.write(&rect, sizeof(rect)); 1522 } 1523 1524 void SkPictureRecord::addIRectPtr(const SkIRect* rect) { 1525 if (fWriter.writeBool(rect != NULL)) { 1526 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect; 1527 } 1528 } 1529 1530 void SkPictureRecord::addRRect(const SkRRect& rrect) { 1531 fWriter.writeRRect(rrect); 1532 } 1533 1534 void SkPictureRecord::addRegion(const SkRegion& region) { 1535 fWriter.writeRegion(region); 1536 } 1537 1538 void SkPictureRecord::addText(const void* text, size_t byteLength) { 1539 fContentInfo.onDrawText(); 1540 addInt(SkToInt(byteLength)); 1541 fWriter.writePad(text, byteLength); 1542 } 1543 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