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 102void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) { 103 SkASSERT(NULL != matrix); 104 105 if (matrix == fCurrentMatrix) { 106 return; 107 } 108 109 // The matrix is in recording space, but we also inherit 110 // a playback matrix from out target canvas. 111 SkMatrix m = *matrix; 112 m.postConcat(fPlaybackMatrix); 113 fCanvas->setMatrix(m); 114 fCurrentMatrix = matrix; 115} 116 117uint32_t SkPictureStateTree::Iterator::finish() { 118 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { 119 fCanvas->restore(); 120 } 121 122 for (fCurrentNode = fCurrentNode->fParent; fCurrentNode; 123 fCurrentNode = fCurrentNode->fParent) { 124 // Note: we call restore() twice when both flags are set. 125 if (fCurrentNode->fFlags & Node::kSave_Flag) { 126 fCanvas->restore(); 127 } 128 if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { 129 fCanvas->restore(); 130 } 131 } 132 133 fCanvas->setMatrix(fPlaybackMatrix); 134 fCurrentMatrix = NULL; 135 return kDrawComplete; 136} 137 138uint32_t SkPictureStateTree::Iterator::nextDraw() { 139 SkASSERT(this->isValid()); 140 if (fPlaybackIndex >= fDraws->count()) { 141 return this->finish(); 142 } 143 144 Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]); 145 Node* targetNode = draw->fNode; 146 147 if (fSave) { 148 fCanvas->save(); 149 fSave = false; 150 } 151 152 if (fCurrentNode != targetNode) { 153 // If we're not at the target and we don't have a list of nodes to get there, we need to 154 // figure out the path from our current node, to the target 155 if (fNodes.count() == 0) { 156 // Trace back up to a common ancestor, restoring to get our current state to match that 157 // of the ancestor, and saving a list of nodes whose state we need to apply to get to 158 // the target (we can restore up to the ancestor immediately, but we'll need to return 159 // an offset for each node on the way down to the target, to apply the desired clips and 160 // saveLayers, so it may take several draw() calls before the next draw actually occurs) 161 Node* tmp = fCurrentNode; 162 Node* ancestor = targetNode; 163 while (tmp != ancestor) { 164 uint16_t currentLevel = tmp->fLevel; 165 uint16_t targetLevel = ancestor->fLevel; 166 if (currentLevel >= targetLevel) { 167 if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { 168 fCanvas->restore(); 169 // restore() may change the matrix, so we need to reapply. 170 fCurrentMatrix = NULL; 171 } 172 if (tmp->fFlags & Node::kSaveLayer_Flag) { 173 fCanvas->restore(); 174 // restore() may change the matrix, so we need to reapply. 175 fCurrentMatrix = NULL; 176 } 177 tmp = tmp->fParent; 178 } 179 if (currentLevel <= targetLevel) { 180 fNodes.push(ancestor); 181 ancestor = ancestor->fParent; 182 } 183 } 184 185 if (ancestor->fFlags & Node::kSave_Flag) { 186 if (fCurrentNode != ancestor) { 187 fCanvas->restore(); 188 // restore() may change the matrix, so we need to reapply. 189 fCurrentMatrix = NULL; 190 } 191 if (targetNode != ancestor) { 192 fCanvas->save(); 193 } 194 } 195 fCurrentNode = ancestor; 196 } 197 198 // If we're not at the target node yet, we'll need to return an offset to make the caller 199 // apply the next clip or saveLayer. 200 if (fCurrentNode != targetNode) { 201 uint32_t offset = fNodes.top()->fOffset; 202 fCurrentNode = fNodes.top(); 203 fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag; 204 fNodes.pop(); 205 this->setCurrentMatrix(fCurrentNode->fMatrix); 206 return offset; 207 } 208 } 209 210 // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix 211 // for the draw, and return its offset. 212 this->setCurrentMatrix(draw->fMatrix); 213 214 ++fPlaybackIndex; 215 return draw->fOffset; 216} 217