Home | History | Annotate | Download | only in core
      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 }
    219