Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / core / SkPictureStateTree.cpp
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(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
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(size_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(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(size_t offset) {
73     this->appendNode(offset);
74 }
75
76 void SkPictureStateTree::initIterator(SkPictureStateTree::Iterator* iter,
77                                       const SkTDArray<void*>& draws,
78                                       SkCanvas* canvas) {
79     iter->init(draws, canvas, &fRoot);
80 }
81
82 void 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
92 void 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
104 void 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
119 uint32_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
140 uint32_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 }