/* * 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) , fPlaybackMatrix(canvas->getTotalMatrix()) , fCurrentMatrix(NULL) , fPlaybackIndex(0) , fSave(false) , fValid(true) { } uint32_t SkPictureStateTree::Iterator::draw() { SkASSERT(this->isValid()); if (fPlaybackIndex >= fDraws->count()) { // restore back to where we started if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } fCurrentNode = fCurrentNode->fParent; while (NULL != fCurrentNode) { 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; }