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(uint32_t offset) {
     31     Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
     32     *draw = fCurrentState;
     33     draw->fOffset = 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(uint32_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(uint32_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(uint32_t offset) {
     82     Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
     83     n->fOffset = 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 uint32_t SkPictureStateTree::Iterator::draw() {
    103     SkASSERT(this->isValid());
    104     if (fPlaybackIndex >= fDraws->count()) {
    105         // restore back to where we started
    106         if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    107         fCurrentNode = fCurrentNode->fParent;
    108         while (NULL != fCurrentNode) {
    109             if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
    110             if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    111             fCurrentNode = fCurrentNode->fParent;
    112         }
    113         fCanvas->setMatrix(fPlaybackMatrix);
    114         return kDrawComplete;
    115     }
    116 
    117     Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
    118     Node* targetNode = draw->fNode;
    119 
    120     if (fSave) {
    121         fCanvas->save(SkCanvas::kClip_SaveFlag);
    122         fSave = false;
    123     }
    124 
    125     if (fCurrentNode != targetNode) {
    126         // If we're not at the target and we don't have a list of nodes to get there, we need to
    127         // figure out the path from our current node, to the target
    128         if (fNodes.count() == 0) {
    129             // Trace back up to a common ancestor, restoring to get our current state to match that
    130             // of the ancestor, and saving a list of nodes whose state we need to apply to get to
    131             // the target (we can restore up to the ancestor immediately, but we'll need to return
    132             // an offset for each node on the way down to the target, to apply the desired clips and
    133             // saveLayers, so it may take several draw() calls before the next draw actually occurs)
    134             Node* tmp = fCurrentNode;
    135             Node* ancestor = targetNode;
    136             while (tmp != ancestor) {
    137                 uint16_t currentLevel = tmp->fLevel;
    138                 uint16_t targetLevel = ancestor->fLevel;
    139                 if (currentLevel >= targetLevel) {
    140                     if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
    141                     if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    142                     tmp = tmp->fParent;
    143                 }
    144                 if (currentLevel <= targetLevel) {
    145                     fNodes.push(ancestor);
    146                     ancestor = ancestor->fParent;
    147                 }
    148             }
    149 
    150             if (ancestor->fFlags & Node::kSave_Flag) {
    151                 if (fCurrentNode != ancestor) { fCanvas->restore(); }
    152                 if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); }
    153             }
    154             fCurrentNode = ancestor;
    155         }
    156 
    157         // If we're not at the target node yet, we'll need to return an offset to make the caller
    158         // apply the next clip or saveLayer.
    159         if (fCurrentNode != targetNode) {
    160             if (fCurrentMatrix != fNodes.top()->fMatrix) {
    161                 fCurrentMatrix = fNodes.top()->fMatrix;
    162                 SkMatrix tmp = *fNodes.top()->fMatrix;
    163                 tmp.postConcat(fPlaybackMatrix);
    164                 fCanvas->setMatrix(tmp);
    165             }
    166             uint32_t offset = fNodes.top()->fOffset;
    167             fCurrentNode = fNodes.top();
    168             fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
    169             fNodes.pop();
    170             return offset;
    171         }
    172     }
    173 
    174     // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
    175     // for the draw, and return its offset.
    176 
    177     if (fCurrentMatrix != draw->fMatrix) {
    178         SkMatrix tmp = *draw->fMatrix;
    179         tmp.postConcat(fPlaybackMatrix);
    180         fCanvas->setMatrix(tmp);
    181         fCurrentMatrix = draw->fMatrix;
    182     }
    183 
    184     ++fPlaybackIndex;
    185     return draw->fOffset;
    186 }
    187