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