Split SkPictureReplacementPlayback out of SkPicturePlayback
R=mtklein@google.com, reed@google.com
Author: robertphillips@google.com
Review URL: https://codereview.chromium.org/383733002
diff --git a/gyp/core.gypi b/gyp/core.gypi
index b1c8cc9..f7853f9 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -140,6 +140,8 @@
'<(skia_src_path)/core/SkPictureRecord.cpp',
'<(skia_src_path)/core/SkPictureRecord.h',
'<(skia_src_path)/core/SkPictureRecorder.cpp',
+ '<(skia_src_path)/core/SkPictureReplacementPlayback.cpp',
+ '<(skia_src_path)/core/SkPictureReplacementPlayback.h',
'<(skia_src_path)/core/SkPictureShader.cpp',
'<(skia_src_path)/core/SkPictureShader.h',
'<(skia_src_path)/core/SkPictureStateTree.cpp',
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index a346606..4652136 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -288,6 +288,7 @@
friend class GrGatherDevice;
friend class SkDebugCanvas;
friend class SkPicturePlayback; // to get fData
+ friend class SkPictureReplacementPlayback; // to access OperationList
typedef SkRefCnt INHERITED;
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index eee63d2..0c5c74a 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -14,48 +14,6 @@
#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;
-}
-
/*
* 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
@@ -137,79 +95,6 @@
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;
- }
-
- reader->setOffset(skipTo);
- } else {
- reader->setOffset(temp->fStop);
- uint32_t size;
- SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
- SkASSERT(RESTORE == op);
- }
-
- return true;
- }
- }
-
- return false;
-}
-
// If 'iter' is valid use it to skip forward through the picture.
void SkPicturePlayback::StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader) {
if (iter->isValid()) {
@@ -270,10 +155,6 @@
return;
}
- if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
- continue;
- }
-
fCurOffset = reader.offset();
uint32_t size;
DrawType op = ReadOpAndSize(&reader, &size);
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index 6c32b15..6114fee 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -17,71 +17,29 @@
class SkPaint;
class SkPictureData;
+// The basic picture playback class replays the provided picture into a canvas.
+// If the picture was generated with a BBH it is used to accelerate drawing
+// unless disabled via setUseBBH.
class SkPicturePlayback : SkNoncopyable {
public:
SkPicturePlayback(const SkPicture* picture)
: fPictureData(picture->fData.get())
, fCurOffset(0)
- , fUseBBH(true)
- , fReplacements(NULL) {
+ , fUseBBH(true) {
}
virtual ~SkPicturePlayback() { }
virtual void draw(SkCanvas* canvas, SkDrawPictureCallback*);
+ // TODO: remove the curOp calls after cleaning up GrGatherDevice
// 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; }
+ // TODO: remove setUseBBH after cleaning up GrGatherCanvas
void setUseBBH(bool useBBH) { fUseBBH = useBBH; }
- // 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<ReplacementInfo> 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) {
- fReplacements = replacements;
- }
-
protected:
const SkPictureData* fPictureData;
@@ -89,7 +47,6 @@
size_t fCurOffset;
bool fUseBBH;
- PlaybackReplacements* fReplacements;
void handleOp(SkReader32* reader,
DrawType op,
@@ -104,10 +61,6 @@
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);
diff --git a/src/core/SkPictureRangePlayback.h b/src/core/SkPictureRangePlayback.h
index 61d10f5..ee114bd 100644
--- a/src/core/SkPictureRangePlayback.h
+++ b/src/core/SkPictureRangePlayback.h
@@ -1,3 +1,4 @@
+
/*
* Copyright 2014 Google Inc.
*
diff --git a/src/core/SkPictureReplacementPlayback.cpp b/src/core/SkPictureReplacementPlayback.cpp
new file mode 100644
index 0000000..1249806
--- /dev/null
+++ b/src/core/SkPictureReplacementPlayback.cpp
@@ -0,0 +1,171 @@
+
+/*
+ * 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);
+ }
+}
diff --git a/src/core/SkPictureReplacementPlayback.h b/src/core/SkPictureReplacementPlayback.h
new file mode 100644
index 0000000..166a3bf
--- /dev/null
+++ b/src/core/SkPictureReplacementPlayback.h
@@ -0,0 +1,86 @@
+/*
+ * 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 SkPictureReplacementPlayback_DEFINED
+#define SkPictureReplacementPlayback_DEFINED
+
+#include "SkPicturePlayback.h"
+
+// This playback class replaces complete "saveLayer ... restore" runs with a
+// single drawBitmap call.
+class SkPictureReplacementPlayback : public SkPicturePlayback {
+public:
+ // 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();
+
+ // look up a replacement range by its start offset
+ ReplacementInfo* lookupByStart(size_t start);
+
+ private:
+ SkTDArray<ReplacementInfo> fReplacements;
+
+ void freeAll();
+
+#ifdef SK_DEBUG
+ void validate() const;
+#endif
+ };
+
+ // This class doesn't take ownership of either 'replacements' or 'activeOpsList'
+ // The caller must guarantee they exist across any calls to 'draw'.
+ // 'activeOpsList' can be NULL but in that case BBH acceleration will not
+ // be used ('replacements' can be NULL too but that defeats the purpose
+ // of using this class).
+ SkPictureReplacementPlayback(const SkPicture* picture,
+ PlaybackReplacements* replacements,
+ const SkPicture::OperationList* activeOpsList)
+ : INHERITED(picture)
+ , fReplacements(replacements)
+ , fActiveOpsList(activeOpsList) {
+ }
+
+ virtual void draw(SkCanvas* canvas, SkDrawPictureCallback*) SK_OVERRIDE;
+
+private:
+ PlaybackReplacements* fReplacements;
+ const SkPicture::OperationList* fActiveOpsList;
+
+ // This method checks if the current op pointed at by 'iter' and 'reader'
+ // is within a replacement range. If so, it issues the drawBitmap call,
+ // updates 'iter' and 'reader' to be after the restore operation, and
+ // returns true. If the operation is not in a replacement range (and thus
+ // needs to be drawn normally) false is returned.
+ bool replaceOps(SkPictureStateTree::Iterator* iter,
+ SkReader32* reader,
+ SkCanvas* canvas,
+ const SkMatrix& initialMatrix);
+
+ typedef SkPicturePlayback INHERITED;
+};
+
+#endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 0dbc2fb..870ca4a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -30,8 +30,8 @@
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkPictureData.h"
-#include "SkPicturePlayback.h"
#include "SkPictureRangePlayback.h"
+#include "SkPictureReplacementPlayback.h"
#include "SkRRect.h"
#include "SkStroke.h"
#include "SkSurface.h"
@@ -1938,7 +1938,7 @@
}
}
- SkPicturePlayback::PlaybackReplacements replacements;
+ SkPictureReplacementPlayback::PlaybackReplacements replacements;
// Generate the layer and/or ensure it is locked
for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
@@ -1947,7 +1947,7 @@
const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
- SkPicturePlayback::PlaybackReplacements::ReplacementInfo* layerInfo =
+ SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo* layerInfo =
replacements.push();
layerInfo->fStart = info.fSaveLayerOpID;
layerInfo->fStop = info.fRestoreOpID;
@@ -2030,9 +2030,8 @@
}
// Playback using new layers
- SkPicturePlayback playback(picture);
+ SkPictureReplacementPlayback playback(picture, &replacements, ops.get());
- playback.setReplacements(&replacements);
playback.draw(canvas, NULL);
// unlock the layers