michael@0: michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkPictureStateTree.h" michael@0: #include "SkCanvas.h" michael@0: michael@0: SkPictureStateTree::SkPictureStateTree() michael@0: : fAlloc(2048) michael@0: , fLastRestoredNode(NULL) michael@0: , fStateStack(sizeof(Draw), 16) { michael@0: fRootMatrix.reset(); michael@0: fRoot.fParent = NULL; michael@0: fRoot.fMatrix = &fRootMatrix; michael@0: fRoot.fFlags = Node::kSave_Flag; michael@0: fRoot.fOffset = 0; michael@0: fRoot.fLevel = 0; michael@0: fCurrentState.fNode = &fRoot; michael@0: fCurrentState.fMatrix = &fRootMatrix; michael@0: *static_cast(fStateStack.push_back()) = fCurrentState; michael@0: } michael@0: michael@0: SkPictureStateTree::~SkPictureStateTree() { michael@0: } michael@0: michael@0: SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(size_t offset) { michael@0: Draw* draw = static_cast(fAlloc.allocThrow(sizeof(Draw))); michael@0: *draw = fCurrentState; michael@0: draw->fOffset = SkToU32(offset); michael@0: return draw; michael@0: } michael@0: michael@0: void SkPictureStateTree::appendSave() { michael@0: *static_cast(fStateStack.push_back()) = fCurrentState; michael@0: fCurrentState.fNode->fFlags |= Node::kSave_Flag; michael@0: } michael@0: michael@0: void SkPictureStateTree::appendSaveLayer(size_t offset) { michael@0: *static_cast(fStateStack.push_back()) = fCurrentState; michael@0: this->appendNode(offset); michael@0: fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag; michael@0: } michael@0: michael@0: void SkPictureStateTree::saveCollapsed() { michael@0: SkASSERT(NULL != fLastRestoredNode); michael@0: SkASSERT(SkToBool(fLastRestoredNode->fFlags & \ michael@0: (Node::kSaveLayer_Flag | Node::kSave_Flag))); michael@0: SkASSERT(fLastRestoredNode->fParent == fCurrentState.fNode); michael@0: // The structure of the tree is not modified here. We just turn off michael@0: // the save or saveLayer flag to prevent the iterator from making state michael@0: // changing calls on the playback canvas when traversing a save or michael@0: // saveLayerNode node. michael@0: fLastRestoredNode->fFlags = 0; michael@0: } michael@0: michael@0: void SkPictureStateTree::appendRestore() { michael@0: fLastRestoredNode = fCurrentState.fNode; michael@0: fCurrentState = *static_cast(fStateStack.back()); michael@0: fStateStack.pop_back(); michael@0: } michael@0: michael@0: void SkPictureStateTree::appendTransform(const SkMatrix& trans) { michael@0: SkMatrix* m = static_cast(fAlloc.allocThrow(sizeof(SkMatrix))); michael@0: *m = trans; michael@0: fCurrentState.fMatrix = m; michael@0: } michael@0: michael@0: void SkPictureStateTree::appendClip(size_t offset) { michael@0: this->appendNode(offset); michael@0: } michael@0: michael@0: SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray& draws, michael@0: SkCanvas* canvas) { michael@0: return Iterator(draws, canvas, &fRoot); michael@0: } michael@0: michael@0: void SkPictureStateTree::appendNode(size_t offset) { michael@0: Node* n = static_cast(fAlloc.allocThrow(sizeof(Node))); michael@0: n->fOffset = SkToU32(offset); michael@0: n->fFlags = 0; michael@0: n->fParent = fCurrentState.fNode; michael@0: n->fLevel = fCurrentState.fNode->fLevel + 1; michael@0: n->fMatrix = fCurrentState.fMatrix; michael@0: fCurrentState.fNode = n; michael@0: } michael@0: michael@0: SkPictureStateTree::Iterator::Iterator(const SkTDArray& draws, SkCanvas* canvas, Node* root) michael@0: : fDraws(&draws) michael@0: , fCanvas(canvas) michael@0: , fCurrentNode(root) michael@0: , fPlaybackMatrix(canvas->getTotalMatrix()) michael@0: , fCurrentMatrix(NULL) michael@0: , fPlaybackIndex(0) michael@0: , fSave(false) michael@0: , fValid(true) { michael@0: } michael@0: michael@0: uint32_t SkPictureStateTree::Iterator::draw() { michael@0: SkASSERT(this->isValid()); michael@0: if (fPlaybackIndex >= fDraws->count()) { michael@0: // restore back to where we started michael@0: fCanvas->setMatrix(fPlaybackMatrix); michael@0: if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } michael@0: fCurrentNode = fCurrentNode->fParent; michael@0: while (NULL != fCurrentNode) { michael@0: if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); } michael@0: if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } michael@0: fCurrentNode = fCurrentNode->fParent; michael@0: } michael@0: return kDrawComplete; michael@0: } michael@0: michael@0: Draw* draw = static_cast((*fDraws)[fPlaybackIndex]); michael@0: Node* targetNode = draw->fNode; michael@0: michael@0: if (fSave) { michael@0: fCanvas->save(SkCanvas::kClip_SaveFlag); michael@0: fSave = false; michael@0: } michael@0: michael@0: if (fCurrentNode != targetNode) { michael@0: // If we're not at the target and we don't have a list of nodes to get there, we need to michael@0: // figure out the path from our current node, to the target michael@0: if (fNodes.count() == 0) { michael@0: // Trace back up to a common ancestor, restoring to get our current state to match that michael@0: // of the ancestor, and saving a list of nodes whose state we need to apply to get to michael@0: // the target (we can restore up to the ancestor immediately, but we'll need to return michael@0: // an offset for each node on the way down to the target, to apply the desired clips and michael@0: // saveLayers, so it may take several draw() calls before the next draw actually occurs) michael@0: Node* tmp = fCurrentNode; michael@0: Node* ancestor = targetNode; michael@0: while (tmp != ancestor) { michael@0: uint16_t currentLevel = tmp->fLevel; michael@0: uint16_t targetLevel = ancestor->fLevel; michael@0: if (currentLevel >= targetLevel) { michael@0: if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); } michael@0: if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } michael@0: tmp = tmp->fParent; michael@0: } michael@0: if (currentLevel <= targetLevel) { michael@0: fNodes.push(ancestor); michael@0: ancestor = ancestor->fParent; michael@0: } michael@0: } michael@0: michael@0: if (ancestor->fFlags & Node::kSave_Flag) { michael@0: if (fCurrentNode != ancestor) { fCanvas->restore(); } michael@0: if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); } michael@0: } michael@0: fCurrentNode = ancestor; michael@0: } michael@0: michael@0: // If we're not at the target node yet, we'll need to return an offset to make the caller michael@0: // apply the next clip or saveLayer. michael@0: if (fCurrentNode != targetNode) { michael@0: if (fCurrentMatrix != fNodes.top()->fMatrix) { michael@0: fCurrentMatrix = fNodes.top()->fMatrix; michael@0: SkMatrix tmp = *fNodes.top()->fMatrix; michael@0: tmp.postConcat(fPlaybackMatrix); michael@0: fCanvas->setMatrix(tmp); michael@0: } michael@0: uint32_t offset = fNodes.top()->fOffset; michael@0: fCurrentNode = fNodes.top(); michael@0: fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag; michael@0: fNodes.pop(); michael@0: return offset; michael@0: } michael@0: } michael@0: michael@0: // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix michael@0: // for the draw, and return its offset. michael@0: michael@0: if (fCurrentMatrix != draw->fMatrix) { michael@0: SkMatrix tmp = *draw->fMatrix; michael@0: tmp.postConcat(fPlaybackMatrix); michael@0: fCanvas->setMatrix(tmp); michael@0: fCurrentMatrix = draw->fMatrix; michael@0: } michael@0: michael@0: ++fPlaybackIndex; michael@0: return draw->fOffset; michael@0: }