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