Home | History | Annotate | Download | only in core
      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 "SkPictureData.h"
     12 #include "SkPicturePlayback.h"
     13 #include "SkPictureRecord.h"
     14 #include "SkPictureRecorder.h"
     15 #include "SkPictureStateTree.h"
     16 
     17 #include "SkBitmapDevice.h"
     18 #include "SkCanvas.h"
     19 #include "SkChunkAlloc.h"
     20 #include "SkDrawPictureCallback.h"
     21 #include "SkPaintPriv.h"
     22 #include "SkPathEffect.h"
     23 #include "SkPicture.h"
     24 #include "SkRegion.h"
     25 #include "SkShader.h"
     26 #include "SkStream.h"
     27 #include "SkTDArray.h"
     28 #include "SkTLogic.h"
     29 #include "SkTSearch.h"
     30 #include "SkTime.h"
     31 
     32 #include "SkReader32.h"
     33 #include "SkWriter32.h"
     34 #include "SkRTree.h"
     35 #include "SkBBoxHierarchyRecord.h"
     36 
     37 #if SK_SUPPORT_GPU
     38 #include "GrContext.h"
     39 #endif
     40 
     41 #include "SkRecord.h"
     42 #include "SkRecordDraw.h"
     43 #include "SkRecordOpts.h"
     44 #include "SkRecorder.h"
     45 
     46 template <typename T> int SafeCount(const T* obj) {
     47     return obj ? obj->count() : 0;
     48 }
     49 
     50 ///////////////////////////////////////////////////////////////////////////////
     51 
     52 namespace {
     53 
     54 // Some commands have a paint, some have an optional paint.  Either way, get back a pointer.
     55 static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
     56 static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
     57 
     58 /** SkRecords visitor to determine whether an instance may require an
     59     "external" bitmap to rasterize. May return false positives.
     60     Does not return true for bitmap text.
     61 
     62     Expected use is to determine whether images need to be decoded before
     63     rasterizing a particular SkRecord.
     64  */
     65 struct BitmapTester {
     66     // Helpers.  These create HasMember_bitmap and HasMember_paint.
     67     SK_CREATE_MEMBER_DETECTOR(bitmap);
     68     SK_CREATE_MEMBER_DETECTOR(paint);
     69 
     70 
     71     // Main entry for visitor:
     72     // If the command is a DrawPicture, recurse.
     73     // If the command has a bitmap directly, return true.
     74     // If the command has a paint and the paint has a bitmap, return true.
     75     // Otherwise, return false.
     76     bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
     77 
     78     template <typename T>
     79     bool operator()(const T& r) { return CheckBitmap(r); }
     80 
     81 
     82     // If the command has a bitmap, of course we're going to play back bitmaps.
     83     template <typename T>
     84     static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
     85 
     86     // If not, look for one in its paint (if it has a paint).
     87     template <typename T>
     88     static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
     89 
     90     // If we have a paint, dig down into the effects looking for a bitmap.
     91     template <typename T>
     92     static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
     93         const SkPaint* paint = AsPtr(r.paint);
     94         if (paint) {
     95             const SkShader* shader = paint->getShader();
     96             if (shader &&
     97                 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
     98                 return true;
     99             }
    100         }
    101         return false;
    102     }
    103 
    104     // If we don't have a paint, that non-paint has no bitmap.
    105     template <typename T>
    106     static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
    107 };
    108 
    109 bool WillPlaybackBitmaps(const SkRecord& record) {
    110     BitmapTester tester;
    111     for (unsigned i = 0; i < record.count(); i++) {
    112         if (record.visit<bool>(i, tester)) {
    113             return true;
    114         }
    115     }
    116     return false;
    117 }
    118 
    119 // SkRecord visitor to find recorded text.
    120 struct TextHunter {
    121     // All ops with text have that text as a char array member named "text".
    122     SK_CREATE_MEMBER_DETECTOR(text);
    123     bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
    124     template <typename T> SK_WHEN(HasMember_text<T>,  bool) operator()(const T&) { return true;  }
    125     template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; }
    126 };
    127 
    128 } // namespace
    129 
    130 /** SkRecords visitor to determine heuristically whether or not a SkPicture
    131     will be performant when rasterized on the GPU.
    132  */
    133 struct SkPicture::PathCounter {
    134     SK_CREATE_MEMBER_DETECTOR(paint);
    135 
    136     PathCounter()
    137         : numPaintWithPathEffectUses (0)
    138         , numFastPathDashEffects (0)
    139         , numAAConcavePaths (0)
    140         , numAAHairlineConcavePaths (0) {
    141     }
    142 
    143     // Recurse into nested pictures.
    144     void operator()(const SkRecords::DrawPicture& op) {
    145         const SkPicture::Analysis& analysis = op.picture->fAnalysis;
    146         numPaintWithPathEffectUses += analysis.fNumPaintWithPathEffectUses;
    147         numFastPathDashEffects     += analysis.fNumFastPathDashEffects;
    148         numAAConcavePaths          += analysis.fNumAAConcavePaths;
    149         numAAHairlineConcavePaths  += analysis.fNumAAHairlineConcavePaths;
    150     }
    151 
    152     void checkPaint(const SkPaint* paint) {
    153         if (paint && paint->getPathEffect()) {
    154             numPaintWithPathEffectUses++;
    155         }
    156     }
    157 
    158     void operator()(const SkRecords::DrawPoints& op) {
    159         this->checkPaint(&op.paint);
    160         const SkPathEffect* effect = op.paint.getPathEffect();
    161         if (effect) {
    162             SkPathEffect::DashInfo info;
    163             SkPathEffect::DashType dashType = effect->asADash(&info);
    164             if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
    165                 SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
    166                 numFastPathDashEffects++;
    167             }
    168         }
    169     }
    170 
    171     void operator()(const SkRecords::DrawPath& op) {
    172         this->checkPaint(&op.paint);
    173         if (op.paint.isAntiAlias() && !op.path.isConvex()) {
    174             numAAConcavePaths++;
    175 
    176             if (SkPaint::kStroke_Style == op.paint.getStyle() &&
    177                 0 == op.paint.getStrokeWidth()) {
    178                 numAAHairlineConcavePaths++;
    179             }
    180         }
    181     }
    182 
    183     template <typename T>
    184     SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
    185         this->checkPaint(AsPtr(op.paint));
    186     }
    187 
    188     template <typename T>
    189     SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
    190 
    191     int numPaintWithPathEffectUses;
    192     int numFastPathDashEffects;
    193     int numAAConcavePaths;
    194     int numAAHairlineConcavePaths;
    195 };
    196 
    197 SkPicture::Analysis::Analysis(const SkRecord& record) {
    198     fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
    199 
    200     PathCounter counter;
    201     for (unsigned i = 0; i < record.count(); i++) {
    202         record.visit<void>(i, counter);
    203     }
    204     fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
    205     fNumFastPathDashEffects     = counter.numFastPathDashEffects;
    206     fNumAAConcavePaths          = counter.numAAConcavePaths;
    207     fNumAAHairlineConcavePaths  = counter.numAAHairlineConcavePaths;
    208 
    209     fHasText = false;
    210     TextHunter text;
    211     for (unsigned i = 0; i < record.count(); i++) {
    212         if (record.visit<bool>(i, text)) {
    213             fHasText = true;
    214             break;
    215         }
    216     }
    217 }
    218 
    219 bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
    220                                                       int sampleCount) const {
    221     // TODO: the heuristic used here needs to be refined
    222     static const int kNumPaintWithPathEffectsUsesTol = 1;
    223     static const int kNumAAConcavePathsTol = 5;
    224 
    225     int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
    226                                   fNumFastPathDashEffects;
    227     bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
    228                            (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
    229                                && 0 == sampleCount);
    230 
    231     bool ret = suitableForDash &&
    232                (fNumAAConcavePaths - fNumAAHairlineConcavePaths)
    233                    < kNumAAConcavePathsTol;
    234 
    235     if (!ret && reason) {
    236         if (!suitableForDash) {
    237             if (0 != sampleCount) {
    238                 *reason = "Can't use multisample on dash effect.";
    239             } else {
    240                 *reason = "Too many non dashed path effects.";
    241             }
    242         } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
    243                     >= kNumAAConcavePathsTol)
    244             *reason = "Too many anti-aliased concave paths.";
    245         else
    246             *reason = "Unknown reason for GPU unsuitability.";
    247     }
    248     return ret;
    249 }
    250 
    251 ///////////////////////////////////////////////////////////////////////////////
    252 
    253 // fRecord OK
    254 SkPicture::SkPicture(SkScalar width, SkScalar height,
    255                      const SkPictureRecord& record,
    256                      bool deepCopyOps)
    257     : fCullWidth(width)
    258     , fCullHeight(height)
    259     , fAnalysis() {
    260     this->needsNewGenID();
    261 
    262     SkPictInfo info;
    263     this->createHeader(&info);
    264     fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
    265 }
    266 
    267 // Create an SkPictureData-backed SkPicture from an SkRecord.
    268 // This for compatibility with serialization code only.  This is not cheap.
    269 static SkPicture* backport(const SkRecord& src, const SkRect& cullRect) {
    270     SkPictureRecorder recorder;
    271     SkRecordDraw(src,
    272                  recorder.DEPRECATED_beginRecording(cullRect.width(), cullRect.height()),
    273                  NULL/*bbh*/, NULL/*callback*/);
    274     return recorder.endRecording();
    275 }
    276 
    277 // fRecord OK
    278 SkPicture::~SkPicture() {
    279     this->callDeletionListeners();
    280 }
    281 
    282 // fRecord OK
    283 #ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
    284 SkPicture* SkPicture::clone() const {
    285     return SkRef(const_cast<SkPicture*>(this));
    286 }
    287 #endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
    288 
    289 // fRecord OK
    290 void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
    291     fAccelData.reset(SkRef(data));
    292 }
    293 
    294 // fRecord OK
    295 const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
    296         SkPicture::AccelData::Key key) const {
    297     if (fAccelData.get() && fAccelData->getKey() == key) {
    298         return fAccelData.get();
    299     }
    300     return NULL;
    301 }
    302 
    303 // fRecord OK
    304 SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
    305     static int32_t gNextID = 0;
    306 
    307     int32_t id = sk_atomic_inc(&gNextID);
    308     if (id >= 1 << (8 * sizeof(Domain))) {
    309         SK_CRASH();
    310     }
    311 
    312     return static_cast<Domain>(id);
    313 }
    314 
    315 ///////////////////////////////////////////////////////////////////////////////
    316 
    317 uint32_t SkPicture::OperationList::offset(int index) const {
    318     SkASSERT(index < fOps.count());
    319     return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
    320 }
    321 
    322 const SkMatrix& SkPicture::OperationList::matrix(int index) const {
    323     SkASSERT(index < fOps.count());
    324     return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
    325 }
    326 
    327 // fRecord OK
    328 void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
    329     SkASSERT(canvas);
    330     SkASSERT(fData.get() || fRecord.get());
    331 
    332     // If the query contains the whole picture, don't bother with the BBH.
    333     SkRect clipBounds = { 0, 0, 0, 0 };
    334     (void)canvas->getClipBounds(&clipBounds);
    335     const bool useBBH = !clipBounds.contains(this->cullRect());
    336 
    337     if (fData.get()) {
    338         SkPicturePlayback playback(this);
    339         playback.setUseBBH(useBBH);
    340         playback.draw(canvas, callback);
    341     }
    342     if (fRecord.get()) {
    343         SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
    344     }
    345 }
    346 
    347 ///////////////////////////////////////////////////////////////////////////////
    348 
    349 #include "SkStream.h"
    350 
    351 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
    352 
    353 // fRecord OK
    354 bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
    355     if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
    356         return false;
    357     }
    358 
    359     if (info.fVersion < MIN_PICTURE_VERSION ||
    360         info.fVersion > CURRENT_PICTURE_VERSION) {
    361         return false;
    362     }
    363 
    364     return true;
    365 }
    366 
    367 // fRecord OK
    368 bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
    369     if (NULL == stream) {
    370         return false;
    371     }
    372 
    373     // Check magic bytes.
    374     SkPictInfo info;
    375     SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
    376 
    377     if (!stream->read(&info.fMagic, sizeof(kMagic))) {
    378         return false;
    379     }
    380 
    381     info.fVersion = stream->readU32();
    382 
    383 #ifndef V35_COMPATIBILITY_CODE
    384     if (info.fVersion < 35) {
    385         info.fCullRect.fLeft = 0;
    386         info.fCullRect.fTop = 0;
    387         info.fCullRect.fRight = SkIntToScalar(stream->readU32());
    388         info.fCullRect.fBottom = SkIntToScalar(stream->readU32());
    389     } else {
    390 #endif
    391         info.fCullRect.fLeft = stream->readScalar();
    392         info.fCullRect.fTop = stream->readScalar();
    393         info.fCullRect.fRight = stream->readScalar();
    394         info.fCullRect.fBottom = stream->readScalar();
    395 #ifndef V35_COMPATIBILITY_CODE
    396     }
    397 #endif
    398 
    399     info.fFlags = stream->readU32();
    400 
    401     if (!IsValidPictInfo(info)) {
    402         return false;
    403     }
    404 
    405     if (pInfo != NULL) {
    406         *pInfo = info;
    407     }
    408     return true;
    409 }
    410 
    411 // fRecord OK
    412 bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
    413     // Check magic bytes.
    414     SkPictInfo info;
    415     SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
    416 
    417     if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
    418         return false;
    419     }
    420 
    421     info.fVersion = buffer->readUInt();
    422 
    423 #ifndef V35_COMPATIBILITY_CODE
    424     if (info.fVersion < 35) {
    425         info.fCullRect.fLeft = 0;
    426         info.fCullRect.fTop = 0;
    427         info.fCullRect.fRight = SkIntToScalar(buffer->readUInt());
    428         info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt());
    429     } else {
    430 #endif
    431         buffer->readRect(&info.fCullRect);
    432 #ifndef V35_COMPATIBILITY_CODE
    433     }
    434 #endif
    435 
    436     info.fFlags = buffer->readUInt();
    437 
    438     if (!IsValidPictInfo(info)) {
    439         return false;
    440     }
    441 
    442     if (pInfo != NULL) {
    443         *pInfo = info;
    444     }
    445     return true;
    446 }
    447 
    448 // fRecord OK
    449 SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height)
    450     : fData(data)
    451     , fCullWidth(width)
    452     , fCullHeight(height)
    453     , fAnalysis() {
    454     this->needsNewGenID();
    455 }
    456 
    457 SkPicture* SkPicture::Forwardport(const SkPicture& src) {
    458     SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
    459     SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height());
    460     src.playback(&canvas);
    461     return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(),
    462                                   record.detach(), NULL/*bbh*/));
    463 }
    464 
    465 // fRecord OK
    466 SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
    467     SkPictInfo info;
    468 
    469     if (!InternalOnly_StreamIsSKP(stream, &info)) {
    470         return NULL;
    471     }
    472 
    473     // Check to see if there is a playback to recreate.
    474     if (stream->readBool()) {
    475         SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
    476         if (NULL == data) {
    477             return NULL;
    478         }
    479         const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
    480         return Forwardport(src);
    481     }
    482 
    483     return NULL;
    484 }
    485 
    486 // fRecord OK
    487 SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
    488     SkPictInfo info;
    489 
    490     if (!InternalOnly_BufferIsSKP(&buffer, &info)) {
    491         return NULL;
    492     }
    493 
    494     // Check to see if there is a playback to recreate.
    495     if (buffer.readBool()) {
    496         SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
    497         if (NULL == data) {
    498             return NULL;
    499         }
    500         const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
    501         return Forwardport(src);
    502     }
    503 
    504     return NULL;
    505 }
    506 
    507 // fRecord OK
    508 void SkPicture::createHeader(SkPictInfo* info) const {
    509     // Copy magic bytes at the beginning of the header
    510     SkASSERT(sizeof(kMagic) == 8);
    511     SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
    512     memcpy(info->fMagic, kMagic, sizeof(kMagic));
    513 
    514     // Set picture info after magic bytes in the header
    515     info->fVersion = CURRENT_PICTURE_VERSION;
    516     info->fCullRect = this->cullRect();
    517     info->fFlags = SkPictInfo::kCrossProcess_Flag;
    518     // TODO: remove this flag, since we're always float (now)
    519     info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
    520 
    521     if (8 == sizeof(void*)) {
    522         info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
    523     }
    524 }
    525 
    526 // fRecord OK
    527 void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
    528     const SkPictureData* data = fData.get();
    529 
    530     // If we're a new-format picture, backport to old format for serialization.
    531     SkAutoTDelete<SkPicture> oldFormat;
    532     if (NULL == data && fRecord.get()) {
    533         oldFormat.reset(backport(*fRecord, this->cullRect()));
    534         data = oldFormat->fData.get();
    535         SkASSERT(data);
    536     }
    537 
    538     SkPictInfo info;
    539     this->createHeader(&info);
    540     SkASSERT(sizeof(SkPictInfo) == 32);
    541     stream->write(&info, sizeof(info));
    542 
    543     if (data) {
    544         stream->writeBool(true);
    545         data->serialize(stream, encoder);
    546     } else {
    547         stream->writeBool(false);
    548     }
    549 }
    550 
    551 // fRecord OK
    552 void SkPicture::flatten(SkWriteBuffer& buffer) const {
    553     const SkPictureData* data = fData.get();
    554 
    555     // If we're a new-format picture, backport to old format for serialization.
    556     SkAutoTDelete<SkPicture> oldFormat;
    557     if (NULL == data && fRecord.get()) {
    558         oldFormat.reset(backport(*fRecord, this->cullRect()));
    559         data = oldFormat->fData.get();
    560         SkASSERT(data);
    561     }
    562 
    563     SkPictInfo info;
    564     this->createHeader(&info);
    565     buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
    566     buffer.writeUInt(info.fVersion);
    567     buffer.writeRect(info.fCullRect);
    568     buffer.writeUInt(info.fFlags);
    569 
    570     if (data) {
    571         buffer.writeBool(true);
    572         data->flatten(buffer);
    573     } else {
    574         buffer.writeBool(false);
    575     }
    576 }
    577 
    578 #if SK_SUPPORT_GPU
    579 // fRecord OK
    580 bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
    581     if (fRecord.get()) {
    582         return fAnalysis.suitableForGpuRasterization(reason, 0);
    583     }
    584     if (NULL == fData.get()) {
    585         if (reason) {
    586             *reason = "Missing internal data.";
    587         }
    588         return false;
    589     }
    590 
    591     return fData->suitableForGpuRasterization(context, reason);
    592 }
    593 #endif
    594 
    595 // fRecord OK
    596 bool SkPicture::hasText() const {
    597     if (fRecord.get()) {
    598         return fAnalysis.fHasText;
    599     }
    600     if (fData.get()) {
    601         return fData->hasText();
    602     }
    603     SkFAIL("Unreachable");
    604     return false;
    605 }
    606 
    607 // fRecord OK
    608 bool SkPicture::willPlayBackBitmaps() const {
    609     if (fRecord.get()) {
    610         return fAnalysis.fWillPlaybackBitmaps;
    611     }
    612     if (fData.get()) {
    613         return fData->containsBitmaps();
    614     }
    615     SkFAIL("Unreachable");
    616     return false;
    617 }
    618 
    619 // fRecord OK
    620 static int32_t next_picture_generation_id() {
    621     static int32_t  gPictureGenerationID = 0;
    622     // do a loop in case our global wraps around, as we never want to
    623     // return a 0
    624     int32_t genID;
    625     do {
    626         genID = sk_atomic_inc(&gPictureGenerationID) + 1;
    627     } while (SK_InvalidGenID == genID);
    628     return genID;
    629 }
    630 
    631 // fRecord OK
    632 uint32_t SkPicture::uniqueID() const {
    633     if (SK_InvalidGenID == fUniqueID) {
    634         fUniqueID = next_picture_generation_id();
    635     }
    636     return fUniqueID;
    637 }
    638 
    639 
    640 static SkRecord* optimized(SkRecord* r) {
    641 #ifdef SK_PICTURE_OPTIMIZE_SK_RECORD
    642     SkRecordOptimize(r);
    643 #endif
    644     return r;
    645 }
    646 
    647 // fRecord OK
    648 SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh)
    649     : fCullWidth(width)
    650     , fCullHeight(height)
    651     , fRecord(optimized(record))
    652     , fBBH(SkSafeRef(bbh))
    653     , fAnalysis(*fRecord) {
    654     // TODO: delay as much of this work until just before first playback?
    655     if (fBBH.get()) {
    656         SkRecordFillBounds(*fRecord, fBBH.get());
    657     }
    658     this->needsNewGenID();
    659 }
    660 
    661 // Note that we are assuming that this entry point will only be called from
    662 // one thread. Currently the only client of this method is
    663 // SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
    664 // thread.
    665 void SkPicture::addDeletionListener(DeletionListener* listener) const {
    666     SkASSERT(listener);
    667 
    668     *fDeletionListeners.append() = SkRef(listener);
    669 }
    670 
    671 void SkPicture::callDeletionListeners() {
    672     for (int i = 0; i < fDeletionListeners.count(); ++i) {
    673         fDeletionListeners[i]->onDeletion(this->uniqueID());
    674     }
    675 
    676     fDeletionListeners.unrefAll();
    677 }
    678 
    679 // fRecord OK
    680 int SkPicture::approximateOpCount() const {
    681     SkASSERT(fRecord.get() || fData.get());
    682     if (fRecord.get()) {
    683         return fRecord->count();
    684     }
    685     if (fData.get()) {
    686         return fData->opCount();
    687     }
    688     return 0;
    689 }
    690