From 9fd7f8d2906365637dc25f98ffdede87d631ef71 Mon Sep 17 00:00:00 2001 From: "rileya@google.com" Date: Tue, 11 Sep 2012 14:57:33 +0000 Subject: 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 --- src/core/SkPictureStateTree.cpp | 176 ++++++++++++++++++++++++++++++++++++++++ src/core/SkPictureStateTree.h | 129 +++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 src/core/SkPictureStateTree.cpp create mode 100644 src/core/SkPictureStateTree.h (limited to 'src/core') 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(fAlloc.allocThrow(sizeof(SkMatrix))); + identity->reset(); + fRoot = static_cast(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(fStateStack.push_back()) = fCurrentState; +} + +SkPictureStateTree::~SkPictureStateTree() { +} + +SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) { + Draw* draw = static_cast(fAlloc.allocThrow(sizeof(Draw))); + *draw = fCurrentState; + draw->fOffset = offset; + return draw; +} + +void SkPictureStateTree::appendSave() { + *static_cast(fStateStack.push_back()) = fCurrentState; + fCurrentState.fNode->fFlags |= Node::kSave_Flag; +} + +void SkPictureStateTree::appendSaveLayer(uint32_t offset) { + *static_cast(fStateStack.push_back()) = fCurrentState; + this->appendNode(offset); + fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag; +} + +void SkPictureStateTree::appendRestore() { + fCurrentState = *static_cast(fStateStack.back()); + fStateStack.pop_back(); +} + +void SkPictureStateTree::appendTransform(const SkMatrix& trans) { + SkMatrix* m = static_cast(fAlloc.allocThrow(sizeof(SkMatrix))); + *m = trans; + fCurrentState.fMatrix = m; +} + +void SkPictureStateTree::appendClip(uint32_t offset) { + this->appendNode(offset); +} + +SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray& draws, + SkCanvas* canvas) { + return Iterator(draws, canvas, fRoot); +} + +void SkPictureStateTree::appendNode(uint32_t offset) { + Node* n = static_cast(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& 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(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& 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& draws, SkCanvas* canvas, Node* root); + // The draws this iterator is associated with + const SkTDArray& 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 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 + -- cgit v1.2.3