aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar rileya@google.com <rileya@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-11 14:57:33 +0000
committerGravatar rileya@google.com <rileya@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-11 14:57:33 +0000
commit9fd7f8d2906365637dc25f98ffdede87d631ef71 (patch)
treec4644aa2eca9dfd0cfe2f52696284306a159a648
parent047696c1c67b2e0a73f2b951ce23ff5b155111bb (diff)
Add helper for maintaining clip/matrix state in non-contiguous picture playback.
Review URL: https://codereview.appspot.com/6509043 git-svn-id: http://skia.googlecode.com/svn/trunk@5483 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gyp/core.gypi2
-rw-r--r--src/core/SkPictureStateTree.cpp176
-rw-r--r--src/core/SkPictureStateTree.h129
3 files changed, 307 insertions, 0 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 075d91539f..80b1939999 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -117,6 +117,8 @@
'<(skia_src_path)/core/SkPicturePlayback.h',
'<(skia_src_path)/core/SkPictureRecord.cpp',
'<(skia_src_path)/core/SkPictureRecord.h',
+ '<(skia_src_path)/core/SkPictureStateTree.cpp',
+ '<(skia_src_path)/core/SkPictureStateTree.h',
'<(skia_src_path)/core/SkPixelRef.cpp',
'<(skia_src_path)/core/SkPoint.cpp',
'<(skia_src_path)/core/SkProcSpriteBlitter.cpp',
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
new file mode 100644
index 0000000000..31aa00bf7a
--- /dev/null
+++ b/src/core/SkPictureStateTree.cpp
@@ -0,0 +1,176 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPictureStateTree.h"
+#include "SkCanvas.h"
+
+SK_DEFINE_INST_COUNT(SkPictureStateTree)
+
+SkPictureStateTree::SkPictureStateTree()
+ : fAlloc(2048)
+ , fRoot(NULL)
+ , fStateStack(sizeof(Draw), 16) {
+ SkMatrix* identity = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
+ identity->reset();
+ fRoot = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
+ fRoot->fParent = NULL;
+ fRoot->fMatrix = identity;
+ fRoot->fFlags = Node::kSave_Flag;
+ fRoot->fOffset = 0;
+ fRoot->fLevel = 0;
+ fCurrentState.fNode = fRoot;
+ fCurrentState.fMatrix = identity;
+ *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+}
+
+SkPictureStateTree::~SkPictureStateTree() {
+}
+
+SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) {
+ Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
+ *draw = fCurrentState;
+ draw->fOffset = offset;
+ return draw;
+}
+
+void SkPictureStateTree::appendSave() {
+ *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+ fCurrentState.fNode->fFlags |= Node::kSave_Flag;
+}
+
+void SkPictureStateTree::appendSaveLayer(uint32_t offset) {
+ *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+ this->appendNode(offset);
+ fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
+}
+
+void SkPictureStateTree::appendRestore() {
+ fCurrentState = *static_cast<Draw*>(fStateStack.back());
+ fStateStack.pop_back();
+}
+
+void SkPictureStateTree::appendTransform(const SkMatrix& trans) {
+ SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
+ *m = trans;
+ fCurrentState.fMatrix = m;
+}
+
+void SkPictureStateTree::appendClip(uint32_t offset) {
+ this->appendNode(offset);
+}
+
+SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
+ SkCanvas* canvas) {
+ return Iterator(draws, canvas, fRoot);
+}
+
+void SkPictureStateTree::appendNode(uint32_t offset) {
+ Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
+ n->fOffset = offset;
+ n->fFlags = 0;
+ n->fParent = fCurrentState.fNode;
+ n->fLevel = fCurrentState.fNode->fLevel + 1;
+ n->fMatrix = fCurrentState.fMatrix;
+ fCurrentState.fNode = n;
+}
+
+SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
+ : fDraws(draws)
+ , fCanvas(canvas)
+ , fCurrentNode(root)
+ , fCurrentMatrix(NULL)
+ , fPlaybackIndex(0)
+ , fPlaybackMatrix(canvas->getTotalMatrix())
+ , fSave(false) {
+}
+
+uint32_t SkPictureStateTree::Iterator::draw() {
+ if (fPlaybackIndex >= fDraws.count()) {
+ // restore back to where we started
+ if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+ fCurrentNode = fCurrentNode->fParent;
+ while (NULL != fCurrentNode->fParent) {
+ if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
+ if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+ fCurrentNode = fCurrentNode->fParent;
+ }
+ fCanvas->setMatrix(fPlaybackMatrix);
+ return kDrawComplete;
+ }
+
+ Draw* draw = static_cast<Draw*>(fDraws[fPlaybackIndex]);
+ Node* targetNode = draw->fNode;
+
+ if (fSave) {
+ fCanvas->save(SkCanvas::kClip_SaveFlag);
+ fSave = false;
+ }
+
+ if (fCurrentNode != targetNode) {
+ // If we're not at the target and we don't have a list of nodes to get there, we need to
+ // figure out the path from our current node, to the target
+ if (fNodes.count() == 0) {
+ // Trace back up to a common ancestor, restoring to get our current state to match that
+ // of the ancestor, and saving a list of nodes whose state we need to apply to get to
+ // the target (we can restore up to the ancestor immediately, but we'll need to return
+ // an offset for each node on the way down to the target, to apply the desired clips and
+ // saveLayers, so it may take several draw() calls before the next draw actually occurs)
+ Node* tmp = fCurrentNode;
+ Node* ancestor = targetNode;
+ while (tmp != ancestor) {
+ uint16_t currentLevel = tmp->fLevel;
+ uint16_t targetLevel = ancestor->fLevel;
+ if (currentLevel >= targetLevel) {
+ if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
+ if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+ tmp = tmp->fParent;
+ }
+ if (currentLevel <= targetLevel) {
+ fNodes.push(ancestor);
+ ancestor = ancestor->fParent;
+ }
+ }
+
+ if (ancestor->fFlags & Node::kSave_Flag) {
+ if (fCurrentNode != ancestor) { fCanvas->restore(); }
+ if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); }
+ }
+ fCurrentNode = ancestor;
+ }
+
+ // If we're not at the target node yet, we'll need to return an offset to make the caller
+ // apply the next clip or saveLayer.
+ if (fCurrentNode != targetNode) {
+ if (fCurrentMatrix != fNodes.top()->fMatrix) {
+ fCurrentMatrix = fNodes.top()->fMatrix;
+ SkMatrix tmp = *fNodes.top()->fMatrix;
+ tmp.postConcat(fPlaybackMatrix);
+ fCanvas->setMatrix(tmp);
+ }
+ uint32_t offset = fNodes.top()->fOffset;
+ fCurrentNode = fNodes.top();
+ fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
+ fNodes.pop();
+ return offset;
+ }
+ }
+
+ // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
+ // for the draw, and return its offset.
+
+ if (fCurrentMatrix != draw->fMatrix) {
+ SkMatrix tmp = *draw->fMatrix;
+ tmp.postConcat(fPlaybackMatrix);
+ fCanvas->setMatrix(tmp);
+ fCurrentMatrix = draw->fMatrix;
+ }
+
+ ++fPlaybackIndex;
+ return draw->fOffset;
+}
+
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
new file mode 100644
index 0000000000..a4fb8871b5
--- /dev/null
+++ b/src/core/SkPictureStateTree.h
@@ -0,0 +1,129 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPictureStateTree_DEFINED
+#define SkPictureStateTree_DEFINED
+
+#include "SkTDArray.h"
+#include "SkChunkAlloc.h"
+#include "SkDeque.h"
+#include "SkMatrix.h"
+#include "SkRefCnt.h"
+
+class SkCanvas;
+
+/**
+ * Provides an interface that, given a sequence of draws into an SkPicture with corresponding
+ * offsets, allows for playback of an arbitrary subset of the draws (note that Z-order is only
+ * guaranteed if the draws are explicitly sorted).
+ */
+class SkPictureStateTree : public SkRefCnt {
+private:
+ struct Node;
+public:
+ SK_DECLARE_INST_COUNT(SkPictureStateTree)
+
+ /**
+ * A draw call, stores offset into command buffer, a pointer to the matrix, and a pointer to
+ * the node in the tree that corresponds to its clip/layer state
+ */
+ struct Draw {
+ SkMatrix* fMatrix;
+ Node* fNode;
+ uint32_t fOffset;
+ bool operator<(const Draw& other) { return fOffset < other.fOffset; }
+ };
+
+ class Iterator;
+
+ SkPictureStateTree();
+ ~SkPictureStateTree();
+
+ /**
+ * Creates and returns a struct representing a draw at the given offset.
+ */
+ Draw* appendDraw(uint32_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.
+ */
+ Iterator getIterator(const SkTDArray<void*>& draws, SkCanvas* canvas);
+
+ void appendSave();
+ void appendSaveLayer(uint32_t offset);
+ void appendRestore();
+ void appendTransform(const SkMatrix& trans);
+ void appendClip(uint32_t offset);
+
+ /**
+ * Playback helper
+ */
+ class Iterator {
+ public:
+ /** Returns the next offset into the picture stream, or kDrawComplete if complete. */
+ uint32_t draw();
+ static const uint32_t kDrawComplete = SK_MaxU32;
+ private:
+ Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
+ // The draws this iterator is associated with
+ const SkTDArray<void*>& fDraws;
+ // The matrix of the canvas we're playing back into
+ const SkMatrix fPlaybackMatrix;
+
+ // current state node
+ Node* fCurrentNode;
+
+ // List of nodes whose state we need to apply to reach TargetNode
+ SkTDArray<Node*> fNodes;
+ // Cache of current matrix, so we can avoid redundantly setting it
+ SkMatrix* fCurrentMatrix;
+
+ // canvas this is playing into (so we can insert saves/restores as necessary)
+ SkCanvas* fCanvas;
+
+ // current position in the array of draws
+ int fPlaybackIndex;
+ // Whether or not we need to do a save next iteration
+ bool fSave;
+
+ friend class SkPictureStateTree;
+ };
+
+private:
+
+ void appendNode(uint32_t offset);
+
+ SkChunkAlloc fAlloc;
+ Node* fRoot;
+
+ // The currently active state
+ Draw fCurrentState;
+ // A stack of states for tracking save/restores
+ SkDeque fStateStack;
+
+ // Represents a notable piece of state that requires an offset into the command buffer,
+ // corresponding to a clip/saveLayer/etc call, to apply.
+ struct Node {
+ Node* fParent;
+ uint32_t fOffset;
+ uint16_t fLevel;
+ uint16_t fFlags;
+ SkMatrix* fMatrix;
+ enum Flags {
+ kSave_Flag = 0x1,
+ kSaveLayer_Flag = 0x2
+ };
+ };
+
+ typedef SkRefCnt INHERITED;
+};
+
+#endif
+