Index: src/core/SkPicturePlayback.cpp |
=================================================================== |
--- src/core/SkPicturePlayback.cpp (revision 14614) |
+++ src/core/SkPicturePlayback.cpp (working copy) |
@@ -23,6 +23,33 @@ |
*/ |
#define SPEW_CLIP_SKIPPINGx |
+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 |
+ |
SkPicturePlayback::SkPicturePlayback(const SkPicture* picture, const SkPictInfo& info) |
: fPicture(picture) |
, fInfo(info) { |
@@ -205,6 +232,10 @@ |
fStateTree = NULL; |
fCachedActiveOps = NULL; |
fCurOffset = 0; |
+ fUseBBH = true; |
+ fStart = 0; |
+ fStop = 0; |
+ fReplacements = NULL; |
} |
SkPicturePlayback::~SkPicturePlayback() { |
@@ -744,6 +775,21 @@ |
SkPicturePlayback* fPlayback; |
}; |
+// 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; |
+} |
+ |
void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) { |
SkAutoResetOpID aroi(this); |
SkASSERT(0 == fCurOffset); |
@@ -769,20 +815,24 @@ |
TextContainer text; |
const SkTDArray<void*>* activeOps = NULL; |
- if (NULL != fStateTree && NULL != fBoundingHierarchy) { |
- SkRect clipBounds; |
- if (canvas.getClipBounds(&clipBounds)) { |
- SkIRect query; |
- clipBounds.roundOut(&query); |
+ // 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 |
+ 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; |
} |
- |
- // Since the opList is valid we know it is our derived class |
- activeOps = &((const CachedOperationList&)activeOpsList).fOps; |
} |
} |
} |
@@ -791,6 +841,14 @@ |
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) { |
@@ -821,7 +879,61 @@ |
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 |
+ SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp = |
+ fReplacements->lookupByStart(reader.offset()); |
+ if (NULL != temp) { |
+ SkASSERT(NULL != temp->fBM); |
+ SkASSERT(NULL != temp->fPaint); |
+ canvas.drawBitmap(*temp->fBM, temp->fPos.fX, temp->fPos.fY, temp->fPaint); |
+ |
+ if (it.isValid()) { |
+ // This save is needed since the BBH will automatically issue |
+ // a restore to balanced the saveLayer we're skipping |
+ canvas.save(); |
+ // Note: This skipping only works if the client only issues |
+ // well behaved saveLayer calls (i.e., doesn't use |
+ // kMatrix_SaveFlag or kClip_SaveFlag in isolation) |
+ |
+ // 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. |
+ SkASSERT(it.peekDraw() >= temp->fStart && it.peekDraw() <= temp->fStop); |
+ |
+ while (kDrawComplete != it.peekDraw() && it.peekDraw() <= temp->fStop) { |
+ it.skipDraw(); |
+ } |
+ |
+ if (kDrawComplete == it.peekDraw()) { |
+ break; |
+ } |
+ |
+ uint32_t skipTo = it.nextDraw(); |
+ 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 |
@@ -915,8 +1027,7 @@ |
SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed); |
bool doAA = ClipParams_unpackDoAA(packed); |
size_t offsetToRestore = reader.readInt(); |
- SkASSERT(!offsetToRestore || \ |
- offsetToRestore >= reader.offset()); |
+ SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset()); |
canvas.clipRRect(rrect, regionOp, doAA); |
if (canvas.isClipEmpty() && offsetToRestore) { |
#ifdef SPEW_CLIP_SKIPPING |