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