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 SK_DEFINE_INST_COUNT(SkPictureStateTree)
     13 
     14 SkPictureStateTree::SkPictureStateTree()
     15     : fAlloc(2048)
     16     , fRoot(NULL)
     17     , fLastRestoredNode(NULL)
     18     , fStateStack(sizeof(Draw), 16) {
     19     SkMatrix* identity = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
     20     identity->reset();
     21     fRoot = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
     22     fRoot->fParent = NULL;
     23     fRoot->fMatrix = identity;
     24     fRoot->fFlags = Node::kSave_Flag;
     25     fRoot->fOffset = 0;
     26     fRoot->fLevel = 0;
     27     fCurrentState.fNode = fRoot;
     28     fCurrentState.fMatrix = identity;
     29     *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
     30 }
     31 
     32 SkPictureStateTree::~SkPictureStateTree() {
     33 }
     34 
     35 SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) {
     36     Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
     37     *draw = fCurrentState;
     38     draw->fOffset = offset;
     39     return draw;
     40 }
     41 
     42 void SkPictureStateTree::appendSave() {
     43     *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
     44     fCurrentState.fNode->fFlags |= Node::kSave_Flag;
     45 }
     46 
     47 void SkPictureStateTree::appendSaveLayer(uint32_t offset) {
     48     *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
     49     this->appendNode(offset);
     50     fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
     51 }
     52 
     53 void SkPictureStateTree::saveCollapsed() {
     54     SkASSERT(NULL != fLastRestoredNode);
     55     SkASSERT(SkToBool(fLastRestoredNode->fFlags & \
     56         (Node::kSaveLayer_Flag | Node::kSave_Flag)));
     57     SkASSERT(fLastRestoredNode->fParent == fCurrentState.fNode);
     58     // The structure of the tree is not modified here. We just turn off
     59     // the save or saveLayer flag to prevent the iterator from making state
     60     // changing calls on the playback canvas when traversing a save or
     61     // saveLayerNode node.
     62     fLastRestoredNode->fFlags = 0;
     63 }
     64 
     65 void SkPictureStateTree::appendRestore() {
     66     fLastRestoredNode = fCurrentState.fNode;
     67     fCurrentState = *static_cast<Draw*>(fStateStack.back());
     68     fStateStack.pop_back();
     69 }
     70 
     71 void SkPictureStateTree::appendTransform(const SkMatrix& trans) {
     72     SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
     73     *m = trans;
     74     fCurrentState.fMatrix = m;
     75 }
     76 
     77 void SkPictureStateTree::appendClip(uint32_t offset) {
     78     this->appendNode(offset);
     79 }
     80 
     81 SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
     82                                                              SkCanvas* canvas) {
     83     return Iterator(draws, canvas, fRoot);
     84 }
     85 
     86 void SkPictureStateTree::appendNode(uint32_t offset) {
     87     Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
     88     n->fOffset = offset;
     89     n->fFlags = 0;
     90     n->fParent = fCurrentState.fNode;
     91     n->fLevel = fCurrentState.fNode->fLevel + 1;
     92     n->fMatrix = fCurrentState.fMatrix;
     93     fCurrentState.fNode = n;
     94 }
     95 
     96 SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
     97     : fDraws(&draws)
     98     , fCanvas(canvas)
     99     , fCurrentNode(root)
    100     , fPlaybackMatrix(canvas->getTotalMatrix())
    101     , fCurrentMatrix(NULL)
    102     , fPlaybackIndex(0)
    103     , fSave(false)
    104     , fValid(true) {
    105 }
    106 
    107 uint32_t SkPictureStateTree::Iterator::draw() {
    108     SkASSERT(this->isValid());
    109     if (fPlaybackIndex >= fDraws->count()) {
    110         // restore back to where we started
    111         if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    112         fCurrentNode = fCurrentNode->fParent;
    113         while (NULL != fCurrentNode) {
    114             if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
    115             if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    116             fCurrentNode = fCurrentNode->fParent;
    117         }
    118         fCanvas->setMatrix(fPlaybackMatrix);
    119         return kDrawComplete;
    120     }
    121 
    122     Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
    123     Node* targetNode = draw->fNode;
    124 
    125     if (fSave) {
    126         fCanvas->save(SkCanvas::kClip_SaveFlag);
    127         fSave = false;
    128     }
    129 
    130     if (fCurrentNode != targetNode) {
    131         // If we're not at the target and we don't have a list of nodes to get there, we need to
    132         // figure out the path from our current node, to the target
    133         if (fNodes.count() == 0) {
    134             // Trace back up to a common ancestor, restoring to get our current state to match that
    135             // of the ancestor, and saving a list of nodes whose state we need to apply to get to
    136             // the target (we can restore up to the ancestor immediately, but we'll need to return
    137             // an offset for each node on the way down to the target, to apply the desired clips and
    138             // saveLayers, so it may take several draw() calls before the next draw actually occurs)
    139             Node* tmp = fCurrentNode;
    140             Node* ancestor = targetNode;
    141             while (tmp != ancestor) {
    142                 uint16_t currentLevel = tmp->fLevel;
    143                 uint16_t targetLevel = ancestor->fLevel;
    144                 if (currentLevel >= targetLevel) {
    145                     if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
    146                     if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
    147                     tmp = tmp->fParent;
    148                 }
    149                 if (currentLevel <= targetLevel) {
    150                     fNodes.push(ancestor);
    151                     ancestor = ancestor->fParent;
    152                 }
    153             }
    154 
    155             if (ancestor->fFlags & Node::kSave_Flag) {
    156                 if (fCurrentNode != ancestor) { fCanvas->restore(); }
    157                 if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); }
    158             }
    159             fCurrentNode = ancestor;
    160         }
    161 
    162         // If we're not at the target node yet, we'll need to return an offset to make the caller
    163         // apply the next clip or saveLayer.
    164         if (fCurrentNode != targetNode) {
    165             if (fCurrentMatrix != fNodes.top()->fMatrix) {
    166                 fCurrentMatrix = fNodes.top()->fMatrix;
    167                 SkMatrix tmp = *fNodes.top()->fMatrix;
    168                 tmp.postConcat(fPlaybackMatrix);
    169                 fCanvas->setMatrix(tmp);
    170             }
    171             uint32_t offset = fNodes.top()->fOffset;
    172             fCurrentNode = fNodes.top();
    173             fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
    174             fNodes.pop();
    175             return offset;
    176         }
    177     }
    178 
    179     // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
    180     // for the draw, and return its offset.
    181 
    182     if (fCurrentMatrix != draw->fMatrix) {
    183         SkMatrix tmp = *draw->fMatrix;
    184         tmp.postConcat(fPlaybackMatrix);
    185         fCanvas->setMatrix(tmp);
    186         fCurrentMatrix = draw->fMatrix;
    187     }
    188 
    189     ++fPlaybackIndex;
    190     return draw->fOffset;
    191 }
    192