From ce4dd3de38cd7c29bf5b9d8a8efb55c08ec9be47 Mon Sep 17 00:00:00 2001 From: robertphillips Date: Mon, 7 Jul 2014 13:46:35 -0700 Subject: [PATCH] Split SkPicturePlayback out of SkPictureData This splits the playback functionality out of SkPictureData. The old SkPictureData::draw method is pulled out along with its supporting functions as verbatim as possible. Some follow on CLs will be required to: re-enable profiling in the debugger (and remove the vestiges of SkTimedPicture) re-enable display of command offsets in the picture (this should probably wait until we've switched to SkRecord though) Clean up CachedOperationList (maybe fuse with SkPicture::OperationList) Split SkPicturePlayback into a base class and two derived classes Implement parallel version of GatherGPUInfo for SkRecord Landing this is blocked on removing Android's use of the abortPlayback entry point. R=mtklein@google.com, reed@google.com Author: robertphillips@google.com Review URL: https://codereview.chromium.org/377623002 --- debugger/QT/SkDebuggerGUI.cpp | 36 +- debugger/QT/SkDebuggerGUI.h | 2 +- gyp/core.gypi | 2 + include/core/SkPicture.h | 24 +- include/utils/SkPictureUtils.h | 2 +- src/core/SkPicture.cpp | 32 +- src/core/SkPictureData.cpp | 687 +---------------------------- src/core/SkPictureData.h | 164 +------ src/core/SkPicturePlayback.cpp | 648 +++++++++++++++++++++++++++ src/core/SkPicturePlayback.h | 118 +++++ src/gpu/GrPictureUtils.cpp | 66 ++- src/gpu/SkGpuDevice.cpp | 26 +- src/utils/SkPictureUtils.cpp | 2 +- src/utils/debugger/SkDebugCanvas.h | 2 + tools/CopyTilesRenderer.cpp | 2 +- tools/CopyTilesRenderer.h | 4 +- tools/PictureRenderer.cpp | 15 +- tools/PictureRenderer.h | 25 +- 18 files changed, 899 insertions(+), 958 deletions(-) create mode 100644 src/core/SkPicturePlayback.cpp create mode 100644 src/core/SkPicturePlayback.h diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp index 8610ebec5f..b6920a7b3e 100644 --- a/debugger/QT/SkDebuggerGUI.cpp +++ b/debugger/QT/SkDebuggerGUI.cpp @@ -11,6 +11,7 @@ #include "SkImageDecoder.h" #include #include "PictureRenderer.h" +#include "SkPicturePlayback.h" #include "SkPictureRecord.h" #include "SkPictureData.h" @@ -158,23 +159,11 @@ void SkDebuggerGUI::showDeletes() { // The timed picture playback uses the SkPictureData's profiling stubs // to time individual commands. The offsets are needed to map SkPicture // offsets to individual commands. -class SkTimedPicturePlayback : public SkPictureData { +class SkTimedPicturePlayback : public SkPicturePlayback { public: - static SkTimedPicturePlayback* CreateFromStream(SkStream* stream, const SkPictInfo& info, - SkPicture::InstallPixelRefProc proc, - const SkTDArray& deletedCommands) { - // Mimics SkPictureData::CreateFromStream - SkAutoTDelete playback(SkNEW_ARGS(SkTimedPicturePlayback, - (deletedCommands, info))); - if (!playback->parseStream(stream, proc)) { - return NULL; // we're invalid - } - return playback.detach(); - } - SkTimedPicturePlayback(const SkTDArray& deletedCommands, - const SkPictInfo& info) - : INHERITED(info) + SkTimedPicturePlayback(const SkPicture* picture, const SkTDArray& deletedCommands) + : INHERITED(picture) , fSkipCommands(deletedCommands) , fTot(0.0) , fCurCommand(0) { @@ -256,9 +245,10 @@ protected: #endif private: - typedef SkPictureData INHERITED; + typedef SkPicturePlayback INHERITED; }; +#if 0 // Wrap SkPicture to allow installation of an SkTimedPicturePlayback object class SkTimedPicture : public SkPicture { public: @@ -308,10 +298,11 @@ private: typedef SkPicture INHERITED; }; +#endif // This is a simplification of PictureBenchmark's run with the addition of // clearing of the times after the first pass (in resetTimes) -void SkDebuggerGUI::run(SkTimedPicture* pict, +void SkDebuggerGUI::run(const SkPicture* pict, sk_tools::PictureRenderer* renderer, int repeats) { SkASSERT(pict); @@ -330,8 +321,10 @@ void SkDebuggerGUI::run(SkTimedPicture* pict, renderer->render(); renderer->resetState(true); // flush, swapBuffers and Finish +#if 0 // We throw this away the first batch of times to remove first time effects (such as paging in this program) pict->resetTimes(); +#endif for (int i = 0; i < repeats; ++i) { renderer->setup(); @@ -360,12 +353,15 @@ void SkDebuggerGUI::actionProfile() { return; } - SkAutoTUnref picture(SkTimedPicture::CreateTimedPicture(&inputStream, - &SkImageDecoder::DecodeMemory, fSkipCommands)); + SkAutoTUnref picture(SkPicture::CreateFromStream(&inputStream, + &SkImageDecoder::DecodeMemory)); // , fSkipCommands)); if (NULL == picture.get()) { return; } + +#if 0 + // For now this #if allows switching between tiled and simple rendering // modes. Eventually this will be accomplished via the GUI #if 0 @@ -414,6 +410,8 @@ void SkDebuggerGUI::actionProfile() { setupOverviewText(picture->typeTimes(), picture->totTime(), kNumRepeats); setupClipStackText(); + +#endif } void SkDebuggerGUI::actionCancel() { diff --git a/debugger/QT/SkDebuggerGUI.h b/debugger/QT/SkDebuggerGUI.h index a137ee7cfb..3f445500c7 100644 --- a/debugger/QT/SkDebuggerGUI.h +++ b/debugger/QT/SkDebuggerGUI.h @@ -357,7 +357,7 @@ private: Render the supplied picture several times tracking the time consumed by each command. */ - void run(SkTimedPicture* pict, + void run(const SkPicture* pict, sk_tools::PictureRenderer* renderer, int repeats); }; diff --git a/gyp/core.gypi b/gyp/core.gypi index 31479caff7..ae782db053 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -133,6 +133,8 @@ '<(skia_src_path)/core/SkPictureData.h', '<(skia_src_path)/core/SkPictureFlat.cpp', '<(skia_src_path)/core/SkPictureFlat.h', + '<(skia_src_path)/core/SkPicturePlayback.cpp', + '<(skia_src_path)/core/SkPicturePlayback.h', '<(skia_src_path)/core/SkPictureRecord.cpp', '<(skia_src_path)/core/SkPictureRecord.h', '<(skia_src_path)/core/SkPictureRecorder.cpp', diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 62fc633198..d57af50611 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -181,15 +181,6 @@ public: */ bool willPlayBackBitmaps() const; -#ifdef SK_BUILD_FOR_ANDROID - /** Signals that the caller is prematurely done replaying the drawing - commands. This can be called from a canvas virtual while the picture - is drawing. Has no effect if the picture is not drawing. - @deprecated preserving for legacy purposes - */ - void abortPlayback(); -#endif - /** Return true if the SkStream/Buffer represents a serialized picture, and fills out SkPictInfo. After this function returns, the data source is not rewound so it will have to be manually reset before passing to @@ -272,10 +263,6 @@ private: public: virtual ~OperationList() {} - // If valid returns false then there is no optimization data - // present. All the draw operations need to be issued. - virtual bool valid() const { return false; } - // The following three entry points should only be accessed if // 'valid' returns true. virtual int numOps() const { SkASSERT(false); return 0; }; @@ -283,20 +270,12 @@ private: virtual uint32_t offset(int index) const { SkASSERT(false); return 0; }; // The CTM that must be installed for the operation to behave correctly virtual const SkMatrix& matrix(int index) const { SkASSERT(false); return SkMatrix::I(); } - - static const OperationList& InvalidList(); }; /** PRIVATE / EXPERIMENTAL -- do not call Return the operations required to render the content inside 'queryRect'. */ - const OperationList& EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const; - - /** PRIVATE / EXPERIMENTAL -- do not call - Return the ID of the operation currently being executed when playing - back. 0 indicates no call is active. - */ - size_t EXPERIMENTAL_curOpID() const; + const OperationList* EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const; void createHeader(SkPictInfo* info) const; static bool IsValidPictInfo(const SkPictInfo& info); @@ -308,6 +287,7 @@ private: friend class GrGatherCanvas; friend class GrGatherDevice; friend class SkDebugCanvas; + friend class SkPicturePlayback; // to get fData typedef SkRefCnt INHERITED; diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h index c35aca81e2..c35e1c7409 100644 --- a/include/utils/SkPictureUtils.h +++ b/include/utils/SkPictureUtils.h @@ -26,7 +26,7 @@ public: * so the returned pointers are only valid while the picture is in scope * and remains unchanged. */ - static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area); + static SkData* GatherPixelRefs(const SkPicture* pict, const SkRect& area); /** * SkPixelRefContainer provides a base class for more elaborate pixel ref diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp index 0c6f31b2ed..d8b69d4c2b 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -9,6 +9,7 @@ #include "SkPictureFlat.h" #include "SkPictureData.h" +#include "SkPicturePlayback.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" @@ -292,27 +293,13 @@ SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { /////////////////////////////////////////////////////////////////////////////// -// fRecord OK -const SkPicture::OperationList& SkPicture::OperationList::InvalidList() { - static OperationList gInvalid; - return gInvalid; -} - // fRecord TODO -const SkPicture::OperationList& SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const { +const SkPicture::OperationList* SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const { SkASSERT(NULL != fData.get()); if (NULL != fData.get()) { return fData->getActiveOps(queryRect); } - return OperationList::InvalidList(); -} - -// fRecord TODO -size_t SkPicture::EXPERIMENTAL_curOpID() const { - if (NULL != fData.get()) { - return fData->curOpID(); - } - return 0; + return NULL; } // fRecord OK @@ -321,7 +308,8 @@ void SkPicture::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) const { SkASSERT(NULL != fData.get() || NULL != fRecord.get()); if (NULL != fData.get()) { - fData->draw(*canvas, callback); + SkPicturePlayback playback(this); + playback.draw(canvas, callback); } if (NULL != fRecord.get()) { SkRecordDraw(*fRecord, canvas, callback); @@ -538,16 +526,6 @@ bool SkPicture::willPlayBackBitmaps() const { return fData->containsBitmaps(); } -#ifdef SK_BUILD_FOR_ANDROID -// fRecord TODO, fix by switching Android to SkDrawPictureCallback, then deleting this method -void SkPicture::abortPlayback() { - if (NULL == fData.get()) { - return; - } - fData->abort(); -} -#endif - // fRecord OK static int32_t next_picture_generation_id() { static int32_t gPictureGenerationID = 0; diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp index d121a302ba..15ba45d7d6 100644 --- a/src/core/SkPictureData.cpp +++ b/src/core/SkPictureData.cpp @@ -28,33 +28,6 @@ template int SafeCount(const T* obj) { */ #define SPEW_CLIP_SKIPPINGx -SkPictureData::PlaybackReplacements::ReplacementInfo* -SkPictureData::PlaybackReplacements::push() { - SkDEBUGCODE(this->validate()); - return fReplacements.push(); -} - -void SkPictureData::PlaybackReplacements::freeAll() { - for (int i = 0; i < fReplacements.count(); ++i) { - SkDELETE(fReplacements[i].fBM); - } - fReplacements.reset(); -} - -#ifdef SK_DEBUG -void SkPictureData::PlaybackReplacements::validate() const { - // Check that the ranges are monotonically increasing and non-overlapping - if (fReplacements.count() > 0) { - SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop); - - for (int i = 1; i < fReplacements.count(); ++i) { - SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop); - SkASSERT(fReplacements[i-1].fStop < fReplacements[i].fStart); - } - } -} -#endif - SkPictureData::SkPictureData(const SkPictInfo& info) : fInfo(info) { this->init(); @@ -260,12 +233,6 @@ void SkPictureData::init() { fFactoryPlayback = NULL; fBoundingHierarchy = NULL; fStateTree = NULL; - fCachedActiveOps = NULL; - fCurOffset = 0; - fUseBBH = true; - fStart = 0; - fStop = 0; - fReplacements = NULL; } SkPictureData::~SkPictureData() { @@ -276,8 +243,6 @@ SkPictureData::~SkPictureData() { SkSafeUnref(fBoundingHierarchy); SkSafeUnref(fStateTree); - SkDELETE(fCachedActiveOps); - for (int i = 0; i < fPictureCount; i++) { fPictureRefs[i]->unref(); } @@ -475,9 +440,9 @@ static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) { } bool SkPictureData::parseStreamTag(SkStream* stream, - uint32_t tag, - uint32_t size, - SkPicture::InstallPixelRefProc proc) { + uint32_t tag, + uint32_t size, + SkPicture::InstallPixelRefProc proc) { /* * By the time we encounter BUFFER_SIZE_TAG, we need to have already seen * its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required @@ -587,7 +552,7 @@ bool SkPictureData::parseStreamTag(SkStream* stream, } bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, - uint32_t tag, uint32_t size) { + uint32_t tag, uint32_t size) { switch (tag) { case SK_PICT_BITMAP_BUFFER_TAG: { const int count = SkToInt(size); @@ -725,656 +690,33 @@ struct SkipClipRec { }; #endif -#ifdef SK_DEVELOPER -bool SkPictureData::preDraw(int opIndex, int type) { - return false; -} - -void SkPictureData::postDraw(int opIndex) { -} -#endif - -/* - * Read the next op code and chunk size from 'reader'. The returned size - * is the entire size of the chunk (including the opcode). Thus, the - * offset just prior to calling read_op_and_size + 'size' is the offset - * to the next chunk's op code. This also means that the size of a chunk - * with no arguments (just an opcode) will be 4. - */ -static DrawType read_op_and_size(SkReader32* reader, uint32_t* size) { - uint32_t temp = reader->readInt(); - uint32_t op; - if (((uint8_t) temp) == temp) { - // old skp file - no size information - op = temp; - *size = 0; - } else { - UNPACK_8_24(temp, op, *size); - if (MASK_24 == *size) { - *size = reader->readInt(); - } - } - return (DrawType) op; -} - -uint32_t SkPictureData::CachedOperationList::offset(int index) const { +uint32_t SkPictureData::OperationList::offset(int index) const { SkASSERT(index < fOps.count()); return ((SkPictureStateTree::Draw*)fOps[index])->fOffset; } -const SkMatrix& SkPictureData::CachedOperationList::matrix(int index) const { +const SkMatrix& SkPictureData::OperationList::matrix(int index) const { SkASSERT(index < fOps.count()); return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix; } -const SkPicture::OperationList& SkPictureData::getActiveOps(const SkIRect& query) { +const SkPicture::OperationList* SkPictureData::getActiveOps(const SkIRect& query) const { if (NULL == fStateTree || NULL == fBoundingHierarchy) { - return SkPicture::OperationList::InvalidList(); + return NULL; } - if (NULL == fCachedActiveOps) { - fCachedActiveOps = SkNEW(CachedOperationList); - } + OperationList* activeOps = SkNEW(OperationList); - if (query == fCachedActiveOps->fCacheQueryRect) { - return *fCachedActiveOps; - } - - fCachedActiveOps->fOps.rewind(); - - fBoundingHierarchy->search(query, &(fCachedActiveOps->fOps)); - if (0 != fCachedActiveOps->fOps.count()) { + fBoundingHierarchy->search(query, &(activeOps->fOps)); + if (0 != activeOps->fOps.count()) { SkTQSort( - reinterpret_cast(fCachedActiveOps->fOps.begin()), - reinterpret_cast(fCachedActiveOps->fOps.end()-1)); + reinterpret_cast(activeOps->fOps.begin()), + reinterpret_cast(activeOps->fOps.end()-1)); } - fCachedActiveOps->fCacheQueryRect = query; - return *fCachedActiveOps; + return activeOps; } -class SkAutoResetOpID { -public: - SkAutoResetOpID(SkPictureData* data) : fData(data) { } - ~SkAutoResetOpID() { - if (NULL != fData) { - fData->resetOpID(); - } - } - -private: - SkPictureData* fData; -}; - -// TODO: Replace with hash or pass in "lastLookedUp" hint -SkPictureData::PlaybackReplacements::ReplacementInfo* -SkPictureData::PlaybackReplacements::lookupByStart(size_t start) { - SkDEBUGCODE(this->validate()); - for (int i = 0; i < fReplacements.count(); ++i) { - if (start == fReplacements[i].fStart) { - return &fReplacements[i]; - } else if (start < fReplacements[i].fStart) { - return NULL; // the ranges are monotonically increasing and non-overlapping - } - } - - return NULL; -} - -void SkPictureData::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) { - SkAutoResetOpID aroi(this); - SkASSERT(0 == fCurOffset); - -#ifdef ENABLE_TIME_DRAW - SkAutoTime at("SkPicture::draw", 50); -#endif - -#ifdef SPEW_CLIP_SKIPPING - SkipClipRec skipRect, skipRRect, skipRegion, skipPath, skipCull; - int opCount = 0; -#endif - -#ifdef SK_BUILD_FOR_ANDROID - SkAutoMutexAcquire autoMutex(fDrawMutex); -#endif - - // kDrawComplete will be the signal that we have reached the end of - // the command stream - static const uint32_t kDrawComplete = SK_MaxU32; - - SkReader32 reader(fOpData->bytes(), fOpData->size()); - TextContainer text; - const SkTDArray* activeOps = NULL; - - // When draw limits are enabled (i.e., 0 != fStart || 0 != fStop) the state - // tree isn't used to pick and choose the draw operations - if (0 == fStart && 0 == fStop) { - if (fUseBBH && NULL != fStateTree && NULL != fBoundingHierarchy) { - SkRect clipBounds; - if (canvas.getClipBounds(&clipBounds)) { - SkIRect query; - clipBounds.roundOut(&query); - - const SkPicture::OperationList& activeOpsList = this->getActiveOps(query); - if (activeOpsList.valid()) { - if (0 == activeOpsList.numOps()) { - return; // nothing to draw - } - - // Since the opList is valid we know it is our derived class - activeOps = &((const CachedOperationList&)activeOpsList).fOps; - } - } - } - } - - SkPictureStateTree::Iterator it = (NULL == activeOps) ? - SkPictureStateTree::Iterator() : - fStateTree->getIterator(*activeOps, &canvas); - - if (0 != fStart || 0 != fStop) { - reader.setOffset(fStart); - uint32_t size; - SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size); - SkASSERT(SAVE_LAYER == op); - reader.setOffset(fStart+size); - } - - if (it.isValid()) { - uint32_t skipTo = it.nextDraw(); - if (kDrawComplete == skipTo) { - return; - } - reader.setOffset(skipTo); - } - - // Record this, so we can concat w/ it if we encounter a setMatrix() - SkMatrix initialMatrix = canvas.getTotalMatrix(); - - SkAutoCanvasRestore acr(&canvas, false); - -#ifdef SK_BUILD_FOR_ANDROID - fAbortCurrentPlayback = false; -#endif - -#ifdef SK_DEVELOPER - int opIndex = -1; -#endif - - while (!reader.eof()) { - if (callback && callback->abortDrawing()) { - return; - } -#ifdef SK_BUILD_FOR_ANDROID - if (fAbortCurrentPlayback) { - return; - } -#endif - if (0 != fStart || 0 != fStop) { - size_t offset = reader.offset() ; - if (offset >= fStop) { - uint32_t size; - SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size); - SkASSERT(RESTORE == op); - return; - } - } - - if (NULL != fReplacements) { - // Potentially replace a block of operations with a single drawBitmap call - SkPictureData::PlaybackReplacements::ReplacementInfo* temp = - fReplacements->lookupByStart(reader.offset()); - if (NULL != temp) { - SkASSERT(NULL != temp->fBM); - SkASSERT(NULL != temp->fPaint); - canvas.save(); - canvas.setMatrix(initialMatrix); - SkRect src = SkRect::Make(temp->fSrcRect); - SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY, - temp->fSrcRect.width(), - temp->fSrcRect.height()); - canvas.drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint); - canvas.restore(); - - if (it.isValid()) { - // This save is needed since the BBH will automatically issue - // a restore to balanced the saveLayer we're skipping - canvas.save(); - - // At this point we know that the PictureStateTree was aiming - // for some draw op within temp's saveLayer (although potentially - // in a separate saveLayer nested inside it). - // We need to skip all the operations inside temp's range - // along with all the associated state changes but update - // the state tree to the first operation outside temp's range. - - uint32_t skipTo; - do { - skipTo = it.nextDraw(); - if (kDrawComplete == skipTo) { - break; - } - - if (skipTo <= temp->fStop) { - reader.setOffset(skipTo); - uint32_t size; - DrawType op = read_op_and_size(&reader, &size); - // Since we are relying on the normal SkPictureStateTree - // playback we need to convert any nested saveLayer calls - // it may issue into saves (so that all its internal - // restores will be balanced). - if (SAVE_LAYER == op) { - canvas.save(); - } - } - } while (skipTo <= temp->fStop); - - if (kDrawComplete == skipTo) { - break; - } - - reader.setOffset(skipTo); - } else { - reader.setOffset(temp->fStop); - uint32_t size; - SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size); - SkASSERT(RESTORE == op); - } - continue; - } - } - -#ifdef SPEW_CLIP_SKIPPING - opCount++; -#endif - - fCurOffset = reader.offset(); - uint32_t size; - DrawType op = read_op_and_size(&reader, &size); - size_t skipTo = 0; - if (NOOP == op) { - // NOOPs are to be ignored - do not propagate them any further - skipTo = fCurOffset + size; -#ifdef SK_DEVELOPER - } else { - opIndex++; - if (this->preDraw(opIndex, op)) { - skipTo = fCurOffset + size; - } -#endif - } - - if (0 != skipTo) { - if (it.isValid()) { - // If using a bounding box hierarchy, advance the state tree - // iterator until at or after skipTo - uint32_t adjustedSkipTo; - do { - adjustedSkipTo = it.nextDraw(); - } while (adjustedSkipTo < skipTo); - skipTo = adjustedSkipTo; - } - if (kDrawComplete == skipTo) { - break; - } - reader.setOffset(skipTo); - continue; - } - - switch (op) { - case CLIP_PATH: { - const SkPath& path = getPath(reader); - uint32_t packed = reader.readInt(); - SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); - bool doAA = ClipParams_unpackDoAA(packed); - size_t offsetToRestore = reader.readInt(); - SkASSERT(!offsetToRestore || \ - offsetToRestore >= reader.offset()); - canvas.clipPath(path, regionOp, doAA); - if (canvas.isClipEmpty() && offsetToRestore) { -#ifdef SPEW_CLIP_SKIPPING - skipPath.recordSkip(offsetToRestore - reader.offset()); -#endif - reader.setOffset(offsetToRestore); - } - } break; - case CLIP_REGION: { - SkRegion region; - this->getRegion(reader, ®ion); - uint32_t packed = reader.readInt(); - SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); - size_t offsetToRestore = reader.readInt(); - SkASSERT(!offsetToRestore || \ - offsetToRestore >= reader.offset()); - canvas.clipRegion(region, regionOp); - if (canvas.isClipEmpty() && offsetToRestore) { -#ifdef SPEW_CLIP_SKIPPING - skipRegion.recordSkip(offsetToRestore - reader.offset()); -#endif - reader.setOffset(offsetToRestore); - } - } break; - case CLIP_RECT: { - const SkRect& rect = reader.skipT(); - uint32_t packed = reader.readInt(); - SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); - bool doAA = ClipParams_unpackDoAA(packed); - size_t offsetToRestore = reader.readInt(); - SkASSERT(!offsetToRestore || \ - offsetToRestore >= reader.offset()); - canvas.clipRect(rect, regionOp, doAA); - if (canvas.isClipEmpty() && offsetToRestore) { -#ifdef SPEW_CLIP_SKIPPING - skipRect.recordSkip(offsetToRestore - reader.offset()); -#endif - reader.setOffset(offsetToRestore); - } - } break; - case CLIP_RRECT: { - SkRRect rrect; - reader.readRRect(&rrect); - uint32_t packed = reader.readInt(); - SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); - bool doAA = ClipParams_unpackDoAA(packed); - size_t offsetToRestore = reader.readInt(); - SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); - canvas.clipRRect(rrect, regionOp, doAA); - if (canvas.isClipEmpty() && offsetToRestore) { -#ifdef SPEW_CLIP_SKIPPING - skipRRect.recordSkip(offsetToRestore - reader.offset()); -#endif - reader.setOffset(offsetToRestore); - } - } break; - case PUSH_CULL: { - const SkRect& cullRect = reader.skipT(); - size_t offsetToRestore = reader.readInt(); - if (offsetToRestore && canvas.quickReject(cullRect)) { -#ifdef SPEW_CLIP_SKIPPING - skipCull.recordSkip(offsetToRestore - reader.offset()); -#endif - reader.setOffset(offsetToRestore); - } else { - canvas.pushCull(cullRect); - } - } break; - case POP_CULL: - canvas.popCull(); - break; - case CONCAT: { - SkMatrix matrix; - this->getMatrix(reader, &matrix); - canvas.concat(matrix); - break; - } - case DRAW_BITMAP: { - const SkPaint* paint = this->getPaint(reader); - const SkBitmap& bitmap = this->getBitmap(reader); - const SkPoint& loc = reader.skipT(); - canvas.drawBitmap(bitmap, loc.fX, loc.fY, paint); - } break; - case DRAW_BITMAP_RECT_TO_RECT: { - const SkPaint* paint = this->getPaint(reader); - const SkBitmap& bitmap = this->getBitmap(reader); - const SkRect* src = this->getRectPtr(reader); // may be null - const SkRect& dst = reader.skipT(); // required - SkCanvas::DrawBitmapRectFlags flags; - flags = (SkCanvas::DrawBitmapRectFlags) reader.readInt(); - canvas.drawBitmapRectToRect(bitmap, src, dst, paint, flags); - } break; - case DRAW_BITMAP_MATRIX: { - const SkPaint* paint = this->getPaint(reader); - const SkBitmap& bitmap = this->getBitmap(reader); - SkMatrix matrix; - this->getMatrix(reader, &matrix); - canvas.drawBitmapMatrix(bitmap, matrix, paint); - } break; - case DRAW_BITMAP_NINE: { - const SkPaint* paint = this->getPaint(reader); - const SkBitmap& bitmap = this->getBitmap(reader); - const SkIRect& src = reader.skipT(); - const SkRect& dst = reader.skipT(); - canvas.drawBitmapNine(bitmap, src, dst, paint); - } break; - case DRAW_CLEAR: - canvas.clear(reader.readInt()); - break; - case DRAW_DATA: { - size_t length = reader.readInt(); - canvas.drawData(reader.skip(length), length); - // skip handles padding the read out to a multiple of 4 - } break; - case DRAW_DRRECT: { - const SkPaint& paint = *this->getPaint(reader); - SkRRect outer, inner; - reader.readRRect(&outer); - reader.readRRect(&inner); - canvas.drawDRRect(outer, inner, paint); - } break; - case BEGIN_COMMENT_GROUP: { - const char* desc = reader.readString(); - canvas.beginCommentGroup(desc); - } break; - case COMMENT: { - const char* kywd = reader.readString(); - const char* value = reader.readString(); - canvas.addComment(kywd, value); - } break; - case END_COMMENT_GROUP: { - canvas.endCommentGroup(); - } break; - case DRAW_OVAL: { - const SkPaint& paint = *this->getPaint(reader); - canvas.drawOval(reader.skipT(), paint); - } break; - case DRAW_PAINT: - canvas.drawPaint(*this->getPaint(reader)); - break; - case DRAW_PATH: { - const SkPaint& paint = *this->getPaint(reader); - canvas.drawPath(getPath(reader), paint); - } break; - case DRAW_PICTURE: - canvas.drawPicture(this->getPicture(reader)); - break; - case DRAW_POINTS: { - const SkPaint& paint = *this->getPaint(reader); - SkCanvas::PointMode mode = (SkCanvas::PointMode)reader.readInt(); - size_t count = reader.readInt(); - const SkPoint* pts = (const SkPoint*)reader.skip(sizeof(SkPoint) * count); - canvas.drawPoints(mode, count, pts, paint); - } break; - case DRAW_POS_TEXT: { - const SkPaint& paint = *this->getPaint(reader); - getText(reader, &text); - size_t points = reader.readInt(); - const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint)); - canvas.drawPosText(text.text(), text.length(), pos, paint); - } break; - case DRAW_POS_TEXT_TOP_BOTTOM: { - const SkPaint& paint = *this->getPaint(reader); - getText(reader, &text); - size_t points = reader.readInt(); - const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint)); - const SkScalar top = reader.readScalar(); - const SkScalar bottom = reader.readScalar(); - if (!canvas.quickRejectY(top, bottom)) { - canvas.drawPosText(text.text(), text.length(), pos, paint); - } - } break; - case DRAW_POS_TEXT_H: { - const SkPaint& paint = *this->getPaint(reader); - getText(reader, &text); - size_t xCount = reader.readInt(); - const SkScalar constY = reader.readScalar(); - const SkScalar* xpos = (const SkScalar*)reader.skip(xCount * sizeof(SkScalar)); - canvas.drawPosTextH(text.text(), text.length(), xpos, constY, - paint); - } break; - case DRAW_POS_TEXT_H_TOP_BOTTOM: { - const SkPaint& paint = *this->getPaint(reader); - getText(reader, &text); - size_t xCount = reader.readInt(); - const SkScalar* xpos = (const SkScalar*)reader.skip((3 + xCount) * sizeof(SkScalar)); - const SkScalar top = *xpos++; - const SkScalar bottom = *xpos++; - const SkScalar constY = *xpos++; - if (!canvas.quickRejectY(top, bottom)) { - canvas.drawPosTextH(text.text(), text.length(), xpos, - constY, paint); - } - } break; - case DRAW_RECT: { - const SkPaint& paint = *this->getPaint(reader); - canvas.drawRect(reader.skipT(), paint); - } break; - case DRAW_RRECT: { - const SkPaint& paint = *this->getPaint(reader); - SkRRect rrect; - reader.readRRect(&rrect); - canvas.drawRRect(rrect, paint); - } break; - case DRAW_SPRITE: { - const SkPaint* paint = this->getPaint(reader); - const SkBitmap& bitmap = this->getBitmap(reader); - int left = reader.readInt(); - int top = reader.readInt(); - canvas.drawSprite(bitmap, left, top, paint); - } break; - case DRAW_TEXT: { - const SkPaint& paint = *this->getPaint(reader); - this->getText(reader, &text); - SkScalar x = reader.readScalar(); - SkScalar y = reader.readScalar(); - canvas.drawText(text.text(), text.length(), x, y, paint); - } break; - case DRAW_TEXT_TOP_BOTTOM: { - const SkPaint& paint = *this->getPaint(reader); - this->getText(reader, &text); - const SkScalar* ptr = (const SkScalar*)reader.skip(4 * sizeof(SkScalar)); - // ptr[0] == x - // ptr[1] == y - // ptr[2] == top - // ptr[3] == bottom - if (!canvas.quickRejectY(ptr[2], ptr[3])) { - canvas.drawText(text.text(), text.length(), ptr[0], ptr[1], - paint); - } - } break; - case DRAW_TEXT_ON_PATH: { - const SkPaint& paint = *this->getPaint(reader); - getText(reader, &text); - const SkPath& path = this->getPath(reader); - SkMatrix matrix; - this->getMatrix(reader, &matrix); - canvas.drawTextOnPath(text.text(), text.length(), path, &matrix, paint); - } break; - case DRAW_VERTICES: { - SkAutoTUnref xfer; - const SkPaint& paint = *this->getPaint(reader); - DrawVertexFlags flags = (DrawVertexFlags)reader.readInt(); - SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader.readInt(); - int vCount = reader.readInt(); - const SkPoint* verts = (const SkPoint*)reader.skip( - vCount * sizeof(SkPoint)); - const SkPoint* texs = NULL; - const SkColor* colors = NULL; - const uint16_t* indices = NULL; - int iCount = 0; - if (flags & DRAW_VERTICES_HAS_TEXS) { - texs = (const SkPoint*)reader.skip( - vCount * sizeof(SkPoint)); - } - if (flags & DRAW_VERTICES_HAS_COLORS) { - colors = (const SkColor*)reader.skip( - vCount * sizeof(SkColor)); - } - if (flags & DRAW_VERTICES_HAS_INDICES) { - iCount = reader.readInt(); - indices = (const uint16_t*)reader.skip( - iCount * sizeof(uint16_t)); - } - if (flags & DRAW_VERTICES_HAS_XFER) { - int mode = reader.readInt(); - if (mode < 0 || mode > SkXfermode::kLastMode) { - mode = SkXfermode::kModulate_Mode; - } - xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode)); - } - canvas.drawVertices(vmode, vCount, verts, texs, colors, xfer, - indices, iCount, paint); - } break; - case RESTORE: - canvas.restore(); - break; - case ROTATE: - canvas.rotate(reader.readScalar()); - break; - case SAVE: - // SKPs with version < 29 also store a SaveFlags param. - if (size > 4) { - SkASSERT(8 == size); - reader.readInt(); - } - canvas.save(); - break; - case SAVE_LAYER: { - const SkRect* boundsPtr = this->getRectPtr(reader); - const SkPaint* paint = this->getPaint(reader); - canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader.readInt()); - } break; - case SCALE: { - SkScalar sx = reader.readScalar(); - SkScalar sy = reader.readScalar(); - canvas.scale(sx, sy); - } break; - case SET_MATRIX: { - SkMatrix matrix; - this->getMatrix(reader, &matrix); - matrix.postConcat(initialMatrix); - canvas.setMatrix(matrix); - } break; - case SKEW: { - SkScalar sx = reader.readScalar(); - SkScalar sy = reader.readScalar(); - canvas.skew(sx, sy); - } break; - case TRANSLATE: { - SkScalar dx = reader.readScalar(); - SkScalar dy = reader.readScalar(); - canvas.translate(dx, dy); - } break; - default: - SkASSERT(0); - } - -#ifdef SK_DEVELOPER - this->postDraw(opIndex); -#endif - - if (it.isValid()) { - uint32_t skipTo = it.nextDraw(); - if (kDrawComplete == skipTo) { - break; - } - reader.setOffset(skipTo); - } - } - -#ifdef SPEW_CLIP_SKIPPING - { - size_t size = skipRect.fSize + skipRRect.fSize + skipPath.fSize + skipRegion.fSize + - skipCull.fSize; - SkDebugf("--- Clip skips %d%% rect:%d rrect:%d path:%d rgn:%d cull:%d\n", - size * 100 / reader.offset(), skipRect.fCount, skipRRect.fCount, - skipPath.fCount, skipRegion.fCount, skipCull.fCount); - SkDebugf("--- Total ops: %d\n", opCount); - } -#endif -// this->dumpSize(); -} - - #if SK_SUPPORT_GPU bool SkPictureData::suitableForGpuRasterization(GrContext* context, const char **reason, int sampleCount) const { @@ -1750,7 +1092,6 @@ void SkPictureData::dumpText(char** bufferPtrPtr, char* buffer) { void SkPictureData::dumpStream() { SkDebugf("RecordStream stream = {\n"); DrawType drawType; - TextContainer text; fReadStream.rewind(); char buffer[DUMP_BUFFER_SIZE], * bufferPtr; while (fReadStream.read(&drawType, sizeof(drawType))) { diff --git a/src/core/SkPictureData.h b/src/core/SkPictureData.h index f798e7c923..408d6358c3 100644 --- a/src/core/SkPictureData.h +++ b/src/core/SkPictureData.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -14,10 +13,6 @@ #include "SkPicture.h" #include "SkPictureFlat.h" -#ifdef SK_BUILD_FOR_ANDROID -#include "SkThread.h" -#endif - class SkData; class SkPictureRecord; class SkReader32; @@ -136,25 +131,19 @@ struct SkPictCopyInfo { class SkPictureData { public: #ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE - SkPictureData(const SkPictureData& src, - SkPictCopyInfo* deepCopyInfo = NULL); + SkPictureData(const SkPictureData& src, SkPictCopyInfo* deepCopyInfo = NULL); #else SkPictureData(const SkPictureData& src); #endif SkPictureData(const SkPictureRecord& record, const SkPictInfo&, bool deepCopyOps); static SkPictureData* CreateFromStream(SkStream*, - const SkPictInfo&, - SkPicture::InstallPixelRefProc); - static SkPictureData* CreateFromBuffer(SkReadBuffer&, - const SkPictInfo&); + const SkPictInfo&, + SkPicture::InstallPixelRefProc); + static SkPictureData* CreateFromBuffer(SkReadBuffer&, const SkPictInfo&); virtual ~SkPictureData(); - const SkPicture::OperationList& getActiveOps(const SkIRect& queryRect); - - void setUseBBH(bool useBBH) { fUseBBH = useBBH; } - - void draw(SkCanvas& canvas, SkDrawPictureCallback*); + const SkPicture::OperationList* getActiveOps(const SkIRect& queryRect) const; void serialize(SkWStream*, SkPicture::EncodeBitmap) const; void flatten(SkWriteBuffer&) const; @@ -163,35 +152,15 @@ public: bool containsBitmaps() const; -#ifdef SK_BUILD_FOR_ANDROID - // Can be called in the middle of playback (the draw() call). WIll abort the - // drawing and return from draw() after the "current" op code is done - void abort() { fAbortCurrentPlayback = true; } -#endif - - size_t curOpID() const { return fCurOffset; } - void resetOpID() { fCurOffset = 0; } - protected: explicit SkPictureData(const SkPictInfo& info); bool parseStream(SkStream*, SkPicture::InstallPixelRefProc); bool parseBuffer(SkReadBuffer& buffer); -#ifdef SK_DEVELOPER - virtual bool preDraw(int opIndex, int type); - virtual void postDraw(int opIndex); -#endif private: - class TextContainer { - public: - size_t length() { return fByteLength; } - const void* text() { return (const void*) fText; } - size_t fByteLength; - const char* fText; - }; - const SkBitmap& getBitmap(SkReader32& reader) { + const SkBitmap& getBitmap(SkReader32& reader) const { const int index = reader.readInt(); if (SkBitmapHeap::INVALID_SLOT == index) { #ifdef SK_DEBUG @@ -202,22 +171,18 @@ private: return (*fBitmaps)[index]; } - void getMatrix(SkReader32& reader, SkMatrix* matrix) { - reader.readMatrix(matrix); - } - - const SkPath& getPath(SkReader32& reader) { + const SkPath& getPath(SkReader32& reader) const { int index = reader.readInt() - 1; return (*fPathHeap.get())[index]; } - const SkPicture* getPicture(SkReader32& reader) { + const SkPicture* getPicture(SkReader32& reader) const { int index = reader.readInt(); SkASSERT(index > 0 && index <= fPictureCount); return fPictureRefs[index - 1]; } - const SkPaint* getPaint(SkReader32& reader) { + const SkPaint* getPaint(SkReader32& reader) const { int index = reader.readInt(); if (index == 0) { return NULL; @@ -225,31 +190,6 @@ private: return &(*fPaints)[index - 1]; } - const SkRect* getRectPtr(SkReader32& reader) { - if (reader.readBool()) { - return &reader.skipT(); - } else { - return NULL; - } - } - - const SkIRect* getIRectPtr(SkReader32& reader) { - if (reader.readBool()) { - return &reader.skipT(); - } else { - return NULL; - } - } - - void getRegion(SkReader32& reader, SkRegion* region) { - reader.readRegion(region); - } - - void getText(SkReader32& reader, TextContainer* text) { - size_t length = text->fByteLength = reader.readInt(); - text->fText = (const char*)reader.skip(length); - } - void init(); #ifdef SK_DEBUG_SIZE @@ -306,7 +246,7 @@ private: // these help us with reading/writing private: friend class SkPicture; - friend class SkGpuDevice; // for access to setDrawLimits & setReplacements + friend class SkPicturePlayback; // Only used by getBitmap() if the passed in index is SkBitmapHeap::INVALID_SLOT. This empty // bitmap allows playback to draw nothing and move on. @@ -329,83 +269,13 @@ private: SkPictureContentInfo fContentInfo; - // Limit the opcode playback to be between the offsets 'start' and 'stop'. - // The opcode at 'start' should be a saveLayer while the opcode at - // 'stop' should be a restore. Neither of those commands will be issued. - // Set both start & stop to 0 to disable draw limiting - // Draw limiting cannot be enabled at the same time as draw replacing - void setDrawLimits(size_t start, size_t stop) { - SkASSERT(NULL == fReplacements); - fStart = start; - fStop = stop; - } - - // PlaybackReplacements collects op ranges that can be replaced with - // a single drawBitmap call (using a precomputed bitmap). - class PlaybackReplacements { + class OperationList : public SkPicture::OperationList { public: - // All the operations between fStart and fStop (inclusive) will be replaced with - // a single drawBitmap call using fPos, fBM and fPaint. - // fPaint will be NULL if the picture's paint wasn't copyable - struct ReplacementInfo { - size_t fStart; - size_t fStop; - SkIPoint fPos; - SkBitmap* fBM; // fBM is allocated so ReplacementInfo can remain POD - const SkPaint* fPaint; // Note: this object doesn't own the paint - - SkIRect fSrcRect; - }; - - ~PlaybackReplacements() { this->freeAll(); } - - // Add a new replacement range. The replacement ranges should be - // sorted in increasing order and non-overlapping (esp. no nested - // saveLayers). - ReplacementInfo* push(); - - private: - friend class SkPictureData; // for access to lookupByStart - - // look up a replacement range by its start offset - ReplacementInfo* lookupByStart(size_t start); - - void freeAll(); - -#ifdef SK_DEBUG - void validate() const; -#endif - - SkTDArray fReplacements; - }; - - // Replace all the draw ops in the replacement ranges in 'replacements' with - // the associated drawBitmap call - // Draw replacing cannot be enabled at the same time as draw limiting - void setReplacements(PlaybackReplacements* replacements) { - SkASSERT(fStart == 0 && fStop == 0); - fReplacements = replacements; - } - - bool fUseBBH; - size_t fStart; - size_t fStop; - PlaybackReplacements* fReplacements; - - class CachedOperationList : public SkPicture::OperationList { - public: - CachedOperationList() { - fCacheQueryRect.setEmpty(); - } - - virtual bool valid() const { return true; } + OperationList() { } virtual int numOps() const SK_OVERRIDE { return fOps.count(); } virtual uint32_t offset(int index) const SK_OVERRIDE; virtual const SkMatrix& matrix(int index) const SK_OVERRIDE; - // The query rect for which the cached active ops are valid - SkIRect fCacheQueryRect; - // The operations which are active within 'fCachedQueryRect' SkTDArray fOps; @@ -413,25 +283,15 @@ private: typedef SkPicture::OperationList INHERITED; }; - CachedOperationList* fCachedActiveOps; - SkTypefacePlayback fTFPlayback; SkFactoryPlayback* fFactoryPlayback; - // The offset of the current operation when within the draw method - size_t fCurOffset; - const SkPictInfo fInfo; static void WriteFactories(SkWStream* stream, const SkFactorySet& rec); static void WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec); void initForPlayback() const; - -#ifdef SK_BUILD_FOR_ANDROID - SkMutex fDrawMutex; - bool fAbortCurrentPlayback; -#endif }; #endif diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp new file mode 100644 index 0000000000..eccd1ae0c8 --- /dev/null +++ b/src/core/SkPicturePlayback.cpp @@ -0,0 +1,648 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkPictureData.h" +#include "SkPicturePlayback.h" +#include "SkPictureRecord.h" +#include "SkPictureStateTree.h" +#include "SkReader32.h" +#include "SkTDArray.h" +#include "SkTypes.h" + +SkPicturePlayback::PlaybackReplacements::ReplacementInfo* +SkPicturePlayback::PlaybackReplacements::push() { + SkDEBUGCODE(this->validate()); + return fReplacements.push(); +} + +void SkPicturePlayback::PlaybackReplacements::freeAll() { + for (int i = 0; i < fReplacements.count(); ++i) { + SkDELETE(fReplacements[i].fBM); + } + fReplacements.reset(); +} + +#ifdef SK_DEBUG +void SkPicturePlayback::PlaybackReplacements::validate() const { + // Check that the ranges are monotonically increasing and non-overlapping + if (fReplacements.count() > 0) { + SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop); + + for (int i = 1; i < fReplacements.count(); ++i) { + SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop); + SkASSERT(fReplacements[i - 1].fStop < fReplacements[i].fStart); + } + } +} +#endif + +// TODO: Replace with hash or pass in "lastLookedUp" hint +SkPicturePlayback::PlaybackReplacements::ReplacementInfo* +SkPicturePlayback::PlaybackReplacements::lookupByStart(size_t start) { + SkDEBUGCODE(this->validate()); + for (int i = 0; i < fReplacements.count(); ++i) { + if (start == fReplacements[i].fStart) { + return &fReplacements[i]; + } else if (start < fReplacements[i].fStart) { + return NULL; // the ranges are monotonically increasing and non-overlapping + } + } + + return NULL; +} + + +class SkAutoResetOpID { +public: + SkAutoResetOpID(SkPicturePlayback* playback) : fPlayback(playback) { } + ~SkAutoResetOpID() { + if (NULL != fPlayback) { + fPlayback->resetOpID(); + } + } + +private: + SkPicturePlayback* fPlayback; +}; + +/* +* Read the next op code and chunk size from 'reader'. The returned size +* is the entire size of the chunk (including the opcode). Thus, the +* offset just prior to calling read_op_and_size + 'size' is the offset +* to the next chunk's op code. This also means that the size of a chunk +* with no arguments (just an opcode) will be 4. +*/ +static DrawType read_op_and_size(SkReader32* reader, uint32_t* size) { + uint32_t temp = reader->readInt(); + uint32_t op; + if (((uint8_t)temp) == temp) { + // old skp file - no size information + op = temp; + *size = 0; + } else { + UNPACK_8_24(temp, op, *size); + if (MASK_24 == *size) { + *size = reader->readInt(); + } + } + return (DrawType)op; +} + + +static const SkRect* get_rect_ptr(SkReader32& reader) { + if (reader.readBool()) { + return &reader.skipT(); + } else { + return NULL; + } +} + +class TextContainer { +public: + size_t length() { return fByteLength; } + const void* text() { return (const void*)fText; } + size_t fByteLength; + const char* fText; +}; + +void get_text(SkReader32* reader, TextContainer* text) { + size_t length = text->fByteLength = reader->readInt(); + text->fText = (const char*)reader->skip(length); +} + +void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) { + SkAutoResetOpID aroi(this); + SkASSERT(0 == fCurOffset); + +#ifdef ENABLE_TIME_DRAW + SkAutoTime at("SkPicture::draw", 50); +#endif + +#ifdef SPEW_CLIP_SKIPPING + SkipClipRec skipRect, skipRRect, skipRegion, skipPath, skipCull; + int opCount = 0; +#endif + + // kDrawComplete will be the signal that we have reached the end of + // the command stream + static const uint32_t kDrawComplete = SK_MaxU32; + + SkReader32 reader(fPictureData->fOpData->bytes(), fPictureData->fOpData->size()); + TextContainer text; + SkAutoTDelete activeOpsList; + const SkTDArray* activeOps = NULL; + + // When draw limits are enabled (i.e., 0 != fStart || 0 != fStop) the state + // tree isn't used to pick and choose the draw operations + if (0 == fStart && 0 == fStop) { + if (fUseBBH && NULL != fPictureData->fStateTree && NULL != fPictureData->fBoundingHierarchy) { + SkRect clipBounds; + if (canvas->getClipBounds(&clipBounds)) { + SkIRect query; + clipBounds.roundOut(&query); + + activeOpsList.reset(fPictureData->getActiveOps(query)); + if (NULL != activeOpsList.get()) { + if (0 == activeOpsList->numOps()) { + return; // nothing to draw + } + + // Since the opList is valid we know it is our derived class + activeOps = &((const SkPictureData::OperationList*)activeOpsList.get())->fOps; + } + } + } + } + + SkPictureStateTree::Iterator it = (NULL == activeOps) ? + SkPictureStateTree::Iterator() : + fPictureData->fStateTree->getIterator(*activeOps, canvas); + + if (0 != fStart || 0 != fStop) { + reader.setOffset(fStart); + uint32_t size; + SkDEBUGCODE(DrawType op = ) read_op_and_size(&reader, &size); + SkASSERT(SAVE_LAYER == op); + reader.setOffset(fStart + size); + } + + if (it.isValid()) { + uint32_t skipTo = it.nextDraw(); + if (kDrawComplete == skipTo) { + return; + } + reader.setOffset(skipTo); + } + + // Record this, so we can concat w/ it if we encounter a setMatrix() + SkMatrix initialMatrix = canvas->getTotalMatrix(); + + SkAutoCanvasRestore acr(canvas, false); + +#ifdef SK_DEVELOPER + int opIndex = -1; +#endif + + while (!reader.eof()) { + if (callback && callback->abortDrawing()) { + return; + } + + if (0 != fStart || 0 != fStop) { + size_t offset = reader.offset(); + if (offset >= fStop) { + uint32_t size; + SkDEBUGCODE(DrawType op = ) read_op_and_size(&reader, &size); + SkASSERT(RESTORE == op); + return; + } + } + + if (NULL != fReplacements) { + // Potentially replace a block of operations with a single drawBitmap call + SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp = + fReplacements->lookupByStart(reader.offset()); + if (NULL != temp) { + SkASSERT(NULL != temp->fBM); + SkASSERT(NULL != temp->fPaint); + canvas->save(); + canvas->setMatrix(initialMatrix); + SkRect src = SkRect::Make(temp->fSrcRect); + SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY, + temp->fSrcRect.width(), + temp->fSrcRect.height()); + canvas->drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint); + canvas->restore(); + + if (it.isValid()) { + // This save is needed since the BBH will automatically issue + // a restore to balanced the saveLayer we're skipping + canvas->save(); + + // At this point we know that the PictureStateTree was aiming + // for some draw op within temp's saveLayer (although potentially + // in a separate saveLayer nested inside it). + // We need to skip all the operations inside temp's range + // along with all the associated state changes but update + // the state tree to the first operation outside temp's range. + + uint32_t skipTo; + do { + skipTo = it.nextDraw(); + if (kDrawComplete == skipTo) { + break; + } + + if (skipTo <= temp->fStop) { + reader.setOffset(skipTo); + uint32_t size; + DrawType op = read_op_and_size(&reader, &size); + // Since we are relying on the normal SkPictureStateTree + // playback we need to convert any nested saveLayer calls + // it may issue into saves (so that all its internal + // restores will be balanced). + if (SAVE_LAYER == op) { + canvas->save(); + } + } + } while (skipTo <= temp->fStop); + + if (kDrawComplete == skipTo) { + break; + } + + reader.setOffset(skipTo); + } else { + reader.setOffset(temp->fStop); + uint32_t size; + SkDEBUGCODE(DrawType op = ) read_op_and_size(&reader, &size); + SkASSERT(RESTORE == op); + } + continue; + } + } + +#ifdef SPEW_CLIP_SKIPPING + opCount++; +#endif + + fCurOffset = reader.offset(); + uint32_t size; + DrawType op = read_op_and_size(&reader, &size); + size_t skipTo = 0; + if (NOOP == op) { + // NOOPs are to be ignored - do not propagate them any further + skipTo = fCurOffset + size; +#ifdef SK_DEVELOPER + } else { + opIndex++; + if (this->preDraw(opIndex, op)) { + skipTo = fCurOffset + size; + } +#endif + } + + if (0 != skipTo) { + if (it.isValid()) { + // If using a bounding box hierarchy, advance the state tree + // iterator until at or after skipTo + uint32_t adjustedSkipTo; + do { + adjustedSkipTo = it.nextDraw(); + } while (adjustedSkipTo < skipTo); + skipTo = adjustedSkipTo; + } + if (kDrawComplete == skipTo) { + break; + } + reader.setOffset(skipTo); + continue; + } + + switch (op) { + case CLIP_PATH: { + const SkPath& path = fPictureData->getPath(reader); + uint32_t packed = reader.readInt(); + SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); + bool doAA = ClipParams_unpackDoAA(packed); + size_t offsetToRestore = reader.readInt(); + SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); + canvas->clipPath(path, regionOp, doAA); + if (canvas->isClipEmpty() && offsetToRestore) { +#ifdef SPEW_CLIP_SKIPPING + skipPath.recordSkip(offsetToRestore - reader.offset()); +#endif + reader.setOffset(offsetToRestore); + } + } break; + case CLIP_REGION: { + SkRegion region; + reader.readRegion(®ion); + uint32_t packed = reader.readInt(); + SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); + size_t offsetToRestore = reader.readInt(); + SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); + canvas->clipRegion(region, regionOp); + if (canvas->isClipEmpty() && offsetToRestore) { +#ifdef SPEW_CLIP_SKIPPING + skipRegion.recordSkip(offsetToRestore - reader.offset()); +#endif + reader.setOffset(offsetToRestore); + } + } break; + case CLIP_RECT: { + const SkRect& rect = reader.skipT(); + uint32_t packed = reader.readInt(); + SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); + bool doAA = ClipParams_unpackDoAA(packed); + size_t offsetToRestore = reader.readInt(); + SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); + canvas->clipRect(rect, regionOp, doAA); + if (canvas->isClipEmpty() && offsetToRestore) { +#ifdef SPEW_CLIP_SKIPPING + skipRect.recordSkip(offsetToRestore - reader.offset()); +#endif + reader.setOffset(offsetToRestore); + } + } break; + case CLIP_RRECT: { + SkRRect rrect; + reader.readRRect(&rrect); + uint32_t packed = reader.readInt(); + SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); + bool doAA = ClipParams_unpackDoAA(packed); + size_t offsetToRestore = reader.readInt(); + SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); + canvas->clipRRect(rrect, regionOp, doAA); + if (canvas->isClipEmpty() && offsetToRestore) { +#ifdef SPEW_CLIP_SKIPPING + skipRRect.recordSkip(offsetToRestore - reader.offset()); +#endif + reader.setOffset(offsetToRestore); + } + } break; + case PUSH_CULL: { + const SkRect& cullRect = reader.skipT(); + size_t offsetToRestore = reader.readInt(); + if (offsetToRestore && canvas->quickReject(cullRect)) { +#ifdef SPEW_CLIP_SKIPPING + skipCull.recordSkip(offsetToRestore - reader.offset()); +#endif + reader.setOffset(offsetToRestore); + } else { + canvas->pushCull(cullRect); + } + } break; + case POP_CULL: + canvas->popCull(); + break; + case CONCAT: { + SkMatrix matrix; + reader.readMatrix(&matrix); + canvas->concat(matrix); + break; + } + case DRAW_BITMAP: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap& bitmap = fPictureData->getBitmap(reader); + const SkPoint& loc = reader.skipT(); + canvas->drawBitmap(bitmap, loc.fX, loc.fY, paint); + } break; + case DRAW_BITMAP_RECT_TO_RECT: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap& bitmap = fPictureData->getBitmap(reader); + const SkRect* src = get_rect_ptr(reader); // may be null + const SkRect& dst = reader.skipT(); // required + SkCanvas::DrawBitmapRectFlags flags; + flags = (SkCanvas::DrawBitmapRectFlags) reader.readInt(); + canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags); + } break; + case DRAW_BITMAP_MATRIX: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap& bitmap = fPictureData->getBitmap(reader); + SkMatrix matrix; + reader.readMatrix(&matrix); + canvas->drawBitmapMatrix(bitmap, matrix, paint); + } break; + case DRAW_BITMAP_NINE: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap& bitmap = fPictureData->getBitmap(reader); + const SkIRect& src = reader.skipT(); + const SkRect& dst = reader.skipT(); + canvas->drawBitmapNine(bitmap, src, dst, paint); + } break; + case DRAW_CLEAR: + canvas->clear(reader.readInt()); + break; + case DRAW_DATA: { + size_t length = reader.readInt(); + canvas->drawData(reader.skip(length), length); + // skip handles padding the read out to a multiple of 4 + } break; + case DRAW_DRRECT: { + const SkPaint& paint = *fPictureData->getPaint(reader); + SkRRect outer, inner; + reader.readRRect(&outer); + reader.readRRect(&inner); + canvas->drawDRRect(outer, inner, paint); + } break; + case BEGIN_COMMENT_GROUP: { + const char* desc = reader.readString(); + canvas->beginCommentGroup(desc); + } break; + case COMMENT: { + const char* kywd = reader.readString(); + const char* value = reader.readString(); + canvas->addComment(kywd, value); + } break; + case END_COMMENT_GROUP: { + canvas->endCommentGroup(); + } break; + case DRAW_OVAL: { + const SkPaint& paint = *fPictureData->getPaint(reader); + canvas->drawOval(reader.skipT(), paint); + } break; + case DRAW_PAINT: + canvas->drawPaint(*fPictureData->getPaint(reader)); + break; + case DRAW_PATH: { + const SkPaint& paint = *fPictureData->getPaint(reader); + canvas->drawPath(fPictureData->getPath(reader), paint); + } break; + case DRAW_PICTURE: + canvas->drawPicture(fPictureData->getPicture(reader)); + break; + case DRAW_POINTS: { + const SkPaint& paint = *fPictureData->getPaint(reader); + SkCanvas::PointMode mode = (SkCanvas::PointMode)reader.readInt(); + size_t count = reader.readInt(); + const SkPoint* pts = (const SkPoint*)reader.skip(sizeof(SkPoint)* count); + canvas->drawPoints(mode, count, pts, paint); + } break; + case DRAW_POS_TEXT: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + size_t points = reader.readInt(); + const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint)); + canvas->drawPosText(text.text(), text.length(), pos, paint); + } break; + case DRAW_POS_TEXT_TOP_BOTTOM: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + size_t points = reader.readInt(); + const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint)); + const SkScalar top = reader.readScalar(); + const SkScalar bottom = reader.readScalar(); + if (!canvas->quickRejectY(top, bottom)) { + canvas->drawPosText(text.text(), text.length(), pos, paint); + } + } break; + case DRAW_POS_TEXT_H: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + size_t xCount = reader.readInt(); + const SkScalar constY = reader.readScalar(); + const SkScalar* xpos = (const SkScalar*)reader.skip(xCount * sizeof(SkScalar)); + canvas->drawPosTextH(text.text(), text.length(), xpos, constY, paint); + } break; + case DRAW_POS_TEXT_H_TOP_BOTTOM: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + size_t xCount = reader.readInt(); + const SkScalar* xpos = (const SkScalar*)reader.skip((3 + xCount) * sizeof(SkScalar)); + const SkScalar top = *xpos++; + const SkScalar bottom = *xpos++; + const SkScalar constY = *xpos++; + if (!canvas->quickRejectY(top, bottom)) { + canvas->drawPosTextH(text.text(), text.length(), xpos, constY, paint); + } + } break; + case DRAW_RECT: { + const SkPaint& paint = *fPictureData->getPaint(reader); + canvas->drawRect(reader.skipT(), paint); + } break; + case DRAW_RRECT: { + const SkPaint& paint = *fPictureData->getPaint(reader); + SkRRect rrect; + reader.readRRect(&rrect); + canvas->drawRRect(rrect, paint); + } break; + case DRAW_SPRITE: { + const SkPaint* paint = fPictureData->getPaint(reader); + const SkBitmap& bitmap = fPictureData->getBitmap(reader); + int left = reader.readInt(); + int top = reader.readInt(); + canvas->drawSprite(bitmap, left, top, paint); + } break; + case DRAW_TEXT: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + SkScalar x = reader.readScalar(); + SkScalar y = reader.readScalar(); + canvas->drawText(text.text(), text.length(), x, y, paint); + } break; + case DRAW_TEXT_TOP_BOTTOM: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + const SkScalar* ptr = (const SkScalar*)reader.skip(4 * sizeof(SkScalar)); + // ptr[0] == x + // ptr[1] == y + // ptr[2] == top + // ptr[3] == bottom + if (!canvas->quickRejectY(ptr[2], ptr[3])) { + canvas->drawText(text.text(), text.length(), ptr[0], ptr[1], paint); + } + } break; + case DRAW_TEXT_ON_PATH: { + const SkPaint& paint = *fPictureData->getPaint(reader); + get_text(&reader, &text); + const SkPath& path = fPictureData->getPath(reader); + SkMatrix matrix; + reader.readMatrix(&matrix); + canvas->drawTextOnPath(text.text(), text.length(), path, &matrix, paint); + } break; + case DRAW_VERTICES: { + SkAutoTUnref xfer; + const SkPaint& paint = *fPictureData->getPaint(reader); + DrawVertexFlags flags = (DrawVertexFlags)reader.readInt(); + SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader.readInt(); + int vCount = reader.readInt(); + const SkPoint* verts = (const SkPoint*)reader.skip(vCount * sizeof(SkPoint)); + const SkPoint* texs = NULL; + const SkColor* colors = NULL; + const uint16_t* indices = NULL; + int iCount = 0; + if (flags & DRAW_VERTICES_HAS_TEXS) { + texs = (const SkPoint*)reader.skip(vCount * sizeof(SkPoint)); + } + if (flags & DRAW_VERTICES_HAS_COLORS) { + colors = (const SkColor*)reader.skip(vCount * sizeof(SkColor)); + } + if (flags & DRAW_VERTICES_HAS_INDICES) { + iCount = reader.readInt(); + indices = (const uint16_t*)reader.skip(iCount * sizeof(uint16_t)); + } + if (flags & DRAW_VERTICES_HAS_XFER) { + int mode = reader.readInt(); + if (mode < 0 || mode > SkXfermode::kLastMode) { + mode = SkXfermode::kModulate_Mode; + } + xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode)); + } + canvas->drawVertices(vmode, vCount, verts, texs, colors, xfer, indices, iCount, paint); + } break; + case RESTORE: + canvas->restore(); + break; + case ROTATE: + canvas->rotate(reader.readScalar()); + break; + case SAVE: + // SKPs with version < 29 also store a SaveFlags param. + if (size > 4) { + SkASSERT(8 == size); + reader.readInt(); + } + canvas->save(); + break; + case SAVE_LAYER: { + const SkRect* boundsPtr = get_rect_ptr(reader); + const SkPaint* paint = fPictureData->getPaint(reader); + canvas->saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader.readInt()); + } break; + case SCALE: { + SkScalar sx = reader.readScalar(); + SkScalar sy = reader.readScalar(); + canvas->scale(sx, sy); + } break; + case SET_MATRIX: { + SkMatrix matrix; + reader.readMatrix(&matrix); + matrix.postConcat(initialMatrix); + canvas->setMatrix(matrix); + } break; + case SKEW: { + SkScalar sx = reader.readScalar(); + SkScalar sy = reader.readScalar(); + canvas->skew(sx, sy); + } break; + case TRANSLATE: { + SkScalar dx = reader.readScalar(); + SkScalar dy = reader.readScalar(); + canvas->translate(dx, dy); + } break; + default: + SkASSERT(0); + } + +#ifdef SK_DEVELOPER + this->postDraw(opIndex); +#endif + + if (it.isValid()) { + uint32_t skipTo = it.nextDraw(); + if (kDrawComplete == skipTo) { + break; + } + reader.setOffset(skipTo); + } + } + +#ifdef SPEW_CLIP_SKIPPING + { + size_t size = skipRect.fSize + skipRRect.fSize + skipPath.fSize + skipRegion.fSize + + skipCull.fSize; + SkDebugf("--- Clip skips %d%% rect:%d rrect:%d path:%d rgn:%d cull:%d\n", + size * 100 / reader.offset(), skipRect.fCount, skipRRect.fCount, + skipPath.fCount, skipRegion.fCount, skipCull.fCount); + SkDebugf("--- Total ops: %d\n", opCount); + } +#endif + // this->dumpSize(); +} + diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h new file mode 100644 index 0000000000..251ad3a856 --- /dev/null +++ b/src/core/SkPicturePlayback.h @@ -0,0 +1,118 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkPicturePlayback_DEFINED +#define SkPicturePlayback_DEFINED + +#include "SkTypes.h" + +class SkBitmap; +class SkCanvas; +class SkDrawPictureCallback; +class SkPaint; +class SkPictureData; + +class SkPicturePlayback : SkNoncopyable { +public: + SkPicturePlayback(const SkPicture* picture) + : fPictureData(picture->fData.get()) + , fCurOffset(0) + , fUseBBH(true) + , fStart(0) + , fStop(0) + , fReplacements(NULL) { + } + virtual ~SkPicturePlayback() { } + + void draw(SkCanvas* canvas, SkDrawPictureCallback*); + + // Return the ID of the operation currently being executed when playing + // back. 0 indicates no call is active. + size_t curOpID() const { return fCurOffset; } + void resetOpID() { fCurOffset = 0; } + + void setUseBBH(bool useBBH) { fUseBBH = useBBH; } + + // Limit the opcode playback to be between the offsets 'start' and 'stop'. + // The opcode at 'start' should be a saveLayer while the opcode at + // 'stop' should be a restore. Neither of those commands will be issued. + // Set both start & stop to 0 to disable draw limiting + // Draw limiting cannot be enabled at the same time as draw replacing + void setDrawLimits(size_t start, size_t stop) { + SkASSERT(NULL == fReplacements); + fStart = start; + fStop = stop; + } + + // PlaybackReplacements collects op ranges that can be replaced with + // a single drawBitmap call (using a precomputed bitmap). + class PlaybackReplacements { + public: + // All the operations between fStart and fStop (inclusive) will be replaced with + // a single drawBitmap call using fPos, fBM and fPaint. + // fPaint will be NULL if the picture's paint wasn't copyable + struct ReplacementInfo { + size_t fStart; + size_t fStop; + SkIPoint fPos; + SkBitmap* fBM; // fBM is allocated so ReplacementInfo can remain POD + const SkPaint* fPaint; // Note: this object doesn't own the paint + + SkIRect fSrcRect; + }; + + ~PlaybackReplacements() { this->freeAll(); } + + // Add a new replacement range. The replacement ranges should be + // sorted in increasing order and non-overlapping (esp. no nested + // saveLayers). + ReplacementInfo* push(); + + private: + friend class SkPicturePlayback; // for access to lookupByStart + + // look up a replacement range by its start offset + ReplacementInfo* lookupByStart(size_t start); + + void freeAll(); + +#ifdef SK_DEBUG + void validate() const; +#endif + + SkTDArray fReplacements; + }; + + // Replace all the draw ops in the replacement ranges in 'replacements' with + // the associated drawBitmap call + // Draw replacing cannot be enabled at the same time as draw limiting + void setReplacements(PlaybackReplacements* replacements) { + SkASSERT(fStart == 0 && fStop == 0); + fReplacements = replacements; + } + +protected: + const SkPictureData* fPictureData; + + // The offset of the current operation when within the draw method + size_t fCurOffset; + + bool fUseBBH; + size_t fStart; + size_t fStop; + PlaybackReplacements* fReplacements; + +#ifdef SK_DEVELOPER + virtual bool preDraw(int opIndex, int type) { return false; } + virtual void postDraw(int opIndex) { } +#endif + +private: + typedef SkNoncopyable INHERITED; +}; + +#endif diff --git a/src/gpu/GrPictureUtils.cpp b/src/gpu/GrPictureUtils.cpp index 4da9e3ceab..0bcd927e57 100644 --- a/src/gpu/GrPictureUtils.cpp +++ b/src/gpu/GrPictureUtils.cpp @@ -10,6 +10,7 @@ #include "SkDraw.h" #include "SkPaintPriv.h" #include "SkPictureData.h" +#include "SkPicturePlayback.h" SkPicture::AccelData::Key GPUAccelData::ComputeAccelDataKey() { static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain(); @@ -29,14 +30,14 @@ class GrGatherDevice : public SkBaseDevice { public: SK_DECLARE_INST_COUNT(GrGatherDevice) - GrGatherDevice(int width, int height, const SkPicture* picture, GPUAccelData* accelData, + GrGatherDevice(int width, int height, SkPicturePlayback* playback, GPUAccelData* accelData, int saveLayerDepth) { - fPicture = picture; + fPlayback = playback; fSaveLayerDepth = saveLayerDepth; fInfo.fValid = true; fInfo.fSize.set(width, height); fInfo.fPaint = NULL; - fInfo.fSaveLayerOpID = fPicture->EXPERIMENTAL_curOpID(); + fInfo.fSaveLayerOpID = fPlayback->curOpID(); fInfo.fRestoreOpID = 0; fInfo.fHasNestedLayers = false; fInfo.fIsNested = (2 == fSaveLayerDepth); @@ -123,7 +124,7 @@ protected: return; } - device->fInfo.fRestoreOpID = fPicture->EXPERIMENTAL_curOpID(); + device->fInfo.fRestoreOpID = fPlayback->curOpID(); device->fInfo.fCTM = *draw.fMatrix; device->fInfo.fCTM.postTranslate(SkIntToScalar(-device->getOrigin().fX), SkIntToScalar(-device->getOrigin().fY)); @@ -163,8 +164,8 @@ protected: } private: - // The picture being processed - const SkPicture *fPicture; + // The playback object driving this rendering + SkPicturePlayback *fPlayback; SkBitmap fEmptyBitmap; // legacy -- need to remove @@ -190,7 +191,7 @@ private: SkASSERT(kSaveLayer_Usage == usage); fInfo.fHasNestedLayers = true; - return SkNEW_ARGS(GrGatherDevice, (info.width(), info.height(), fPicture, + return SkNEW_ARGS(GrGatherDevice, (info.width(), info.height(), fPlayback, fAccelData, fSaveLayerDepth+1)); } @@ -215,21 +216,7 @@ private: // which is all just to fill in 'accelData' class SK_API GrGatherCanvas : public SkCanvas { public: - GrGatherCanvas(GrGatherDevice* device, const SkPicture* pict) - : INHERITED(device) - , fPicture(pict) { - } - - void gather() { - if (NULL == fPicture || 0 == fPicture->width() || 0 == fPicture->height()) { - return; - } - - this->clipRect(SkRect::MakeWH(SkIntToScalar(fPicture->width()), - SkIntToScalar(fPicture->height())), - SkRegion::kIntersect_Op, false); - this->drawPicture(fPicture); - } + GrGatherCanvas(GrGatherDevice* device) : INHERITED(device) {} protected: // disable aa for speed @@ -248,32 +235,41 @@ protected: } virtual void onDrawPicture(const SkPicture* picture) SK_OVERRIDE { - // BBH-based rendering doesn't re-issue many of the operations the gather - // process cares about (e.g., saves and restores) so it must be disabled. if (NULL != picture->fData.get()) { - picture->fData->setUseBBH(false); - } - picture->draw(this); - if (NULL != picture->fData.get()) { - picture->fData->setUseBBH(true); + // Disable the BBH for the old path so all the draw calls + // will be seen. The stock SkPicture::draw method can't be + // invoked since it just uses a vanilla SkPicturePlayback. + SkPicturePlayback playback(picture); + playback.setUseBBH(false); + playback.draw(this, NULL); + } else { + // Since we know this is the SkRecord path we can just call + // SkPicture::draw. + picture->draw(this); } } private: - const SkPicture* fPicture; - typedef SkCanvas INHERITED; }; // GatherGPUInfo is only intended to be called within the context of SkGpuDevice's // EXPERIMENTAL_optimize method. void GatherGPUInfo(const SkPicture* pict, GPUAccelData* accelData) { - if (0 == pict->width() || 0 == pict->height()) { + if (NULL == pict || 0 == pict->width() || 0 == pict->height()) { return ; } - GrGatherDevice device(pict->width(), pict->height(), pict, accelData, 0); - GrGatherCanvas canvas(&device, pict); + // BBH-based rendering doesn't re-issue many of the operations the gather + // process cares about (e.g., saves and restores) so it must be disabled. + SkPicturePlayback playback(pict); + playback.setUseBBH(false); - canvas.gather(); + GrGatherDevice device(pict->width(), pict->height(), &playback, accelData, 0); + GrGatherCanvas canvas(&device); + + canvas.clipRect(SkRect::MakeWH(SkIntToScalar(pict->width()), + SkIntToScalar(pict->height())), + SkRegion::kIntersect_Op, false); + playback.draw(&canvas, NULL); } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 9041bdfba2..cf152fa6e5 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -30,6 +30,7 @@ #include "SkPathEffect.h" #include "SkPicture.h" #include "SkPictureData.h" +#include "SkPicturePlayback.h" #include "SkRRect.h" #include "SkStroke.h" #include "SkSurface.h" @@ -1859,7 +1860,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi SkIRect query; clipBounds.roundOut(&query); - const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(query); + SkAutoTDelete ops(picture->EXPERIMENTAL_getActiveOps(query)); // This code pre-renders the entire layer since it will be cached and potentially // reused with different clips (e.g., in different tiles). Because of this the @@ -1867,12 +1868,12 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi // is used to limit which clips are pre-rendered. static const int kSaveLayerMaxSize = 256; - if (ops.valid()) { + if (NULL != ops.get()) { // In this case the picture has been generated with a BBH so we use // the BBH to limit the pre-rendering to just the layers needed to cover // the region being drawn - for (int i = 0; i < ops.numOps(); ++i) { - uint32_t offset = ops.offset(i); + for (int i = 0; i < ops->numOps(); ++i) { + uint32_t offset = ops->offset(i); // For now we're saving all the layers in the GPUAccelData so they // can be nested. Additionally, the nested layers appear before @@ -1928,7 +1929,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi } } - SkPictureData::PlaybackReplacements replacements; + SkPicturePlayback::PlaybackReplacements replacements; // Generate the layer and/or ensure it is locked for (int i = 0; i < gpuData->numSaveLayers(); ++i) { @@ -1937,7 +1938,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); - SkPictureData::PlaybackReplacements::ReplacementInfo* layerInfo = + SkPicturePlayback::PlaybackReplacements::ReplacementInfo* layerInfo = replacements.push(); layerInfo->fStart = info.fSaveLayerOpID; layerInfo->fStop = info.fRestoreOpID; @@ -2009,9 +2010,9 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi SkIntToScalar(layer->rect().fTop)); } - picture->fData->setDrawLimits(info.fSaveLayerOpID, info.fRestoreOpID); - picture->fData->draw(*canvas, NULL); - picture->fData->setDrawLimits(0, 0); + SkPicturePlayback playback(picture); + playback.setDrawLimits(info.fSaveLayerOpID, info.fRestoreOpID); + playback.draw(canvas, NULL); canvas->flush(); } @@ -2019,9 +2020,10 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi } // Playback using new layers - picture->fData->setReplacements(&replacements); - picture->fData->draw(*canvas, NULL); - picture->fData->setReplacements(NULL); + SkPicturePlayback playback(picture); + + playback.setReplacements(&replacements); + playback.draw(canvas, NULL); // unlock the layers for (int i = 0; i < gpuData->numSaveLayers(); ++i) { diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp index 702a78dda2..85d523f46c 100644 --- a/src/utils/SkPictureUtils.cpp +++ b/src/utils/SkPictureUtils.cpp @@ -190,7 +190,7 @@ private: typedef SkBaseDevice INHERITED; }; -SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) { +SkData* SkPictureUtils::GatherPixelRefs(const SkPicture* pict, const SkRect& area) { if (NULL == pict) { return NULL; } diff --git a/src/utils/debugger/SkDebugCanvas.h b/src/utils/debugger/SkDebugCanvas.h index a3902d2a4e..50a9152239 100644 --- a/src/utils/debugger/SkDebugCanvas.h +++ b/src/utils/debugger/SkDebugCanvas.h @@ -314,9 +314,11 @@ private: void applyUserTransform(SkCanvas* canvas); size_t getOpID() const { +#if 0 if (NULL != fPicture) { return fPicture->EXPERIMENTAL_curOpID(); } +#endif return 0; } diff --git a/tools/CopyTilesRenderer.cpp b/tools/CopyTilesRenderer.cpp index b5534f03a7..7e6f8a8226 100644 --- a/tools/CopyTilesRenderer.cpp +++ b/tools/CopyTilesRenderer.cpp @@ -20,7 +20,7 @@ namespace sk_tools { : fXTilesPerLargeTile(x) , fYTilesPerLargeTile(y) { } - void CopyTilesRenderer::init(SkPicture* pict, const SkString* writePath, + void CopyTilesRenderer::init(const SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, const SkString* inputFilename, bool useChecksumBasedFilenames) { // Do not call INHERITED::init(), which would create a (potentially large) canvas which is diff --git a/tools/CopyTilesRenderer.h b/tools/CopyTilesRenderer.h index 3bf969b15f..d7cf242fff 100644 --- a/tools/CopyTilesRenderer.h +++ b/tools/CopyTilesRenderer.h @@ -23,7 +23,9 @@ namespace sk_tools { public: CopyTilesRenderer(int x, int y); - virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, + virtual void init(const SkPicture* pict, + const SkString* writePath, + const SkString* mismatchPath, const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index acc7878230..3793b85461 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -48,8 +48,11 @@ enum { kDefaultTileHeight = 256 }; -void PictureRenderer::init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, - const SkString* inputFilename, bool useChecksumBasedFilenames) { +void PictureRenderer::init(const SkPicture* pict, + const SkString* writePath, + const SkString* mismatchPath, + const SkString* inputFilename, + bool useChecksumBasedFilenames) { this->CopyString(&fWritePath, writePath); this->CopyString(&fMismatchPath, mismatchPath); this->CopyString(&fInputFilename, inputFilename); @@ -406,7 +409,7 @@ SkString PipePictureRenderer::getConfigNameInternal() { /////////////////////////////////////////////////////////////////////////////////////////////// -void SimplePictureRenderer::init(SkPicture* picture, const SkString* writePath, +void SimplePictureRenderer::init(const SkPicture* picture, const SkString* writePath, const SkString* mismatchPath, const SkString* inputFilename, bool useChecksumBasedFilenames) { INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames); @@ -451,7 +454,7 @@ TiledPictureRenderer::TiledPictureRenderer() , fTilesX(0) , fTilesY(0) { } -void TiledPictureRenderer::init(SkPicture* pict, const SkString* writePath, +void TiledPictureRenderer::init(const SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, const SkString* inputFilename, bool useChecksumBasedFilenames) { SkASSERT(NULL != pict); @@ -579,7 +582,9 @@ void TiledPictureRenderer::setupPowerOf2Tiles() { * Saves and restores so that the initial clip and matrix return to their state before this function * is called. */ -static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) { +static void draw_tile_to_canvas(SkCanvas* canvas, + const SkRect& tileRect, + const SkPicture* picture) { int saveCount = canvas->save(); // Translate so that we draw the correct portion of the picture. // Perform a postTranslate so that the scaleFactor does not interfere with the positioning. diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index 12ed32e070..d197419080 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -90,8 +90,11 @@ public: * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing * bitmap images to disk. */ - virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, - const SkString* inputFilename, bool useChecksumBasedFilenames); + virtual void init(const SkPicture* pict, + const SkString* writePath, + const SkString* mismatchPath, + const SkString* inputFilename, + bool useChecksumBasedFilenames); /** * TODO(epoger): Temporary hack, while we work on http://skbug.com/2584 ('bench_pictures is @@ -406,7 +409,7 @@ public: return fCanvas; } - SkPicture* getPicture() { + const SkPicture* getPicture() { return fPicture; } @@ -436,7 +439,7 @@ public: protected: SkAutoTUnref fCanvas; - SkAutoTUnref fPicture; + SkAutoTUnref fPicture; bool fUseChecksumBasedFilenames; ImageResultsAndExpectations* fJsonSummaryPtr; SkDeviceTypes fDeviceType; @@ -522,8 +525,11 @@ private: class SimplePictureRenderer : public PictureRenderer { public: - virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, - const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; + virtual void init(const SkPicture* pict, + const SkString* writePath, + const SkString* mismatchPath, + const SkString* inputFilename, + bool useChecksumBasedFilenames) SK_OVERRIDE; virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE; @@ -537,8 +543,11 @@ class TiledPictureRenderer : public PictureRenderer { public: TiledPictureRenderer(); - virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath, - const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE; + virtual void init(const SkPicture* pict, + const SkString* writePath, + const SkString* mismatchPath, + const SkString* inputFilename, + bool useChecksumBasedFilenames) SK_OVERRIDE; /** * Renders to tiles, rather than a single canvas.