Refactor SkPicturePlayback for SkPictureReplacementPlayback

This CL is intended to maximize code reuse when SkPictureReplacementPlayback is split off.

R=mtklein@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/378343002
This commit is contained in:
robertphillips 2014-07-09 13:00:07 -07:00 committed by Commit bot
parent 762c718824
commit ec66e6264d
4 changed files with 178 additions and 131 deletions

View File

@ -106,17 +106,7 @@ static SkBitmap shallow_copy(const SkBitmap& bitmap) {
return bitmap;
}
void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
AutoResetOpID aroi(this);
SkASSERT(0 == fCurOffset);
// 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());
SkAutoTDelete<const SkPicture::OperationList> activeOpsList;
const SkTDArray<void*>* activeOps = NULL;
const SkPicture::OperationList* SkPicturePlayback::getActiveOps(const SkCanvas* canvas) {
if (fUseBBH && NULL != fPictureData->fStateTree && NULL != fPictureData->fBoundingHierarchy) {
SkRect clipBounds;
@ -124,28 +114,151 @@ void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback)
SkIRect query;
clipBounds.roundOut(&query);
activeOpsList.reset(fPictureData->getActiveOps(query));
if (NULL != activeOpsList.get()) {
if (0 == activeOpsList->numOps()) {
return; // nothing to draw
return fPictureData->getActiveOps(query);
}
}
return NULL;
}
// Initialize the state tree iterator. Return false if there is nothing left to draw.
bool SkPicturePlayback::initIterator(SkPictureStateTree::Iterator* iter,
SkCanvas* canvas,
const SkPicture::OperationList *activeOpsList) {
if (NULL != activeOpsList) {
if (0 == activeOpsList->numOps()) {
return false; // nothing to draw
}
fPictureData->fStateTree->initIterator(iter, activeOpsList->fOps, canvas);
}
return true;
}
bool SkPicturePlayback::replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix) {
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 (iter->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 = iter->nextDraw();
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
break;
}
if (skipTo <= temp->fStop) {
reader->setOffset(skipTo);
uint32_t size;
DrawType op = ReadOpAndSize(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 (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
reader->setOffset(reader->size()); // skip to end
return true;
}
activeOps = &(activeOpsList.get()->fOps);
reader->setOffset(skipTo);
} else {
reader->setOffset(temp->fStop);
uint32_t size;
SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
SkASSERT(RESTORE == op);
}
return true;
}
}
SkPictureStateTree::Iterator it = (NULL == activeOps) ?
SkPictureStateTree::Iterator() :
fPictureData->fStateTree->getIterator(*activeOps, canvas);
return false;
}
if (it.isValid()) {
uint32_t skipTo = it.nextDraw();
if (kDrawComplete == skipTo) {
return;
// If 'iter' is valid use it to skip forward through the picture.
void SkPicturePlayback::StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader) {
if (iter->isValid()) {
uint32_t skipTo = iter->nextDraw();
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
reader->setOffset(reader->size()); // skip to end
} else {
reader->setOffset(skipTo);
}
reader.setOffset(skipTo);
}
}
// Update the iterator and state tree to catch up with the skipped ops.
void SkPicturePlayback::SkipIterTo(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
uint32_t skipTo) {
SkASSERT(skipTo <= reader->size());
SkASSERT(reader->offset() <= skipTo); // should only be skipping forward
if (iter->isValid()) {
// If using a bounding box hierarchy, advance the state tree
// iterator until at or after skipTo
uint32_t adjustedSkipTo;
do {
adjustedSkipTo = iter->nextDraw();
} while (adjustedSkipTo < skipTo);
skipTo = adjustedSkipTo;
}
if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
reader->setOffset(reader->size()); // skip to end
} else {
reader->setOffset(skipTo);
}
}
void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
AutoResetOpID aroi(this);
SkASSERT(0 == fCurOffset);
SkAutoTDelete<const SkPicture::OperationList> activeOpsList(this->getActiveOps(canvas));
SkPictureStateTree::Iterator it;
if (!this->initIterator(&it, canvas, activeOpsList.get())) {
return; // nothing to draw
}
SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size());
StepIterator(&it, &reader);
// Record this, so we can concat w/ it if we encounter a setMatrix()
SkMatrix initialMatrix = canvas->getTotalMatrix();
@ -153,109 +266,26 @@ void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback)
SkAutoCanvasRestore acr(canvas, false);
while (!reader.eof()) {
if (callback && callback->abortDrawing()) {
if (NULL != callback && callback->abortDrawing()) {
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 = ReadOpAndSize(&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 = ) ReadOpAndSize(&reader, &size);
SkASSERT(RESTORE == op);
}
continue;
}
if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
continue;
}
fCurOffset = reader.offset();
uint32_t size;
DrawType op = ReadOpAndSize(&reader, &size);
size_t skipTo = 0;
if (NOOP == op) {
// NOOPs are to be ignored - do not propagate them any further
skipTo = fCurOffset + size;
}
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);
SkipIterTo(&it, &reader, fCurOffset + size);
continue;
}
this->handleOp(&reader, op, size, canvas, initialMatrix);
if (it.isValid()) {
uint32_t skipTo = it.nextDraw();
if (kDrawComplete == skipTo) {
break;
}
reader.setOffset(skipTo);
}
StepIterator(&it, &reader);
}
}

