diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp index b61de1c912..4324080325 100644 --- a/src/pipe/SkGPipeWrite.cpp +++ b/src/pipe/SkGPipeWrite.cpp @@ -167,32 +167,63 @@ public: /////////////////////////////////////////////////////////////////////////////// +/** + * If SkBitmaps are to be flattened to send to the reader, this class is + * provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so. + */ +class BitmapShuttle : public SkBitmapHeap::ExternalStorage { +public: + BitmapShuttle(SkGPipeCanvas*); + + ~BitmapShuttle(); + + virtual bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE; + + /** + * Remove the SkGPipeCanvas used for insertion. After this, calls to + * insert will crash. + */ + void removeCanvas(); + +private: + SkGPipeCanvas* fCanvas; +}; + +/////////////////////////////////////////////////////////////////////////////// + class SkGPipeCanvas : public SkCanvas { public: SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags, uint32_t width, uint32_t height); virtual ~SkGPipeCanvas(); - void finish() { - if (!fDone) { - if (this->needOpBytes()) { - this->writeOp(kDone_DrawOp); - this->doNotify(); - if (shouldFlattenBitmaps(fFlags)) { - // In this case, a BitmapShuttle is reffed by the SkBitmapHeap - // and refs this canvas. Unref the SkBitmapHeap to end the - // circular reference. When shouldFlattenBitmaps is false, - // there is no circular reference, so the SkBitmapHeap can be - // safely unreffed in the destructor. - fBitmapHeap->unref(); - // This eliminates a similar circular reference (Canvas owns - // the FlattenableHeap which holds a ref to the SkBitmapHeap). - fFlattenableHeap.setBitmapStorage(NULL); - fBitmapHeap = NULL; - } - } - fDone = true; + /** + * Called when nothing else is to be written to the stream. Any repeated + * calls are ignored. + * + * @param notifyReaders Whether to send a message to the reader(s) that + * the writer is through sending commands. Should generally be true, + * unless there is an error which prevents further messages from + * being sent. + */ + void finish(bool notifyReaders) { + if (fDone) { + return; } + if (notifyReaders && this->needOpBytes()) { + this->writeOp(kDone_DrawOp); + this->doNotify(); + } + if (shouldFlattenBitmaps(fFlags)) { + // The following circular references exist: + // fFlattenableHeap -> fWriteBuffer -> fBitmapStorage -> fExternalStorage -> fCanvas + // fBitmapHeap -> fExternalStorage -> fCanvas + // fFlattenableHeap -> fBitmapStorage -> fExternalStorage -> fCanvas + + // Break them all by destroying the final link to this SkGPipeCanvas. + fBitmapShuttle->removeCanvas(); + } + fDone = true; } void flushRecording(bool detachCurrentBlock); @@ -306,9 +337,11 @@ private: // if a new SkFlatData was added when in cross process mode void flattenFactoryNames(); - FlattenableHeap fFlattenableHeap; - FlatDictionary fFlatDictionary; - int fCurrFlatIndex[kCount_PaintFlats]; + FlattenableHeap fFlattenableHeap; + FlatDictionary fFlatDictionary; + SkAutoTUnref fBitmapShuttle; + int fCurrFlatIndex[kCount_PaintFlats]; + int flattenToIndex(SkFlattenable* obj, PaintFlats); // Common code used by drawBitmap*. Behaves differently depending on the @@ -390,24 +423,6 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) { /////////////////////////////////////////////////////////////////////////////// -/** - * If SkBitmaps are to be flattened to send to the reader, this class is - * provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so. - */ -class BitmapShuttle : public SkBitmapHeap::ExternalStorage { -public: - BitmapShuttle(SkGPipeCanvas*); - - ~BitmapShuttle(); - - virtual bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE; - -private: - SkGPipeCanvas* fCanvas; -}; - -/////////////////////////////////////////////////////////////////////////////// - #define MIN_BLOCK_SIZE (16 * 1024) #define BITMAPS_TO_KEEP 5 #define FLATTENABLES_TO_KEEP 10 @@ -440,9 +455,8 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller, } if (shouldFlattenBitmaps(flags)) { - BitmapShuttle* shuttle = SkNEW_ARGS(BitmapShuttle, (this)); - fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (shuttle, BITMAPS_TO_KEEP)); - shuttle->unref(); + fBitmapShuttle.reset(SkNEW_ARGS(BitmapShuttle, (this))); + fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (fBitmapShuttle.get(), BITMAPS_TO_KEEP)); } else { fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (BITMAPS_TO_KEEP, controller->numberOfReaders())); @@ -456,7 +470,7 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller, } SkGPipeCanvas::~SkGPipeCanvas() { - this->finish(); + this->finish(true); SkSafeUnref(fFactorySet); SkSafeUnref(fBitmapHeap); } @@ -474,7 +488,8 @@ bool SkGPipeCanvas::needOpBytes(size_t needed) { size_t blockSize = SkMax32(MIN_BLOCK_SIZE, needed); void* block = fController->requestBlock(blockSize, &fBlockSize); if (NULL == block) { - fDone = true; + // Do not notify the readers, which would call this function again. + this->finish(false); return false; } SkASSERT(SkIsAlign4(fBlockSize)); @@ -1179,7 +1194,7 @@ SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t void SkGPipeWriter::endRecording() { if (fCanvas) { - fCanvas->finish(); + fCanvas->finish(true); fCanvas->unref(); fCanvas = NULL; } @@ -1211,9 +1226,18 @@ BitmapShuttle::BitmapShuttle(SkGPipeCanvas* canvas) { } BitmapShuttle::~BitmapShuttle() { - fCanvas->unref(); + this->removeCanvas(); } bool BitmapShuttle::insert(const SkBitmap& bitmap, int32_t slot) { + SkASSERT(fCanvas != NULL); return fCanvas->shuttleBitmap(bitmap, slot); } + +void BitmapShuttle::removeCanvas() { + if (NULL == fCanvas) { + return; + } + fCanvas->unref(); + fCanvas = NULL; +}