SkPictureStateTree.cpp revision 091a594dbc4116ec2e54724432472bf37dae794a
1 2/* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "SkPictureStateTree.h" 10#include "SkCanvas.h" 11 12SkPictureStateTree::SkPictureStateTree() 13 : fAlloc(2048) 14 , fLastRestoredNode(NULL) 15 , fStateStack(sizeof(Draw), 16) { 16 fRootMatrix.reset(); 17 fRoot.fParent = NULL; 18 fRoot.fMatrix = &fRootMatrix; 19 fRoot.fFlags = Node::kSave_Flag; 20 fRoot.fOffset = 0; 21 fRoot.fLevel = 0; 22 fCurrentState.fNode = &fRoot; 23 fCurrentState.fMatrix = &fRootMatrix; 24 *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState; 25} 26 27SkPictureStateTree::~SkPictureStateTree() { 28} 29 30SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(size_t offset) { 31 Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw))); 32 *draw = fCurrentState; 33 draw->fOffset = SkToU32(offset); 34 return draw; 35} 36 37void SkPictureStateTree::appendSave() { 38 *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState; 39 fCurrentState.fNode->fFlags |= Node::kSave_Flag; 40} 41 42void SkPictureStateTree::appendSaveLayer(size_t offset) { 43 *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState; 44 this->appendNode(offset); 45 fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag; 46} 47 48void SkPictureStateTree::saveCollapsed() { 49 SkASSERT(NULL != fLastRestoredNode); 50 SkASSERT(SkToBool(fLastRestoredNode->fFlags & \ 51 (Node::kSaveLayer_Flag | Node::kSave_Flag))); 52 SkASSERT(fLastRestoredNode->fParent == fCurrentState.fNode); 53 // The structure of the tree is not modified here. We just turn off 54 // the save or saveLayer flag to prevent the iterator from making state 55 // changing calls on the playback canvas when traversing a save or 56 // saveLayerNode node. 57 fLastRestoredNode->fFlags = 0; 58} 59 60void SkPictureStateTree::appendRestore() { 61 fLastRestoredNode = fCurrentState.fNode; 62 fCurrentState = *static_cast<Draw*>(fStateStack.back()); 63 fStateStack.pop_back(); 64} 65 66void SkPictureStateTree::appendTransform(const SkMatrix& trans) { 67 SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix))); 68 *m = trans; 69 fCurrentState.fMatrix = m; 70} 71 72void SkPictureStateTree::appendClip(size_t offset) { 73 this->appendNode(offset); 74} 75 76SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws, 77 SkCanvas* canvas) { 78 return Iterator(draws, canvas, &fRoot); 79} 80 81void SkPictureStateTree::appendNode(size_t offset) { 82 Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node))); 83 n->fOffset = SkToU32(offset); 84 n->fFlags = 0; 85 n->fParent = fCurrentState.fNode; 86 n->fLevel = fCurrentState.fNode->fLevel + 1; 87 n->fMatrix = fCurrentState.fMatrix; 88 fCurrentState.fNode = n; 89} 90 91SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root) 92 : fDraws(&draws) 93 , fCanvas(canvas) 94 , fCurrentNode(root) 95 , fPlaybackMatrix(canvas->getTotalMatrix()) 96 , fCurrentMatrix(NULL) 97 , fPlaybackIndex(0) 98 , fSave(false) 99 , fValid(true) { 100} 101 102uint32_t SkPictureStateTree::Iterator::draw() { 103 SkASSERT(this->isValid()); 104 if (fPlaybackIndex >= fDraws->count()) { 105 // restore back to where we started 106 fCanvas->setMatrix(fPlaybackMatrix); 107 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { 108 fCanvas->restore(); 109 } 110 fCurrentNode = fCurrentNode->fParent; 111 while (NULL != fCurrentNode) { 112 if (fCurrentNode->fFlags & Node::kSave_Flag) { 113 fCanvas->restore(); 114 } 115 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { 116 fCanvas->restore(); 117 } 118 fCurrentNode = fCurrentNode->fParent; 119 } 120 return kDrawComplete; 121 } 122 123 Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]); 124 Node* targetNode = draw->fNode; 125 126 if (fSave) { 127 fCanvas->save(); 128 fSave = false; 129 } 130 131 if (fCurrentNode != targetNode) { 132 // If we're not at the target and we don't have a list of nodes to get there, we need to 133 // figure out the path from our current node, to the target 134 if (fNodes.count() == 0) { 135 // Trace back up to a common ancestor, restoring to get our current state to match that 136 // of the ancestor, and saving a list of nodes whose state we need to apply to get to 137 // the target (we can restore up to the ancestor immediately, but we'll need to return 138 // an offset for each node on the way down to the target, to apply the desired clips and 139 // saveLayers, so it may take several draw() calls before the next draw actually occurs) 140 Node* tmp = fCurrentNode; 141 Node* ancestor = targetNode; 142 while (tmp != ancestor) { 143 uint16_t currentLevel = tmp->fLevel; 144 uint16_t targetLevel = ancestor->fLevel; 145 if (currentLevel >= targetLevel) { 146 if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { 147 fCanvas->restore(); 148 } 149 if (tmp->fFlags & Node::kSaveLayer_Flag) { 150 fCanvas->restore(); 151 } 152 tmp = tmp->fParent; 153 } 154 if (currentLevel <= targetLevel) { 155 fNodes.push(ancestor); 156 ancestor = ancestor->fParent; 157 } 158 } 159 160 if (ancestor->fFlags & Node::kSave_Flag) { 161 if (fCurrentNode != ancestor) { 162 fCanvas->restore(); 163 } 164 if (targetNode != ancestor) { 165 // FIXME: the save below depends on soon-to-be-deprecated 166 // SaveFlags behavior: it relies on matrix changes persisting 167 // after restore. 168 fCanvas->save(SkCanvas::kClip_SaveFlag); 169 } 170 } 171 fCurrentNode = ancestor; 172 } 173 174 // If we're not at the target node yet, we'll need to return an offset to make the caller 175 // apply the next clip or saveLayer. 176 if (fCurrentNode != targetNode) { 177 if (fCurrentMatrix != fNodes.top()->fMatrix) { 178 fCurrentMatrix = fNodes.top()->fMatrix; 179 SkMatrix tmp = *fNodes.top()->fMatrix; 180 tmp.postConcat(fPlaybackMatrix); 181 fCanvas->setMatrix(tmp); 182 } 183 uint32_t offset = fNodes.top()->fOffset; 184 fCurrentNode = fNodes.top(); 185 fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag; 186 fNodes.pop(); 187 return offset; 188 } 189 } 190 191 // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix 192 // for the draw, and return its offset. 193 194 if (fCurrentMatrix != draw->fMatrix) { 195 SkMatrix tmp = *draw->fMatrix; 196 tmp.postConcat(fPlaybackMatrix); 197 fCanvas->setMatrix(tmp); 198 fCurrentMatrix = draw->fMatrix; 199 } 200 201 ++fPlaybackIndex; 202 return draw->fOffset; 203} 204