blob: 12498064e085993055e5fa411afe1fc7c90bb65f [file] [log] [blame]
/*
* 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 "SkPicture.h"
#include "SkPictureData.h"
#include "SkPictureReplacementPlayback.h"
SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo*
SkPictureReplacementPlayback::PlaybackReplacements::push() {
SkDEBUGCODE(this->validate());
return fReplacements.push();
}
void SkPictureReplacementPlayback::PlaybackReplacements::freeAll() {
for (int i = 0; i < fReplacements.count(); ++i) {
SkDELETE(fReplacements[i].fBM);
}
fReplacements.reset();
}
#ifdef SK_DEBUG
void SkPictureReplacementPlayback::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
SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo*
SkPictureReplacementPlayback::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;
}
bool SkPictureReplacementPlayback::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
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;
}
reader->setOffset(skipTo);
} else {
reader->setOffset(temp->fStop);
uint32_t size;
SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
SkASSERT(RESTORE == op);
}
return true;
}
}
return false;
}
void SkPictureReplacementPlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
AutoResetOpID aroi(this);
SkASSERT(0 == fCurOffset);
SkPictureStateTree::Iterator it;
if (!this->initIterator(&it, canvas, fActiveOpsList)) {
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();
SkAutoCanvasRestore acr(canvas, false);
while (!reader.eof()) {
if (NULL != callback && callback->abortDrawing()) {
return;
}
if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
continue;
}
fCurOffset = reader.offset();
uint32_t size;
DrawType op = ReadOpAndSize(&reader, &size);
if (NOOP == op) {
// NOOPs are to be ignored - do not propagate them any further
SkipIterTo(&it, &reader, fCurOffset + size);
continue;
}
this->handleOp(&reader, op, size, canvas, initialMatrix);
StepIterator(&it, &reader);
}
}