blob: 2abed196588693535f5597d67e103aae14d80d91 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkPictureStateTree.h"
#include "SkCanvas.h"
SK_DEFINE_INST_COUNT(SkPictureStateTree)
SkPictureStateTree::SkPictureStateTree()
: fAlloc(2048)
, fRoot(NULL)
, fStateStack(sizeof(Draw), 16) {
SkMatrix* identity = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
identity->reset();
fRoot = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
fRoot->fParent = NULL;
fRoot->fMatrix = identity;
fRoot->fFlags = Node::kSave_Flag;
fRoot->fOffset = 0;
fRoot->fLevel = 0;
fCurrentState.fNode = fRoot;
fCurrentState.fMatrix = identity;
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
}
SkPictureStateTree::~SkPictureStateTree() {
}
SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) {
Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
*draw = fCurrentState;
draw->fOffset = offset;
return draw;
}
void SkPictureStateTree::appendSave() {
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
fCurrentState.fNode->fFlags |= Node::kSave_Flag;
}
void SkPictureStateTree::appendSaveLayer(uint32_t offset) {
*static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
this->appendNode(offset);
fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
}
void SkPictureStateTree::appendRestore() {
fCurrentState = *static_cast<Draw*>(fStateStack.back());
fStateStack.pop_back();
}
void SkPictureStateTree::appendTransform(const SkMatrix& trans) {
SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
*m = trans;
fCurrentState.fMatrix = m;
}
void SkPictureStateTree::appendClip(uint32_t offset) {
this->appendNode(offset);
}
SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
SkCanvas* canvas) {
return Iterator(draws, canvas, fRoot);
}
void SkPictureStateTree::appendNode(uint32_t offset) {
Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
n->fOffset = offset;
n->fFlags = 0;
n->fParent = fCurrentState.fNode;
n->fLevel = fCurrentState.fNode->fLevel + 1;
n->fMatrix = fCurrentState.fMatrix;
fCurrentState.fNode = n;
}
SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
: fDraws(&draws)
, fCanvas(canvas)
, fCurrentNode(root)
, fPlaybackMatrix(canvas->getTotalMatrix())
, fCurrentMatrix(NULL)
, fPlaybackIndex(0)
, fSave(false)
, fValid(true) {
}
uint32_t SkPictureStateTree::Iterator::draw() {
SkASSERT(this->isValid());
if (fPlaybackIndex >= fDraws->count()) {
// restore back to where we started
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
fCurrentNode = fCurrentNode->fParent;
while (NULL != fCurrentNode) {
if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
fCurrentNode = fCurrentNode->fParent;
}
fCanvas->setMatrix(fPlaybackMatrix);
return kDrawComplete;
}
Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
Node* targetNode = draw->fNode;
if (fSave) {
fCanvas->save(SkCanvas::kClip_SaveFlag);
fSave = false;
}
if (fCurrentNode != targetNode) {
// If we're not at the target and we don't have a list of nodes to get there, we need to
// figure out the path from our current node, to the target
if (fNodes.count() == 0) {
// Trace back up to a common ancestor, restoring to get our current state to match that
// of the ancestor, and saving a list of nodes whose state we need to apply to get to
// the target (we can restore up to the ancestor immediately, but we'll need to return
// an offset for each node on the way down to the target, to apply the desired clips and
// saveLayers, so it may take several draw() calls before the next draw actually occurs)
Node* tmp = fCurrentNode;
Node* ancestor = targetNode;
while (tmp != ancestor) {
uint16_t currentLevel = tmp->fLevel;
uint16_t targetLevel = ancestor->fLevel;
if (currentLevel >= targetLevel) {
if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
tmp = tmp->fParent;
}
if (currentLevel <= targetLevel) {
fNodes.push(ancestor);
ancestor = ancestor->fParent;
}
}
if (ancestor->fFlags & Node::kSave_Flag) {
if (fCurrentNode != ancestor) { fCanvas->restore(); }
if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); }
}
fCurrentNode = ancestor;
}
// If we're not at the target node yet, we'll need to return an offset to make the caller
// apply the next clip or saveLayer.
if (fCurrentNode != targetNode) {
if (fCurrentMatrix != fNodes.top()->fMatrix) {
fCurrentMatrix = fNodes.top()->fMatrix;
SkMatrix tmp = *fNodes.top()->fMatrix;
tmp.postConcat(fPlaybackMatrix);
fCanvas->setMatrix(tmp);
}
uint32_t offset = fNodes.top()->fOffset;
fCurrentNode = fNodes.top();
fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
fNodes.pop();
return offset;
}
}
// If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
// for the draw, and return its offset.
if (fCurrentMatrix != draw->fMatrix) {
SkMatrix tmp = *draw->fMatrix;
tmp.postConcat(fPlaybackMatrix);
fCanvas->setMatrix(tmp);
fCurrentMatrix = draw->fMatrix;
}
++fPlaybackIndex;
return draw->fOffset;
}