SkPicture.cpp revision 6b392b6111fad269437517494b14be484cd762f6
1 2/* 3 * Copyright 2007 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkPictureFlat.h" 11#include "SkPicturePlayback.h" 12#include "SkPictureRecord.h" 13 14#include "SkBBHFactory.h" 15#include "SkBitmapDevice.h" 16#include "SkCanvas.h" 17#include "SkChunkAlloc.h" 18#include "SkPaintPriv.h" 19#include "SkPicture.h" 20#include "SkRegion.h" 21#include "SkStream.h" 22#include "SkTDArray.h" 23#include "SkTSearch.h" 24#include "SkTime.h" 25 26#include "SkReader32.h" 27#include "SkWriter32.h" 28#include "SkRTree.h" 29#include "SkBBoxHierarchyRecord.h" 30 31#if SK_SUPPORT_GPU 32#include "GrContext.h" 33#endif 34 35template <typename T> int SafeCount(const T* obj) { 36 return obj ? obj->count() : 0; 37} 38 39#define DUMP_BUFFER_SIZE 65536 40 41//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw 42 43 44#ifdef SK_DEBUG 45// enable SK_DEBUG_TRACE to trace DrawType elements when 46// recorded and played back 47// #define SK_DEBUG_TRACE 48// enable SK_DEBUG_SIZE to see the size of picture components 49// #define SK_DEBUG_SIZE 50// enable SK_DEBUG_DUMP to see the contents of recorded elements 51// #define SK_DEBUG_DUMP 52// enable SK_DEBUG_VALIDATE to check internal structures for consistency 53// #define SK_DEBUG_VALIDATE 54#endif 55 56#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP 57const char* DrawTypeToString(DrawType drawType) { 58 switch (drawType) { 59 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break; 60 case CLIP_PATH: return "CLIP_PATH"; 61 case CLIP_REGION: return "CLIP_REGION"; 62 case CLIP_RECT: return "CLIP_RECT"; 63 case CLIP_RRECT: return "CLIP_RRECT"; 64 case CONCAT: return "CONCAT"; 65 case DRAW_BITMAP: return "DRAW_BITMAP"; 66 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX"; 67 case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE"; 68 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT"; 69 case DRAW_CLEAR: return "DRAW_CLEAR"; 70 case DRAW_DATA: return "DRAW_DATA"; 71 case DRAW_OVAL: return "DRAW_OVAL"; 72 case DRAW_PAINT: return "DRAW_PAINT"; 73 case DRAW_PATH: return "DRAW_PATH"; 74 case DRAW_PICTURE: return "DRAW_PICTURE"; 75 case DRAW_POINTS: return "DRAW_POINTS"; 76 case DRAW_POS_TEXT: return "DRAW_POS_TEXT"; 77 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM"; 78 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H"; 79 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM"; 80 case DRAW_RECT: return "DRAW_RECT"; 81 case DRAW_RRECT: return "DRAW_RRECT"; 82 case DRAW_SPRITE: return "DRAW_SPRITE"; 83 case DRAW_TEXT: return "DRAW_TEXT"; 84 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH"; 85 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM"; 86 case DRAW_VERTICES: return "DRAW_VERTICES"; 87 case RESTORE: return "RESTORE"; 88 case ROTATE: return "ROTATE"; 89 case SAVE: return "SAVE"; 90 case SAVE_LAYER: return "SAVE_LAYER"; 91 case SCALE: return "SCALE"; 92 case SET_MATRIX: return "SET_MATRIX"; 93 case SKEW: return "SKEW"; 94 case TRANSLATE: return "TRANSLATE"; 95 case NOOP: return "NOOP"; 96 default: 97 SkDebugf("DrawType error 0x%08x\n", drawType); 98 SkASSERT(0); 99 break; 100 } 101 SkASSERT(0); 102 return NULL; 103} 104#endif 105 106#ifdef SK_DEBUG_VALIDATE 107static void validateMatrix(const SkMatrix* matrix) { 108 SkScalar scaleX = matrix->getScaleX(); 109 SkScalar scaleY = matrix->getScaleY(); 110 SkScalar skewX = matrix->getSkewX(); 111 SkScalar skewY = matrix->getSkewY(); 112 SkScalar perspX = matrix->getPerspX(); 113 SkScalar perspY = matrix->getPerspY(); 114 if (scaleX != 0 && skewX != 0) 115 SkDebugf("scaleX != 0 && skewX != 0\n"); 116 SkASSERT(scaleX == 0 || skewX == 0); 117 SkASSERT(scaleY == 0 || skewY == 0); 118 SkASSERT(perspX == 0); 119 SkASSERT(perspY == 0); 120} 121#endif 122 123 124/////////////////////////////////////////////////////////////////////////////// 125 126SkPicture::SkPicture() 127 : fAccelData(NULL) { 128 this->needsNewGenID(); 129 fRecord = NULL; 130 fPlayback = NULL; 131 fWidth = fHeight = 0; 132} 133 134SkPicture::SkPicture(const SkPicture& src) 135 : INHERITED() 136 , fAccelData(NULL) 137 , fContentInfo(src.fContentInfo) { 138 this->needsNewGenID(); 139 fWidth = src.fWidth; 140 fHeight = src.fHeight; 141 fRecord = NULL; 142 143 /* We want to copy the src's playback. However, if that hasn't been built 144 yet, we need to fake a call to endRecording() without actually calling 145 it (since it is destructive, and we don't want to change src). 146 */ 147 if (src.fPlayback) { 148 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fPlayback)); 149 SkASSERT(NULL == src.fRecord); 150 fUniqueID = src.uniqueID(); // need to call method to ensure != 0 151 } else if (src.fRecord) { 152 SkPictInfo info; 153 this->createHeader(&info); 154 // here we do a fake src.endRecording() 155 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fRecord, info)); 156 } else { 157 fPlayback = NULL; 158 } 159 160 fPathHeap.reset(SkSafeRef(src.fPathHeap.get())); 161} 162 163const SkPath& SkPicture::getPath(int index) const { 164 return (*fPathHeap.get())[index]; 165} 166 167int SkPicture::addPathToHeap(const SkPath& path) { 168 if (NULL == fPathHeap) { 169 fPathHeap.reset(SkNEW(SkPathHeap)); 170 } 171#ifdef SK_DEDUP_PICTURE_PATHS 172 return fPathHeap->insert(path); 173#else 174 return fPathHeap->append(path); 175#endif 176} 177 178void SkPicture::initForPlayback() const { 179 // ensure that the paths bounds are pre-computed 180 if (NULL != fPathHeap.get()) { 181 for (int i = 0; i < fPathHeap->count(); i++) { 182 (*fPathHeap.get())[i].updateBoundsCache(); 183 } 184 } 185} 186 187void SkPicture::dumpSize() const { 188 SkDebugf("--- picture size: paths=%d\n", 189 SafeCount(fPathHeap.get())); 190} 191 192SkPicture::~SkPicture() { 193 SkSafeUnref(fRecord); 194 SkDELETE(fPlayback); 195 SkSafeUnref(fAccelData); 196} 197 198void SkPicture::internalOnly_EnableOpts(bool enableOpts) { 199 if (NULL != fRecord) { 200 fRecord->internalOnly_EnableOpts(enableOpts); 201 } 202} 203 204void SkPicture::swap(SkPicture& other) { 205 SkTSwap(fUniqueID, other.fUniqueID); 206 SkTSwap(fRecord, other.fRecord); 207 SkTSwap(fPlayback, other.fPlayback); 208 SkTSwap(fAccelData, other.fAccelData); 209 SkTSwap(fWidth, other.fWidth); 210 SkTSwap(fHeight, other.fHeight); 211 fPathHeap.swap(&other.fPathHeap); 212 fContentInfo.swap(&other.fContentInfo); 213} 214 215SkPicture* SkPicture::clone() const { 216 SkPicture* clonedPicture = SkNEW(SkPicture); 217 this->clone(clonedPicture, 1); 218 return clonedPicture; 219} 220 221void SkPicture::clone(SkPicture* pictures, int count) const { 222 SkPictCopyInfo copyInfo; 223 SkPictInfo info; 224 this->createHeader(&info); 225 226 for (int i = 0; i < count; i++) { 227 SkPicture* clone = &pictures[i]; 228 229 clone->needsNewGenID(); 230 clone->fWidth = fWidth; 231 clone->fHeight = fHeight; 232 SkSafeSetNull(clone->fRecord); 233 SkDELETE(clone->fPlayback); 234 clone->fContentInfo.set(fContentInfo); 235 236 /* We want to copy the src's playback. However, if that hasn't been built 237 yet, we need to fake a call to endRecording() without actually calling 238 it (since it is destructive, and we don't want to change src). 239 */ 240 if (fPlayback) { 241 if (!copyInfo.initialized) { 242 int paintCount = SafeCount(fPlayback->fPaints); 243 244 /* The alternative to doing this is to have a clone method on the paint and have it 245 * make the deep copy of its internal structures as needed. The holdup to doing 246 * that is at this point we would need to pass the SkBitmapHeap so that we don't 247 * unnecessarily flatten the pixels in a bitmap shader. 248 */ 249 copyInfo.paintData.setCount(paintCount); 250 251 /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is 252 * one, use it. If this SkPicturePlayback was created from a stream, fBitmapHeap 253 * will be NULL, so create a new one. 254 */ 255 if (fPlayback->fBitmapHeap.get() == NULL) { 256 // FIXME: Put this on the stack inside SkPicture::clone. 257 SkBitmapHeap* heap = SkNEW(SkBitmapHeap); 258 copyInfo.controller.setBitmapStorage(heap); 259 heap->unref(); 260 } else { 261 copyInfo.controller.setBitmapStorage(fPlayback->fBitmapHeap); 262 } 263 264 SkDEBUGCODE(int heapSize = SafeCount(fPlayback->fBitmapHeap.get());) 265 for (int i = 0; i < paintCount; i++) { 266 if (NeedsDeepCopy(fPlayback->fPaints->at(i))) { 267 copyInfo.paintData[i] = 268 SkFlatData::Create<SkPaint::FlatteningTraits>(©Info.controller, 269 fPlayback->fPaints->at(i), 0); 270 271 } else { 272 // this is our sentinel, which we use in the unflatten loop 273 copyInfo.paintData[i] = NULL; 274 } 275 } 276 SkASSERT(SafeCount(fPlayback->fBitmapHeap.get()) == heapSize); 277 278 // needed to create typeface playback 279 copyInfo.controller.setupPlaybacks(); 280 copyInfo.initialized = true; 281 } 282 283 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fPlayback, ©Info)); 284 SkASSERT(NULL == fRecord); 285 clone->fUniqueID = this->uniqueID(); // need to call method to ensure != 0 286 } else if (fRecord) { 287 // here we do a fake src.endRecording() 288 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fRecord, info, true)); 289 } else { 290 clone->fPlayback = NULL; 291 } 292 293 clone->fPathHeap.reset(SkSafeRef(fPathHeap.get())); 294 } 295} 296 297SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { 298 static int32_t gNextID = 0; 299 300 int32_t id = sk_atomic_inc(&gNextID); 301 if (id >= 1 << (8 * sizeof(Domain))) { 302 SK_CRASH(); 303 } 304 305 return static_cast<Domain>(id); 306} 307 308/////////////////////////////////////////////////////////////////////////////// 309 310#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES 311 312SkCanvas* SkPicture::beginRecording(int width, int height, 313 uint32_t recordingFlags) { 314 if (fPlayback) { 315 SkDELETE(fPlayback); 316 fPlayback = NULL; 317 } 318 SkSafeUnref(fAccelData); 319 SkSafeSetNull(fRecord); 320 fContentInfo.reset(); 321 322 this->needsNewGenID(); 323 324 // Must be set before calling createBBoxHierarchy 325 fWidth = width; 326 fHeight = height; 327 328 const SkISize size = SkISize::Make(width, height); 329 330 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) { 331 SkBBoxHierarchy* tree = this->createBBoxHierarchy(); 332 SkASSERT(NULL != tree); 333 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size, recordingFlags, tree)); 334 tree->unref(); 335 } else { 336 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags)); 337 } 338 fRecord->beginRecording(); 339 340 return fRecord; 341} 342 343#endif 344 345SkCanvas* SkPicture::beginRecording(int width, int height, 346 SkBBHFactory* bbhFactory, 347 uint32_t recordingFlags) { 348 if (fPlayback) { 349 SkDELETE(fPlayback); 350 fPlayback = NULL; 351 } 352 SkSafeUnref(fAccelData); 353 SkSafeSetNull(fRecord); 354 SkASSERT(NULL == fPathHeap); 355 fContentInfo.reset(); 356 357 this->needsNewGenID(); 358 359 fWidth = width; 360 fHeight = height; 361 362 const SkISize size = SkISize::Make(width, height); 363 364 if (NULL != bbhFactory) { 365 SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height)); 366 SkASSERT(NULL != tree); 367 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size, 368 recordingFlags| 369 kOptimizeForClippedPlayback_RecordingFlag, 370 tree.get())); 371 } else { 372 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags)); 373 } 374 fRecord->beginRecording(); 375 376 return fRecord; 377} 378 379 380#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES 381 382SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const { 383 // TODO: this code is now replicated in SkRTreePicture. Once all external 384 // clients have been weaned off of kOptimizeForClippedPlayback_RecordingFlag, 385 // this code can be removed. 386 387 // These values were empirically determined to produce reasonable 388 // performance in most cases. 389 static const int kRTreeMinChildren = 6; 390 static const int kRTreeMaxChildren = 11; 391 392 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), 393 SkIntToScalar(fHeight)); 394 bool sortDraws = false; // Do not sort draw calls when bulk loading. 395 396 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, 397 aspectRatio, sortDraws); 398} 399 400#endif 401 402SkCanvas* SkPicture::getRecordingCanvas() const { 403 // will be null if we are not recording 404 return fRecord; 405} 406 407void SkPicture::endRecording() { 408 if (NULL == fPlayback) { 409 if (NULL != fRecord) { 410 fRecord->endRecording(); 411 SkPictInfo info; 412 this->createHeader(&info); 413 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info)); 414 SkSafeSetNull(fRecord); 415 } 416 } 417 SkASSERT(NULL == fRecord); 418} 419 420const SkPicture::OperationList& SkPicture::OperationList::InvalidList() { 421 static OperationList gInvalid; 422 return gInvalid; 423} 424 425const SkPicture::OperationList& SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) { 426 this->endRecording(); // TODO: remove eventually 427 if (NULL != fPlayback) { 428 return fPlayback->getActiveOps(queryRect); 429 } 430 return OperationList::InvalidList(); 431} 432 433size_t SkPicture::EXPERIMENTAL_curOpID() const { 434 if (NULL != fPlayback) { 435 return fPlayback->curOpID(); 436 } 437 return 0; 438} 439 440void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) { 441 this->endRecording(); // TODO: remove eventually 442 if (NULL != fPlayback) { 443 fPlayback->draw(*surface, callback); 444 } 445} 446 447/////////////////////////////////////////////////////////////////////////////// 448 449#include "SkStream.h" 450 451static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; 452 453bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { 454 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { 455 return false; 456 } 457 458 if (info.fVersion < MIN_PICTURE_VERSION || 459 info.fVersion > CURRENT_PICTURE_VERSION) { 460 return false; 461 } 462 463 return true; 464} 465 466bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { 467 if (NULL == stream) { 468 return false; 469 } 470 471 // Check magic bytes. 472 SkPictInfo info; 473 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 474 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) { 475 return false; 476 } 477 478 if (pInfo != NULL) { 479 *pInfo = info; 480 } 481 return true; 482} 483 484bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) { 485 // Check magic bytes. 486 SkPictInfo info; 487 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 488 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) { 489 return false; 490 } 491 492 if (pInfo != NULL) { 493 *pInfo = info; 494 } 495 return true; 496} 497 498SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height) 499 : fPlayback(playback) 500 , fRecord(NULL) 501 , fWidth(width) 502 , fHeight(height) 503 , fAccelData(NULL) { 504 this->needsNewGenID(); 505} 506 507SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { 508 SkPictInfo info; 509 510 if (!InternalOnly_StreamIsSKP(stream, &info)) { 511 return NULL; 512 } 513 514 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight)); 515 516 // Check to see if there is a playback to recreate. 517 if (stream->readBool()) { 518 SkPicturePlayback* playback = SkPicturePlayback::CreateFromStream(newPict, stream, 519 info, proc); 520 if (NULL == playback) { 521 SkDELETE(newPict); 522 return NULL; 523 } 524 newPict->fPlayback = playback; 525 } 526 527 return newPict; 528} 529 530SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { 531 SkPictInfo info; 532 533 if (!InternalOnly_BufferIsSKP(buffer, &info)) { 534 return NULL; 535 } 536 537 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight)); 538 539 // Check to see if there is a playback to recreate. 540 if (buffer.readBool()) { 541 SkPicturePlayback* playback = SkPicturePlayback::CreateFromBuffer(newPict, buffer, info); 542 if (NULL == playback) { 543 SkDELETE(newPict); 544 return NULL; 545 } 546 newPict->fPlayback = playback; 547 } 548 549 return newPict; 550} 551 552void SkPicture::createHeader(SkPictInfo* info) const { 553 // Copy magic bytes at the beginning of the header 554 SkASSERT(sizeof(kMagic) == 8); 555 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); 556 memcpy(info->fMagic, kMagic, sizeof(kMagic)); 557 558 // Set picture info after magic bytes in the header 559 info->fVersion = CURRENT_PICTURE_VERSION; 560 info->fWidth = fWidth; 561 info->fHeight = fHeight; 562 info->fFlags = SkPictInfo::kCrossProcess_Flag; 563 // TODO: remove this flag, since we're always float (now) 564 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; 565 566 if (8 == sizeof(void*)) { 567 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; 568 } 569} 570 571void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { 572 SkPicturePlayback* playback = fPlayback; 573 574 SkPictInfo info; 575 this->createHeader(&info); 576 if (NULL == playback && fRecord) { 577 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info)); 578 } 579 580 stream->write(&info, sizeof(info)); 581 if (playback) { 582 stream->writeBool(true); 583 playback->serialize(stream, encoder); 584 // delete playback if it is a local version (i.e. cons'd up just now) 585 if (playback != fPlayback) { 586 SkDELETE(playback); 587 } 588 } else { 589 stream->writeBool(false); 590 } 591} 592 593void SkPicture::WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size) { 594 buffer.writeUInt(tag); 595 buffer.writeUInt(SkToU32(size)); 596} 597 598void SkPicture::WriteTagSize(SkWStream* stream, uint32_t tag, size_t size) { 599 stream->write32(tag); 600 stream->write32(SkToU32(size)); 601} 602 603bool SkPicture::parseBufferTag(SkReadBuffer& buffer, 604 uint32_t tag, 605 uint32_t size) { 606 switch (tag) { 607 case SK_PICT_PATH_BUFFER_TAG: 608 if (size > 0) { 609 fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer))); 610 } 611 break; 612 default: 613 // The tag was invalid. 614 return false; 615 } 616 617 return true; // success 618} 619 620void SkPicture::flattenToBuffer(SkWriteBuffer& buffer) const { 621 int n; 622 623 if ((n = SafeCount(fPathHeap.get())) > 0) { 624 WriteTagSize(buffer, SK_PICT_PATH_BUFFER_TAG, n); 625 fPathHeap->flatten(buffer); 626 } 627} 628 629void SkPicture::flatten(SkWriteBuffer& buffer) const { 630 SkPicturePlayback* playback = fPlayback; 631 632 SkPictInfo info; 633 this->createHeader(&info); 634 if (NULL == playback && fRecord) { 635 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info)); 636 } 637 638 buffer.writeByteArray(&info, sizeof(info)); 639 if (playback) { 640 buffer.writeBool(true); 641 playback->flatten(buffer); 642 // delete playback if it is a local version (i.e. cons'd up just now) 643 if (playback != fPlayback) { 644 SkDELETE(playback); 645 } 646 } else { 647 buffer.writeBool(false); 648 } 649} 650 651#if SK_SUPPORT_GPU 652bool SkPicture::suitableForGpuRasterization(GrContext* context) const { 653 // TODO: the heuristic used here needs to be refined 654 static const int kNumPaintWithPathEffectUsesTol = 1; 655 static const int kNumAAConcavePaths = 5; 656 657 SkASSERT(this->numAAHairlineConcavePaths() <= this->numAAConcavePaths()); 658 659 return this->numPaintWithPathEffectUses() < kNumPaintWithPathEffectUsesTol && 660 (this->numAAConcavePaths()-this->numAAHairlineConcavePaths()) < kNumAAConcavePaths; 661} 662#endif 663 664bool SkPicture::willPlayBackBitmaps() const { 665 if (!fPlayback) { 666 return false; 667 } 668 return fPlayback->containsBitmaps(); 669} 670 671#ifdef SK_BUILD_FOR_ANDROID 672void SkPicture::abortPlayback() { 673 if (NULL == fPlayback) { 674 return; 675 } 676 fPlayback->abort(); 677} 678#endif 679 680static int32_t next_picture_generation_id() { 681 static int32_t gPictureGenerationID = 0; 682 // do a loop in case our global wraps around, as we never want to 683 // return a 0 684 int32_t genID; 685 do { 686 genID = sk_atomic_inc(&gPictureGenerationID) + 1; 687 } while (SK_InvalidGenID == genID); 688 return genID; 689} 690 691uint32_t SkPicture::uniqueID() const { 692 if (NULL != fRecord) { 693 SkASSERT(NULL == fPlayback); 694 return SK_InvalidGenID; 695 } 696 697 if (SK_InvalidGenID == fUniqueID) { 698 fUniqueID = next_picture_generation_id(); 699 } 700 return fUniqueID; 701} 702