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 12 SkPictureStateTree::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 27 SkPictureStateTree::~SkPictureStateTree() { 28 } 29 30 SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) { 31 Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw))); 32 *draw = fCurrentState; 33 draw->fOffset = offset; 34 return draw; 35 } 36 37 void SkPictureStateTree::appendSave() { 38 *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState; 39 fCurrentState.fNode->fFlags |= Node::kSave_Flag; 40 } 41 42 void SkPictureStateTree::appendSaveLayer(uint32_t offset) { 43 *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState; 44 this->appendNode(offset); 45 fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag; 46 } 47 48 void 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 60 void SkPictureStateTree::appendRestore() { 61 fLastRestoredNode = fCurrentState.fNode; 62 fCurrentState = *static_cast<Draw*>(fStateStack.back()); 63 fStateStack.pop_back(); 64 } 65 66 void SkPictureStateTree::appendTransform(const SkMatrix& trans) { 67 SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix))); 68 *m = trans; 69 fCurrentState.fMatrix = m; 70 } 71 72 void SkPictureStateTree::appendClip(uint32_t offset) { 73 this->appendNode(offset); 74 } 75 76 SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws, 77 SkCanvas* canvas) { 78 return Iterator(draws, canvas, &fRoot); 79 } 80 81 void SkPictureStateTree::appendNode(uint32_t offset) { 82 Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node))); 83 n->fOffset = 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 91 SkPictureStateTree::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 102 uint32_t SkPictureStateTree::Iterator::draw() { 103 SkASSERT(this->isValid()); 104 if (fPlaybackIndex >= fDraws->count()) { 105 // restore back to where we started 106 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } 107 fCurrentNode = fCurrentNode->fParent; 108 while (NULL != fCurrentNode) { 109 if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); } 110 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } 111 fCurrentNode = fCurrentNode->fParent; 112 } 113 fCanvas->setMatrix(fPlaybackMatrix); 114 return kDrawComplete; 115 } 116 117 Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]); 118 Node* targetNode = draw->fNode; 119 120 if (fSave) { 121 fCanvas->save(SkCanvas::kClip_SaveFlag); 122 fSave = false; 123 } 124 125 if (fCurrentNode != targetNode) { 126 // If we're not at the target and we don't have a list of nodes to get there, we need to 127 // figure out the path from our current node, to the target 128 if (fNodes.count() == 0) { 129 // Trace back up to a common ancestor, restoring to get our current state to match that 130 // of the ancestor, and saving a list of nodes whose state we need to apply to get to 131 // the target (we can restore up to the ancestor immediately, but we'll need to return 132 // an offset for each node on the way down to the target, to apply the desired clips and 133 // saveLayers, so it may take several draw() calls before the next draw actually occurs) 134 Node* tmp = fCurrentNode; 135 Node* ancestor = targetNode; 136 while (tmp != ancestor) { 137 uint16_t currentLevel = tmp->fLevel; 138 uint16_t targetLevel = ancestor->fLevel; 139 if (currentLevel >= targetLevel) { 140 if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); } 141 if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); } 142 tmp = tmp->fParent; 143 } 144 if (currentLevel <= targetLevel) { 145 fNodes.push(ancestor); 146 ancestor = ancestor->fParent; 147 } 148 } 149 150 if (ancestor->fFlags & Node::kSave_Flag) { 151 if (fCurrentNode != ancestor) { fCanvas->restore(); } 152 if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); } 153 } 154 fCurrentNode = ancestor; 155 } 156 157 // If we're not at the target node yet, we'll need to return an offset to make the caller 158 // apply the next clip or saveLayer. 159 if (fCurrentNode != targetNode) { 160 if (fCurrentMatrix != fNodes.top()->fMatrix) { 161 fCurrentMatrix = fNodes.top()->fMatrix; 162 SkMatrix tmp = *fNodes.top()->fMatrix; 163 tmp.postConcat(fPlaybackMatrix); 164 fCanvas->setMatrix(tmp); 165 } 166 uint32_t offset = fNodes.top()->fOffset; 167 fCurrentNode = fNodes.top(); 168 fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag; 169 fNodes.pop(); 170 return offset; 171 } 172 } 173 174 // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix 175 // for the draw, and return its offset. 176 177 if (fCurrentMatrix != draw->fMatrix) { 178 SkMatrix tmp = *draw->fMatrix; 179 tmp.postConcat(fPlaybackMatrix); 180 fCanvas->setMatrix(tmp); 181 fCurrentMatrix = draw->fMatrix; 182 } 183 184 ++fPlaybackIndex; 185 return draw->fOffset; 186 } 187