View File

@ -9,6 +9,7 @@
#define SkPicturePlayback_DEFINED
#include "SkPictureFlat.h" // for DrawType
#include "SkPictureStateTree.h"
class SkBitmap;
class SkCanvas;
@ -96,6 +97,18 @@ protected:
SkCanvas* canvas,
const SkMatrix& initialMatrix);
const SkPicture::OperationList* getActiveOps(const SkCanvas* canvas);
bool initIterator(SkPictureStateTree::Iterator* iter,
SkCanvas* canvas,
const SkPicture::OperationList *activeOpsList);
static void StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader);
static void SkipIterTo(SkPictureStateTree::Iterator* iter,
SkReader32* reader, uint32_t skipTo);
bool replaceOps(SkPictureStateTree::Iterator* iter,
SkReader32* reader,
SkCanvas* canvas,
const SkMatrix& initialMatrix);
static DrawType ReadOpAndSize(SkReader32* reader, uint32_t* size);
class AutoResetOpID {

View File

@ -73,9 +73,10 @@ void SkPictureStateTree::appendClip(size_t offset) {
this->appendNode(offset);
}
SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
SkCanvas* canvas) {
return Iterator(draws, canvas, &fRoot);
void SkPictureStateTree::initIterator(SkPictureStateTree::Iterator* iter,
const SkTDArray<void*>& draws,
SkCanvas* canvas) {
iter->init(draws, canvas, &fRoot);
}
void SkPictureStateTree::appendNode(size_t offset) {
@ -88,15 +89,16 @@ void SkPictureStateTree::appendNode(size_t offset) {
fCurrentState.fNode = n;
}
SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
: fDraws(&draws)
, fCanvas(canvas)
, fCurrentNode(root)
, fPlaybackMatrix(canvas->getTotalMatrix())
, fCurrentMatrix(NULL)
, fPlaybackIndex(0)
, fSave(false)
, fValid(true) {
void SkPictureStateTree::Iterator::init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root) {
SkASSERT(!fValid);
fDraws = &draws;
fCanvas = canvas;
fCurrentNode = root;
fPlaybackMatrix = canvas->getTotalMatrix();
fCurrentMatrix = NULL;
fPlaybackIndex = 0;
fSave = false;
fValid = true;
}
void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) {

View File

@ -50,11 +50,13 @@ public:
Draw* appendDraw(size_t offset);
/**
* Given a list of draws, and a canvas, returns an iterator that produces the correct sequence
* of offsets into the command buffer to carry out those calls with correct matrix/clip state.
* This handles saves/restores, and does all necessary matrix setup.
* Given a list of draws, and a canvas, initialize an iterator that produces the correct
* sequence of offsets into the command buffer to carry out those calls with correct
* matrix/clip state. This handles saves/restores, and does all necessary matrix setup.
*/
Iterator getIterator(const SkTDArray<void*>& draws, SkCanvas* canvas);
void initIterator(SkPictureStateTree::Iterator* iter,
const SkTDArray<void*>& draws,
SkCanvas* canvas);
void appendSave();
void appendSaveLayer(size_t offset);
@ -83,11 +85,11 @@ public:
*/
uint32_t nextDraw();
static const uint32_t kDrawComplete = SK_MaxU32;
Iterator() : fPlaybackMatrix(), fValid(false) { }
Iterator() : fValid(false) { }
bool isValid() const { return fValid; }
private:
Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
void init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
void setCurrentMatrix(const SkMatrix*);
@ -104,7 +106,7 @@ public:
SkTDArray<Node*> fNodes;
// The matrix of the canvas we're playing back into
const SkMatrix fPlaybackMatrix;
SkMatrix fPlaybackMatrix;
// Cache of current matrix, so we can avoid redundantly setting it
const SkMatrix* fCurrentMatrix;