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