diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgnode.cpp')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgnode.cpp | 1264 |
1 files changed, 1264 insertions, 0 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp new file mode 100644 index 0000000000..2d6828323c --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -0,0 +1,1264 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnode.h" +#include "qsgrenderer_p.h" +#include "qsgnodeupdater_p.h" +#include "qsgmaterial.h" + +#include "limits.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +static int qt_node_count = 0; + +static void qt_print_node_count() +{ + qDebug("Number of leaked nodes: %i", qt_node_count); + qt_node_count = -1; +} +#endif + +/*! + \class QSGNode + \brief The QSGNode class is the base class for all nodes in the scene graph. + + \inmodule QtQuick + + The QSGNode class can be used as a child container. Children are added with + the appendChildNode(), prependChildNode(), insertChildNodeBefore() and + insertChildNodeAfter(). Ordering of nodes is important as geometry nodes + will be rendered in the order they are added to the scene graph. + Actually, the scene may reorder nodes freely, but the resulting visual + order is still guaranteed. + + If nodes change every frame, the preprocess() function can be used to + apply changes to a node for every frame its rendered. The use of preprocess() + must be explicitly enabled by setting the QSGNode::UsePreprocess flag + on the node. + + The virtual isSubtreeBlocked() function can be used to disable a subtree all + together. Nodes in a blocked subtree will not be preprocessed() and not + rendered. + + Anything related to QSGNode should happen on the scene graph rendering thread. + */ + +QSGNode::QSGNode() + : m_parent(0) + , m_type(BasicNodeType) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +QSGNode::QSGNode(NodeType type) + : m_parent(0) + , m_type(type) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(type == GeometryNodeType ? 1 : 0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +void QSGNode::init() +{ +#ifndef QT_NO_DEBUG + ++qt_node_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_node_count); + atexit_registered = true; + } +#endif +} + +QSGNode::~QSGNode() +{ +#ifndef QT_NO_DEBUG + --qt_node_count; + if (qt_node_count < 0) + qDebug("Node destroyed after qt_print_node_count() was called."); +#endif + destroy(); +} + + +/*! + \fn void QSGNode::preprocess() + + Override this function to do processing on the node before it is rendered. + + Preprocessing needs to be explicitly enabled by setting the flag + QSGNode::UsePreprocess. The flag needs to be set before the node is added + to the scene graph and will cause the preprocess() function to be called + for every frame the node is rendered. + + The preprocess function is called before the update pass that propegates + opacity and transformations through the scene graph. That means that + functions like QSGOpacityNode::combinedOpacity() and + QSGTransformNode::combinedMatrix() will not contain up-to-date values. + If such values are changed during the preprocess, these changes will be + propegated through the scene graph before it is rendered. + + \warning Beware of deleting nodes while they are being preprocessed. It is + possible, with a small performance hit, to delete a single node during its + own preprocess call. Deleting a subtree which has nodes that also use + preprocessing may result in a segmentation fault. This is done for + performance reasons. + */ + + + + +/*! + Returns whether this node and its subtree is available for use. + + Blocked subtrees will not get their dirty states updated and they + will not be rendered. + + The QSGOpacityNode will return a blocked subtree when accumulated opacity + is 0, for instance. + */ + +bool QSGNode::isSubtreeBlocked() const +{ + return m_subtreeGeometryCount == 0; +} + +/*! + \internal + Detaches the node from the scene graph and deletes any children it owns. + + This function is called from QSGNode's and QSGRootNode's destructor. It + should not be called explicitly in user code. QSGRootNode needs to call + destroy() because destroy() calls removeChildNode() which in turn calls + markDirty() which type-casts the node to QSGRootNode. This type-cast is not + valid at the time QSGNode's destructor is called because the node will + already be partially destroyed at that point. +*/ + +void QSGNode::destroy() +{ + if (m_parent) { + m_parent->removeChildNode(this); + Q_ASSERT(m_parent == 0); + } + while (m_firstChild) { + QSGNode *child = m_firstChild; + removeChildNode(child); + Q_ASSERT(child->m_parent == 0); + if (child->flags() & OwnedByParent) + delete child; + } + + Q_ASSERT(m_firstChild == 0 && m_lastChild == 0); +} + + +/*! + Prepends \a node to this node's the list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::prependChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_firstChild) + m_firstChild->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = m_firstChild; + m_firstChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +/*! + Appends \a node to this node's list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::appendChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_lastChild) + m_lastChild->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = m_lastChild; + m_lastChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children before the node specified with \a before. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent"); + Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *previous = before->m_previousSibling; + if (previous) + previous->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = previous; + node->m_nextSibling = before; + before->m_previousSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children after the node specified with \a after. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent"); + Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *next = after->m_nextSibling; + if (next) + next->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = next; + node->m_previousSibling = after; + after->m_nextSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Removes \a node from this node's list of children. + */ + +void QSGNode::removeChildNode(QSGNode *node) +{ + //Q_ASSERT(m_children.contains(node)); + Q_ASSERT(node->parent() == this); + + QSGNode *previous = node->m_previousSibling; + QSGNode *next = node->m_nextSibling; + if (previous) + previous->m_nextSibling = next; + else + m_firstChild = next; + if (next) + next->m_previousSibling = previous; + else + m_lastChild = previous; + node->m_previousSibling = 0; + node->m_nextSibling = 0; + + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; +} + + +/*! + Removes all child nodes from this node's list of children. + */ + +void QSGNode::removeAllChildNodes() +{ + while (m_firstChild) { + QSGNode *node = m_firstChild; + m_firstChild = node->m_nextSibling; + node->m_nextSibling = 0; + if (m_firstChild) + m_firstChild->m_previousSibling = 0; + else + m_lastChild = 0; + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; + } +} + + +int QSGNode::childCount() const +{ + int count = 0; + QSGNode *n = m_firstChild; + while (n) { + ++count; + n = n->m_nextSibling; + } + return count; +} + + +QSGNode *QSGNode::childAtIndex(int i) const +{ + QSGNode *n = m_firstChild; + while (i && n) { + --i; + n = n->m_nextSibling; + } + return n; +} + + +/*! + Sets the flag \a f on this node if \a enabled is true; + otherwise clears the flag. + + \sa flags() +*/ + +void QSGNode::setFlag(Flag f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + +/*! + Sets the flags \a f on this node if \a enabled is true; + otherwise clears the flags. + + \sa flags() +*/ + +void QSGNode::setFlags(Flags f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + + +/*! + Marks this node with the states in \a flags as dirty. + + When a node is marked dirty, it recursively mark the parent chain + as dirty and notify all connected renderers that the has dirty states. + */ + +void QSGNode::markDirty(DirtyFlags flags) +{ + m_flags |= (flags & DirtyPropagationMask); + + DirtyFlags subtreeFlags = DirtyFlags((flags & DirtyPropagationMask) << 16); + + int geometryCountDiff = 0; + if (flags & DirtyNodeAdded) + geometryCountDiff += m_subtreeGeometryCount; + if (flags & DirtyNodeRemoved) + geometryCountDiff -= m_subtreeGeometryCount; + + QSGNode *p = m_parent; + while (p) { + p->m_flags |= subtreeFlags; + p->m_subtreeGeometryCount += geometryCountDiff; + if (p->type() == RootNodeType) + static_cast<QSGRootNode *>(p)->notifyNodeChange(this, flags); + p = p->m_parent; + } +} + + + +/*! + \class QSGBasicGeometryNode + \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes + + \inmodule QtQuick + + The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates + shared functionality between the QSGGeometryNode and QSGClipNode classes. + */ + + +/*! + Creates a new basic geometry node. + */ +QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) + : QSGNode(type) + , m_geometry(0) + , m_matrix(0) + , m_clip_list(0) +{ +} + + +/*! + Deletes this QSGBasicGeometryNode. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +QSGBasicGeometryNode::~QSGBasicGeometryNode() +{ + if (flags() & OwnsGeometry) + delete m_geometry; +} + + +/*! + \fn QSGGeometry *QSGBasicGeometryNode::geometry() const + + Returns this node's geometry. + + The geometry is null by default. + */ + + +/*! + Sets the geometry of this node to \a geometry. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) +{ + if (flags() & OwnsGeometry) + delete m_geometry; + m_geometry = geometry; + markDirty(DirtyGeometry); +} + + + +/*! + \class QSGGeometryNode + \brief The QSGGeometryNode class is used for all rendered content in the scene graph. + + \inmodule QtQuick + + The QSGGeometryNode consists of geometry and material. The geometry defines the mesh, + the vertices and their structure, to be drawn. The Material defines how the shape is + filled. + + A geometry node must have both geometry and a normal material before it is added to + the scene graph. + + The geometry node supports two types of materials, the opaqueMaterial and the normal + material. The opaqueMaterial is used when the accumulated scene graph opacity at the + time of rendering is 1. The primary usecase is to special case opaque rendering + to avoid an extra operation in the fragment shader can have significant performance + impact on embedded graphics chips. The opaque material is optional. + + */ + + +/*! + Creates a new geometry node without geometry and material. + */ + +QSGGeometryNode::QSGGeometryNode() + : QSGBasicGeometryNode(GeometryNodeType) + , m_render_order(0) + , m_material(0) + , m_opaque_material(0) + , m_opacity(1) +{ +} + + +/*! + Deletes this geometry node. + + The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and + QSGNode::OwnsGeometry decides weither the geometry node should also + delete the materials and geometry. By default, these flags are disabled. + */ + +QSGGeometryNode::~QSGGeometryNode() +{ + if (flags() & OwnsMaterial) + delete m_material; + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; +} + + + +/*! + \fn int QSGGeometryNode::renderOrder() const + + Returns the render order of this geometry node. + + \internal + */ + + +/*! + Sets the render order of this node to be \a order. + + GeometryNodes are rendered in an order that visually looks like + low order nodes are rendered prior to high order nodes. For opaque + geometry there is little difference as z-testing will handle + the discard, but for translucent objects, the rendering should + normally be specified in the order of back-to-front. + + The default render order is 0. + + \internal + */ +void QSGGeometryNode::setRenderOrder(int order) +{ + m_render_order = order; +} + + + +/*! + Sets the material of this geometry node to \a material. + + Geometry nodes must have a material before they can be added to the + scene graph. + */ +void QSGGeometryNode::setMaterial(QSGMaterial *material) +{ + if (flags() & OwnsMaterial) + delete m_material; + m_material = material; +#ifndef QT_NO_DEBUG + if (m_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + markDirty(DirtyMaterial); +} + + + +/*! + Sets the opaque material of this geometry to \a material. + + The opaque material will be preferred by the renderer over the + default material, as returned by the material() function, if + it is not null and the geometry item has an inherited opacity of + 1. + + The opaqueness refers to scene graph opacity, the material is still + allowed to set QSGMaterial::Blending to true and draw transparent + pixels. + */ +void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material) +{ + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; + m_opaque_material = material; +#ifndef QT_NO_DEBUG + if (m_opaque_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + + markDirty(DirtyMaterial); +} + + + +/*! + Returns the material which should currently be used for geometry node. + + If the inherited opacity of the node is 1 and there is an opaque material + set on this node, it will be returned; otherwise, the default material + will be returned. + + \warning This function requires the scene graph above this item to be + completely free of dirty states, so it can only be called during rendering + + \internal + + \sa setMaterial, setOpaqueMaterial + */ +QSGMaterial *QSGGeometryNode::activeMaterial() const +{ + Q_ASSERT_X(dirtyFlags() == 0, "QSGGeometryNode::activeMaterial()", "function assumes that all dirty states are cleaned up"); + if (m_opaque_material && m_opacity > 0.999) + return m_opaque_material; + return m_material; +} + + +/*! + Sets the inherited opacity of this geometry to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGGeometryNode::setInheritedOpacity(qreal opacity) +{ + Q_ASSERT(opacity >= 0 && opacity <= 1); + m_opacity = opacity; +} + + +/*! + \class QSGClipNode + \brief The QSGClipNode class implements the clipping functionality in the scene graph. + + \inmodule QtQuick + + Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be + accumulated by intersecting all their geometries. The accumulation happens + as part of the rendering. + + Clip nodes must have a geometry before they can be added to the scene graph. + + Clipping is usually implemented by using the stencil buffer. + */ + + + +/*! + Creates a new QSGClipNode without a geometry. + + The clip node must have a geometry before it can be added to the + scene graph. + */ + +QSGClipNode::QSGClipNode() + : QSGBasicGeometryNode(ClipNodeType) +{ +} + + + +/*! + Deletes this QSGClipNode. + + If the flag QSGNode::OwnsGeometry is set, the geometry will also be + deleted. + */ + +QSGClipNode::~QSGClipNode() +{ +} + + + +/*! + \fn bool QSGClipNode::isRectangular() const + + Returns if this clip node has a rectangular clip. + */ + + + +/*! + Sets whether this clip node has a rectangular clip to \a rectHint. + + This is an optimization hint which means that the renderer can + use scissoring instead of stencil, which is significnatly faster. + + When this hint is and it is applicable, the clip region will be + generated from clipRect() rather than geometry(). + */ + +void QSGClipNode::setIsRectangular(bool rectHint) +{ + m_is_rectangular = rectHint; +} + + + +/*! + \fn void QSGClipNode::clipRect() const + + Returns the clip rect of this node. + */ + + +/*! + Sets the clip rect of this clip node to \a rect. + + When a rectangular clip is set in combination with setIsRectangular + the renderer may in some cases use a more optimal clip method. + */ +void QSGClipNode::setClipRect(const QRectF &rect) +{ + m_clip_rect = rect; +} + + +/*! + \class QSGTransformNode + \brief The QSGTransformNode class implements transformations in the scene graph + + \inmodule QtQuick + + Transformations apply the node's subtree and can be nested. Multiple transform nodes + will be accumulated by intersecting all their matrices. The accumulation happens + as part of the rendering. + + The transform nodes implement a 4x4 matrix which in theory supports full 3D + transformations. However, because the renderer optimizes for 2D use-cases rather + than 3D use-cases, rendering a scene with full 3D transformations needs to + be done with some care. + */ + +QSGTransformNode::QSGTransformNode() + : QSGNode(TransformNodeType) +{ +} + + + +/*! + Deletes this transform node. + */ + +QSGTransformNode::~QSGTransformNode() +{ +} + + + +/*! + \fn QMatrix4x4 QSGTransformNode::matrix() const + + Returns this transform node's matrix. + */ + + + +/*! + Sets this transform node's matrix to \a matrix. + */ + +void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix) +{ + m_matrix = matrix; + markDirty(DirtyMatrix); +} + + +/*! + Sets the combined matrix of this matrix to \a transform. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix) +{ + m_combined_matrix = matrix; +} + + + +/*! + \class QSGRootNode + \brief The QSGRootNode is the toplevel root of any scene graph. + + The root node is used to attach a scene graph to a renderer. + + \internal + */ + + + +/*! + \fn QSGRootNode::QSGRootNode() + + Creates a new root node. + */ + +QSGRootNode::QSGRootNode() + : QSGNode(RootNodeType) +{ +} + + +/*! + Deletes the root node. + + When a root node is deleted it removes itself from all of renderers + that are referencing it. + */ + +QSGRootNode::~QSGRootNode() +{ + while (!m_renderers.isEmpty()) + m_renderers.last()->setRootNode(0); + destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. +} + + + +/*! + Called to notify all renderers that \a node has been marked as dirty + with \a flags. + */ + +void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyFlags flags) +{ + for (int i=0; i<m_renderers.size(); ++i) { + m_renderers.at(i)->nodeChanged(node, flags); + } +} + + + +/*! + \class QSGOpacityNode + \brief The QSGOpacityNode class is used to change opacity of nodes. + + \inmodule QtQuick + + Opacity applies to its subtree and can be nested. Multiple opacity nodes + will be accumulated by multiplying their opacity. The accumulation happens + as part of the rendering. + + When nested opacity gets below a certain threshold, the subtree might + be marked as blocked, causing isSubtreeBlocked() to return true. This + is done for performance reasons. + + */ + + + +/*! + Constructs an opacity node with a default opacity of 1. + + Opacity accumulates downwards in the scene graph so a node with two + QSGOpacityNode instances above it, both with opacity of 0.5, will have + effective opacity of 0.25. + + The default opacity of nodes is 1. + */ +QSGOpacityNode::QSGOpacityNode() + : QSGNode(OpacityNodeType) + , m_opacity(1) + , m_combined_opacity(1) +{ +} + + + +/*! + Deletes the opacity node. + */ + +QSGOpacityNode::~QSGOpacityNode() +{ +} + + + +/*! + \fn qreal QSGOpacityNode::opacity() const + + Returns this opacity node's opacity. + */ + + + +/*! + Sets the opacity of this node to \a opacity. + + Before rendering the graph, the renderer will do an update pass + over the subtree to propegate the opacity to its children. + + The value will be bounded to the range 0 to 1. + */ + +void QSGOpacityNode::setOpacity(qreal opacity) +{ + opacity = qBound<qreal>(0, opacity, 1); + if (m_opacity == opacity) + return; + m_opacity = opacity; + markDirty(DirtyOpacity); +} + + + +/*! + \fn qreal QSGOpacityNode::combinedOpacity() const + + Returns this node's accumulated opacity. + + This vaule is calculated during rendering and only stored + in the opacity node temporarily. + + \internal + */ + + + +/*! + Sets the combined opacity of this node to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ + +void QSGOpacityNode::setCombinedOpacity(qreal opacity) +{ + m_combined_opacity = opacity; +} + + + +/*! + For performance reasons, we block the subtree when the opacity + is below a certain threshold. + + \internal + */ + +bool QSGOpacityNode::isSubtreeBlocked() const +{ + return QSGNode::isSubtreeBlocked() || m_opacity < 0.001; +} + + +/*! + \class QSGNodeVisitor + \brief The QSGNodeVisitor class is a helper class for traversing the scene graph. + + \internal + */ + +QSGNodeVisitor::~QSGNodeVisitor() +{ + +} + + +void QSGNodeVisitor::visitNode(QSGNode *n) +{ + switch (n->type()) { + case QSGNode::TransformNodeType: { + QSGTransformNode *t = static_cast<QSGTransformNode *>(n); + enterTransformNode(t); + visitChildren(t); + leaveTransformNode(t); + break; } + case QSGNode::GeometryNodeType: { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); + enterGeometryNode(g); + visitChildren(g); + leaveGeometryNode(g); + break; } + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode *>(n); + enterClipNode(c); + visitChildren(c); + leaveClipNode(c); + break; } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); + enterOpacityNode(o); + visitChildren(o); + leaveOpacityNode(o); + break; } + default: + visitChildren(n); + break; + } +} + +void QSGNodeVisitor::visitChildren(QSGNode *n) +{ + for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) + visitNode(c); +} + + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QSGGeometryNode *n) +{ + if (!n) { + d << "QSGGeometryNode(null)"; + return d; + } + d << "QSGGeometryNode(" << hex << (void *) n << dec; + + const QSGGeometry *g = n->geometry(); + + if (!g) { + d << "no geometry"; + } else { + + switch (g->drawingMode()) { + case GL_TRIANGLE_STRIP: d << "strip"; break; + case GL_TRIANGLE_FAN: d << "fan"; break; + case GL_TRIANGLES: d << "triangles"; break; + default: break; + } + + d << g->vertexCount(); + + if (g->attributeCount() > 0 && g->attributes()->type == GL_FLOAT) { + float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10; + int stride = g->sizeOfVertex(); + for (int i = 0; i < g->vertexCount(); ++i) { + float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0]; + float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1]; + + x1 = qMin(x1, x); + x2 = qMax(x2, x); + y1 = qMin(y1, y); + y2 = qMax(y2, y); + } + + d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2; + } + } + + d << "order=" << n->renderOrder(); + if (n->material()) + d << "effect=" << n->material() << "type=" << n->material()->type(); + + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + return d; +} + +QDebug operator<<(QDebug d, const QSGClipNode *n) +{ + if (!n) { + d << "QSGClipNode(null)"; + return d; + } + d << "QSGClipNode(" << hex << (void *) n << dec; + + if (n->childCount()) + d << "children=" << n->childCount(); + + d << "is rect?" << (n->isRectangular() ? "yes" : "no"); + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + return d; +} + +QDebug operator<<(QDebug d, const QSGTransformNode *n) +{ + if (!n) { + d << "QSGTransformNode(null)"; + return d; + } + const QMatrix4x4 m = n->matrix(); + d << "QSGTransformNode("; + d << hex << (void *) n << dec; + if (m.isIdentity()) + d << "identity"; + else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1) + d << "translate" << m(0, 3) << m(1, 3) << m(2, 3); + else + d << "det=" << n->matrix().determinant(); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + d << ")"; + return d; +} + +QDebug operator<<(QDebug d, const QSGOpacityNode *n) +{ + if (!n) { + d << "QSGOpacityNode(null)"; + return d; + } + d << "QSGOpacityNode("; + d << hex << (void *) n << dec; + d << "opacity=" << n->opacity() + << "combined=" << n->combinedOpacity() + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + d << ")"; + return d; +} + + +QDebug operator<<(QDebug d, const QSGRootNode *n) +{ + if (!n) { + d << "QSGRootNode(null)"; + return d; + } + d << "QSGRootNode" << hex << (void *) n << "dirty=" << (int) n->dirtyFlags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + return d; +} + + + +QDebug operator<<(QDebug d, const QSGNode *n) +{ + if (!n) { + d << "QSGNode(null)"; + return d; + } + switch (n->type()) { + case QSGNode::GeometryNodeType: + d << static_cast<const QSGGeometryNode *>(n); + break; + case QSGNode::TransformNodeType: + d << static_cast<const QSGTransformNode *>(n); + break; + case QSGNode::ClipNodeType: + d << static_cast<const QSGClipNode *>(n); + break; + case QSGNode::RootNodeType: + d << static_cast<const QSGRootNode *>(n); + break; + case QSGNode::OpacityNodeType: + d << static_cast<const QSGOpacityNode *>(n); + break; + default: + d << "QSGNode(" << hex << (void *) n << dec + << "dirty=" << hex << (int) n->dirtyFlags() + << "flags=" << (int) n->flags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + break; + } + return d; +} + +#endif + +QT_END_NAMESPACE |