aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/adaptations
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/adaptations')
-rw-r--r--src/quick/scenegraph/adaptations/adaptations.pri1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp328
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h111
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp92
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h77
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp224
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h114
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp120
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp501
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h147
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp451
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h103
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp261
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h121
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp231
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h132
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp116
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h76
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp88
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h79
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp196
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h145
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp434
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h157
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp309
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h143
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp165
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp176
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h98
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp264
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h105
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp139
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h96
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp991
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h118
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri48
38 files changed, 7125 insertions, 0 deletions
diff --git a/src/quick/scenegraph/adaptations/adaptations.pri b/src/quick/scenegraph/adaptations/adaptations.pri
new file mode 100644
index 0000000000..40fa739e15
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/adaptations.pri
@@ -0,0 +1 @@
+include(software/software.pri)
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
new file mode 100644
index 0000000000..2ff180ea99
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+#include "qsgsoftwarerenderlistbuilder_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/QWindow>
+#include <QtQuick/QSGSimpleRectNode>
+
+Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context)
+ : QSGRenderer(context)
+ , m_background(new QSGSimpleRectNode)
+ , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this))
+{
+ // Setup special background node
+ auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background);
+ addNodeMapping(m_background, backgroundRenderable);
+}
+
+QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
+{
+ // Cleanup RenderableNodes
+ delete m_background;
+
+ qDeleteAll(m_nodes);
+
+ delete m_nodeUpdater;
+}
+
+QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const
+{
+ return m_nodes.value(node, nullptr);
+}
+
+void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
+{
+ m_nodes.insert(node, renderableNode);
+}
+
+void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node)
+{
+ m_renderableNodes.append(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ if (state & QSGNode::DirtyGeometry) {
+ nodeGeometryUpdated(node);
+ }
+ if (state & QSGNode::DirtyMaterial) {
+ nodeMaterialUpdated(node);
+ }
+ if (state & QSGNode::DirtyMatrix) {
+ nodeMatrixUpdated(node);
+ }
+ if (state & QSGNode::DirtyNodeAdded) {
+ nodeAdded(node);
+ }
+ if (state & QSGNode::DirtyNodeRemoved) {
+ nodeRemoved(node);
+ }
+ if (state & QSGNode::DirtyOpacity) {
+ nodeOpacityUpdated(node);
+ }
+ if (state & QSGNode::DirtySubtreeBlocked) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ if (state & QSGNode::DirtyForceUpdate) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ QSGRenderer::nodeChanged(node, state);
+}
+
+QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter)
+{
+ QRegion dirtyRegion;
+ // If there are no nodes, do nothing
+ if (m_renderableNodes.isEmpty())
+ return dirtyRegion;
+
+ auto iterator = m_renderableNodes.begin();
+ // First node is the background and needs to painted without blending
+ auto backgroundNode = *iterator;
+ dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true);
+ iterator++;
+
+ for (; iterator != m_renderableNodes.end(); ++iterator) {
+ auto node = *iterator;
+ dirtyRegion += node->renderNode(painter);
+ }
+
+ return dirtyRegion;
+}
+
+void QSGAbstractSoftwareRenderer::buildRenderList()
+{
+ // Clear the previous renderlist
+ m_renderableNodes.clear();
+ // Add the background renderable (always first)
+ m_renderableNodes.append(renderableNode(m_background));
+ // Build the renderlist
+ QSGSoftwareRenderListBuilder(this).visitChildren(rootNode());
+}
+
+QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
+{
+ // Iterate through the renderlist from front to back
+ // Objective is to update the dirty status and rects.
+ for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
+ auto node = *i;
+ if (!m_dirtyRegion.isEmpty()) {
+ // See if the current dirty regions apply to the current node
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ if (!m_obscuredRegion.isEmpty()) {
+ // Don't try to paint things that are covered by opaque objects
+ node->subtractDirtyRegion(m_obscuredRegion);
+ }
+
+ // Keep up with obscured regions
+ if (node->isOpaque()) {
+ m_obscuredRegion += QRegion(node->boundingRect());
+ }
+
+ if (node->isDirty()) {
+ // Don't paint things outside of the rendering area
+ if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) {
+ // Some part(s) of node is(are) outside of the rendering area
+ QRegion renderArea(m_background->rect().toRect());
+ QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea);
+ if (!outsideRegions.isEmpty())
+ node->subtractDirtyRegion(outsideRegions);
+ }
+
+ // Get the dirty region's to pass to the next nodes
+ if (node->isOpaque()) {
+ // if isOpaque, subtract node's dirty rect from m_dirtyRegion
+ m_dirtyRegion -= node->dirtyRegion();
+ } else {
+ // if isAlpha, add node's dirty rect to m_dirtyRegion
+ m_dirtyRegion += node->dirtyRegion();
+ }
+ // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion
+ QRegion prevDirty = node->previousDirtyRegion();
+ if (!prevDirty.isNull())
+ m_dirtyRegion += prevDirty;
+ }
+ }
+
+ // Empty dirtyRegion (for second pass)
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+
+ // Iterate through the renderlist from back to front
+ // Objective is to make sure all non-opaque items are painted when an item under them is dirty
+ for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
+ auto node = *j;
+
+ if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) {
+ // Only blended nodes need to be updated
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ m_dirtyRegion += node->dirtyRegion();
+ }
+
+ QRegion updateRegion = m_dirtyRegion;
+
+ // Empty dirtyRegion
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+
+ return updateRegion;
+}
+
+void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
+{
+ if (m_background->color() == color)
+ return;
+ m_background->setColor(color);
+ renderableNode(m_background)->markMaterialDirty();
+}
+
+void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size)
+{
+ if (m_background->rect().size().toSize() == size)
+ return;
+ m_background->setRect(0.0f, 0.0f, size.width(), size.height());
+ renderableNode(m_background)->markGeometryDirty();
+ // Invalidate the whole scene when the background is resized
+ markDirty();
+}
+
+QColor QSGAbstractSoftwareRenderer::backgroundColor()
+{
+ return m_background->color();
+}
+
+QSize QSGAbstractSoftwareRenderer::backgroundSize()
+{
+ return m_background->rect().size().toSize();
+}
+
+void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeAdded" << (void*)node;
+
+ m_nodeUpdater->updateNodes(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeRemoved" << (void*)node;
+
+ auto renderable = renderableNode(node);
+ // remove mapping
+ if (renderable != nullptr) {
+ // Need to mark this region dirty in the other nodes
+ QRegion dirtyRegion = renderable->previousDirtyRegion(true);
+ if (dirtyRegion.isEmpty())
+ dirtyRegion = renderable->boundingRect();
+ m_dirtyRegion += dirtyRegion;
+ m_nodes.remove(node);
+ delete renderable;
+ }
+
+ // Remove all children nodes as well
+ for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
+ nodeRemoved(child);
+ }
+
+ m_nodeUpdater->updateNodes(node, true);
+}
+
+void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeGeometryUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markGeometryDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markMaterialDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeOpacityUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+void QSGAbstractSoftwareRenderer::markDirty()
+{
+ m_dirtyRegion = QRegion(m_background->rect().toRect());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
new file mode 100644
index 0000000000..905577b92a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGABSTRACTSOFTWARERENDERER_H
+#define QSGABSTRACTSOFTWARERENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderer_p.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QLinkedList>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSimpleRectNode;
+
+class QSGSoftwareRenderableNode;
+class QSGSoftwareRenderableNodeUpdater;
+
+class QSGAbstractSoftwareRenderer : public QSGRenderer
+{
+public:
+ QSGAbstractSoftwareRenderer(QSGRenderContext *context);
+ virtual ~QSGAbstractSoftwareRenderer();
+
+ QSGSoftwareRenderableNode *renderableNode(QSGNode *node) const;
+ void addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode);
+ void appendRenderableNode(QSGSoftwareRenderableNode *node);
+
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+
+ void markDirty();
+
+protected:
+ QRegion renderNodes(QPainter *painter);
+ void buildRenderList();
+ QRegion optimizeRenderList();
+
+ void setBackgroundColor(const QColor &color);
+ void setBackgroundSize(const QSize &size);
+ QColor backgroundColor();
+ QSize backgroundSize();
+
+private:
+ void nodeAdded(QSGNode *node);
+ void nodeRemoved(QSGNode *node);
+ void nodeGeometryUpdated(QSGNode *node);
+ void nodeMaterialUpdated(QSGNode *node);
+ void nodeMatrixUpdated(QSGNode *node);
+ void nodeOpacityUpdated(QSGNode *node);
+
+ QHash<QSGNode*, QSGSoftwareRenderableNode*> m_nodes;
+ QLinkedList<QSGSoftwareRenderableNode*> m_renderableNodes;
+
+ QSGSimpleRectNode *m_background;
+
+ QRegion m_dirtyRegion;
+ QRegion m_obscuredRegion;
+
+ QSGSoftwareRenderableNodeUpdater *m_nodeUpdater;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGABSTRACTSOFTWARERENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
new file mode 100644
index 0000000000..8ad9b50b09
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareadaptation_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderloop_p.h"
+#include "qsgsoftwarethreadedrenderloop_p.h"
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent)
+ : QSGContextPlugin(parent)
+{
+}
+
+QStringList QSGSoftwareAdaptation::keys() const
+{
+ return QStringList() << QLatin1String("software") << QLatin1String("softwarecontext");
+}
+
+QSGContext *QSGSoftwareAdaptation::create(const QString &) const
+{
+ if (!instance)
+ instance = new QSGSoftwareContext();
+ return instance;
+}
+
+QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &) const
+{
+ // Claim we support adaptable shader effects, then return null for the
+ // shader effect node. The result is shader effects not being rendered,
+ // with the application working fine in all other respects.
+ return QSGContextFactoryInterface::SupportsShaderEffectNode;
+}
+
+QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
+{
+ static bool threaded = false;
+ static bool envChecked = false;
+ if (!envChecked) {
+ envChecked = true;
+ threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded");
+ }
+
+ if (threaded)
+ return new QSGSoftwareThreadedRenderLoop;
+
+ return new QSGSoftwareRenderLoop();
+}
+
+QSGSoftwareContext *QSGSoftwareAdaptation::instance = 0;
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h
new file mode 100644
index 0000000000..ffe54b5d4b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PLUGINMAIN_H
+#define PLUGINMAIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontextplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSGRenderLoop;
+class QSGSoftwareContext;
+
+class QSGSoftwareAdaptation : public QSGContextPlugin
+{
+public:
+ QSGSoftwareAdaptation(QObject *parent = 0);
+
+ QStringList keys() const override;
+ QSGContext *create(const QString &key) const override;
+ QSGContextFactoryInterface::Flags flags(const QString &key) const override;
+ QSGRenderLoop *createWindowManager() override;
+private:
+ static QSGSoftwareContext *instance;
+};
+
+QT_END_NAMESPACE
+
+#endif // PLUGINMAIN_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
new file mode 100644
index 0000000000..05d5daa686
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarecontext_p.h"
+
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarelayer_p.h"
+#include "qsgsoftwarerenderer_p.h"
+#if QT_CONFIG(quick_sprite)
+#include "qsgsoftwarespritenode_p.h"
+#endif
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QElapsedTimer>
+
+#include <QtGui/QWindow>
+#include <QtQuick/private/qquickwindow_p.h>
+
+// Used for very high-level info about the renderering and gl context
+// Includes GL_VERSION, type of render loop, atlas size, etc.
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO, "qt.scenegraph.info")
+
+// Used to debug the renderloop logic. Primarily useful for platform integrators
+// and when investigating the render loop logic.
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP, "qt.scenegraph.renderloop")
+
+// GLSL shader compilation
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation")
+
+// polish, animations, sync, render and swap in the render loop
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop")
+
+// Texture uploads and swizzling
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture")
+
+// Glyph preparation (only for distance fields atm)
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
+
+// Timing inside the renderer base class
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderContext::QSGSoftwareRenderContext(QSGContext *ctx)
+ : QSGRenderContext(ctx)
+ , m_initialized(false)
+ , m_activePainter(nullptr)
+{
+}
+
+QSGSoftwareContext::QSGSoftwareContext(QObject *parent)
+ : QSGContext(parent)
+{
+}
+
+QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode()
+{
+ return new QSGSoftwareInternalRectangleNode();
+}
+
+QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode()
+{
+ return new QSGSoftwareInternalImageNode();
+}
+
+QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item)
+{
+ return new QSGSoftwarePainterNode(item);
+}
+
+QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+{
+ Q_UNUSED(rc);
+ Q_UNUSED(preferNativeGlyphNode);
+ return new QSGSoftwareGlyphNode();
+}
+
+QSGLayer *QSGSoftwareContext::createLayer(QSGRenderContext *renderContext)
+{
+ return new QSGSoftwareLayer(renderContext);
+}
+
+QSurfaceFormat QSGSoftwareContext::defaultSurfaceFormat() const
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setRenderableType(QSurfaceFormat::DefaultRenderableType);
+ format.setMajorVersion(0);
+ format.setMinorVersion(0);
+ return format;
+}
+
+void QSGSoftwareRenderContext::initializeIfNeeded()
+{
+ if (m_initialized)
+ return;
+ m_initialized = true;
+ emit initialized();
+}
+
+void QSGSoftwareRenderContext::invalidate()
+{
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+
+QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const
+{
+ return new QSGSoftwarePixmapTexture(image, flags);
+}
+
+QSGRenderer *QSGSoftwareRenderContext::createRenderer()
+{
+ return new QSGSoftwareRenderer(this);
+}
+
+
+void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo)
+{
+ renderer->renderScene(fbo);
+}
+
+int QSGSoftwareRenderContext::maxTextureSize() const
+{
+ return 2048;
+}
+
+QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext)
+{
+ Q_UNUSED(renderContext);
+ return this;
+}
+
+QSGRectangleNode *QSGSoftwareContext::createRectangleNode()
+{
+ return new QSGSoftwareRectangleNode;
+}
+
+QSGImageNode *QSGSoftwareContext::createImageNode()
+{
+ return new QSGSoftwareImageNode;
+}
+
+QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode()
+{
+ return new QSGSoftwareNinePatchNode;
+}
+
+#if QT_CONFIG(quick_sprite)
+QSGSpriteNode *QSGSoftwareContext::createSpriteNode()
+{
+ return new QSGSoftwareSpriteNode;
+}
+#endif
+
+QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const
+{
+ return Software;
+}
+
+QSGRendererInterface::ShaderType QSGSoftwareContext::shaderType() const
+{
+ return UnknownShadingLanguage;
+}
+
+QSGRendererInterface::ShaderCompilationTypes QSGSoftwareContext::shaderCompilationType() const
+{
+ return 0;
+}
+
+QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() const
+{
+ return 0;
+}
+
+void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (resource == Painter && window && window->isSceneGraphInitialized())
+ return static_cast<QSGSoftwareRenderContext *>(QQuickWindowPrivate::get(window)->context)->m_activePainter;
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
new file mode 100644
index 0000000000..1f14717416
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARECONTEXT_H
+#define QSGSOFTWARECONTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include "qsgrendererinterface.h"
+
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP)
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderContext : public QSGRenderContext
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderContext(QSGContext *ctx);
+ void initializeIfNeeded();
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
+ QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override;
+ QSGRenderer *createRenderer() override;
+ int maxTextureSize() const override;
+
+ bool m_initialized;
+ QPainter *m_activePainter;
+};
+
+class QSGSoftwareContext : public QSGContext, public QSGRendererInterface
+{
+ Q_OBJECT
+public:
+ explicit QSGSoftwareContext(QObject *parent = nullptr);
+
+ QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
+ QSGInternalRectangleNode *createInternalRectangleNode() override;
+ QSGInternalImageNode *createInternalImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGNinePatchNode *createNinePatchNode() override;
+#if QT_CONFIG(quick_sprite)
+ QSGSpriteNode *createSpriteNode() override;
+#endif
+
+ GraphicsApi graphicsApi() const override;
+ ShaderType shaderType() const override;
+ ShaderCompilationTypes shaderCompilationType() const override;
+ ShaderSourceTypes shaderSourceType() const override;
+ void *getResource(QQuickWindow *window, Resource resource) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARECONTEXT_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
new file mode 100644
index 0000000000..21f20c66cd
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareglyphnode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareGlyphNode::QSGSoftwareGlyphNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+ , m_style(QQuickText::Normal)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry(&m_geometry);
+}
+
+
+void QSGSoftwareGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ m_position = position;
+ m_glyphRun = glyphs;
+ m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent()));
+}
+
+void QSGSoftwareGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+}
+
+void QSGSoftwareGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ m_style = style;
+}
+
+void QSGSoftwareGlyphNode::setStyleColor(const QColor &color)
+{
+ m_styleColor = color;
+}
+
+QPointF QSGSoftwareGlyphNode::baseLine() const
+{
+ return QPointF();
+}
+
+void QSGSoftwareGlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode)
+{
+}
+
+void QSGSoftwareGlyphNode::update()
+{
+}
+
+void QSGSoftwareGlyphNode::paint(QPainter *painter)
+{
+ painter->setBrush(QBrush());
+ QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent());
+
+ qreal offset = 1.0;
+ if (painter->device()->devicePixelRatio() != 0)
+ offset = 1.0 / painter->device()->devicePixelRatio();
+
+ switch (m_style) {
+ case QQuickText::Normal: break;
+ case QQuickText::Outline:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(offset, 0), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(-offset, 0), m_glyphRun);
+ break;
+ case QQuickText::Raised:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun);
+ break;
+ case QQuickText::Sunken:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun);
+ break;
+ }
+
+ painter->setPen(m_color);
+ painter->drawGlyphRun(pos, m_glyphRun);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h
new file mode 100644
index 0000000000..559b156bf3
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREGLYPHNODE_H
+#define QSGSOFTWAREGLYPHNODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareGlyphNode : public QSGGlyphNode
+{
+public:
+ QSGSoftwareGlyphNode();
+
+ void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
+ void setColor(const QColor &color) override;
+ void setStyle(QQuickText::TextStyle style) override;
+ void setStyleColor(const QColor &color) override;
+ QPointF baseLine() const override;
+ void setPreferredAntialiasingMode(AntialiasingMode) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+
+private:
+ QPointF m_position;
+ QGlyphRun m_glyphRun;
+ QColor m_color;
+ QSGGeometry m_geometry;
+ QQuickText::TextStyle m_style;
+ QColor m_styleColor;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREGLYPHNODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
new file mode 100644
index 0000000000..10291b9cb5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareinternalimagenode_p.h"
+
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwarelayer_p.h"
+#include <QPainter>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGSoftwareHelpers {
+// Helper from widgets/styles/qdrawutil.cpp
+
+static inline QMargins normalizedMargins(const QMargins &m)
+{
+ return QMargins(qMax(m.left(), 0), qMax(m.top(), 0), qMax(m.right(), 0), qMax(m.bottom(), 0));
+}
+
+void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn,
+ const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints)
+{
+ QPainter::PixmapFragment d;
+ d.opacity = 1.0;
+ d.rotation = 0.0;
+
+ QPixmapFragmentsArray opaqueData;
+ QPixmapFragmentsArray translucentData;
+
+ QMargins sourceMargins = normalizedMargins(sourceMarginsIn);
+ QMargins targetMargins = normalizedMargins(targetMarginsIn);
+
+ // source center
+ const int sourceCenterTop = sourceRect.top() + sourceMargins.top();
+ const int sourceCenterLeft = sourceRect.left() + sourceMargins.left();
+ const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1;
+ const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1;
+ const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft;
+ const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop;
+ // target center
+ const int targetCenterTop = targetRect.top() + targetMargins.top();
+ const int targetCenterLeft = targetRect.left() + targetMargins.left();
+ const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1;
+ const int targetCenterRight = targetRect.right() - targetMargins.right() + 1;
+ const int targetCenterWidth = targetCenterRight - targetCenterLeft;
+ const int targetCenterHeight = targetCenterBottom - targetCenterTop;
+
+ QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles
+ QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles
+
+ int columns = 3;
+ int rows = 3;
+ if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0)
+ columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth)));
+ if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0)
+ rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight)));
+
+ xTarget.resize(columns + 1);
+ yTarget.resize(rows + 1);
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL
+ && painter->paintEngine()->type() != QPaintEngine::OpenGL2
+ && oldAA && painter->combinedTransform().type() != QTransform::TxNone) {
+ painter->setRenderHint(QPainter::Antialiasing, false);
+ }
+
+ xTarget[0] = targetRect.left();
+ xTarget[1] = targetCenterLeft;
+ xTarget[columns - 1] = targetCenterRight;
+ xTarget[columns] = targetRect.left() + targetRect.width();
+
+ yTarget[0] = targetRect.top();
+ yTarget[1] = targetCenterTop;
+ yTarget[rows - 1] = targetCenterBottom;
+ yTarget[rows] = targetRect.top() + targetRect.height();
+
+ qreal dx = targetCenterWidth;
+ qreal dy = targetCenterHeight;
+
+ switch (rules.horizontal) {
+ case Qt::StretchTile:
+ dx = targetCenterWidth;
+ break;
+ case Qt::RepeatTile:
+ dx = sourceCenterWidth;
+ break;
+ case Qt::RoundTile:
+ dx = targetCenterWidth / qreal(columns - 2);
+ break;
+ }
+
+ for (int i = 2; i < columns - 1; ++i)
+ xTarget[i] = xTarget[i - 1] + dx;
+
+ switch (rules.vertical) {
+ case Qt::StretchTile:
+ dy = targetCenterHeight;
+ break;
+ case Qt::RepeatTile:
+ dy = sourceCenterHeight;
+ break;
+ case Qt::RoundTile:
+ dy = targetCenterHeight / qreal(rows - 2);
+ break;
+ }
+
+ for (int i = 2; i < rows - 1; ++i)
+ yTarget[i] = yTarget[i - 1] + dy;
+
+ // corners
+ if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+
+ // horizontal edges
+ if (targetCenterWidth > 0 && sourceCenterWidth > 0) {
+ if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.top();
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.bottom();
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ }
+
+ // vertical edges
+ if (targetCenterHeight > 0 && sourceCenterHeight > 0) {
+ if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData;
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.left();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.right();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ }
+
+ // center
+ if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) {
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceCenterWidth;
+ d.height = sourceCenterHeight;
+ d.scaleX = dx / d.width;
+ d.scaleY = dy / d.height;
+
+ qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX;
+ qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY;
+
+ for (int j = 1; j < rows - 1; ++j) {
+ d.y = (0.5 * (yTarget[j + 1] + yTarget[j]));
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = repeatWidth;
+ }
+ if (rules.vertical == Qt::RepeatTile) {
+ for (int i = 1; i < columns - 1; ++i)
+ data[data.size() - i].height = repeatHeight;
+ }
+ }
+
+ if (opaqueData.size())
+ painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint);
+ if (translucentData.size())
+ painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap);
+
+ if (oldAA)
+ painter->setRenderHint(QPainter::Antialiasing, true);
+}
+
+} // QSGSoftwareHelpers namespace
+
+QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode()
+ : m_innerSourceRect(0, 0, 1, 1)
+ , m_subSourceRect(0, 0, 1, 1)
+ , m_texture(0)
+ , m_mirror(false)
+ , m_smooth(true)
+ , m_tileHorizontal(false)
+ , m_tileVertical(false)
+ , m_cachedMirroredPixmapIsDirty(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+
+void QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture)
+{
+ m_texture = texture;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setMirror(bool mirror)
+{
+ if (m_mirror != mirror) {
+ m_mirror = mirror;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/)
+{
+}
+
+void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ bool smooth = (filtering == QSGTexture::Linear);
+ if (smooth == m_smooth)
+ return;
+
+ m_smooth = smooth;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileHorizontal = (wrapMode == QSGTexture::Repeat);
+ if (tileHorizontal == m_tileHorizontal)
+ return;
+
+ m_tileHorizontal = tileHorizontal;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileVertical = (wrapMode == QSGTexture::Repeat);
+ if (tileVertical == m_tileVertical)
+ return;
+
+ m_tileVertical = (wrapMode == QSGTexture::Repeat);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::update()
+{
+ if (m_cachedMirroredPixmapIsDirty) {
+ if (m_mirror) {
+ m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0));
+ } else {
+ //Cleanup cached pixmap if necessary
+ if (!m_cachedMirroredPixmap.isNull())
+ m_cachedMirroredPixmap = QPixmap();
+ }
+ m_cachedMirroredPixmapIsDirty = false;
+ }
+}
+
+void QSGSoftwareInternalImageNode::preprocess()
+{
+ bool doDirty = false;
+ QSGLayer *t = qobject_cast<QSGLayer *>(m_texture);
+ if (t) {
+ doDirty = t->updateTexture();
+ markDirty(DirtyGeometry);
+ }
+ if (doDirty)
+ markDirty(DirtyMaterial);
+}
+
+static Qt::TileRule getTileRule(qreal factor)
+{
+ int ifactor = qRound(factor);
+ if (qFuzzyCompare(factor, ifactor )) {
+ if (ifactor == 1 || ifactor == 0)
+ return Qt::StretchTile;
+ return Qt::RoundTile;
+ }
+ return Qt::RepeatTile;
+}
+
+
+void QSGSoftwareInternalImageNode::paint(QPainter *painter)
+{
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth);
+
+ const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap();
+
+ if (m_innerTargetRect != m_targetRect) {
+ // border image
+ QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(),
+ m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom());
+ QSGSoftwareHelpers::QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height()));
+ QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()),
+ margins, tilerules, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0));
+ return;
+ }
+
+ if (m_tileHorizontal || m_tileVertical) {
+ painter->save();
+ qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width());
+ qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height());
+ QMatrix transform(sx, 0, 0, sy, 0, 0);
+ painter->setMatrix(transform, true);
+ painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy),
+ pm,
+ QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height()));
+ painter->restore();
+ } else {
+ QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(),
+ m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height());
+ painter->drawPixmap(m_targetRect, pm, sr);
+ }
+}
+
+QRectF QSGSoftwareInternalImageNode::rect() const
+{
+ return m_targetRect;
+}
+
+const QPixmap &QSGSoftwareInternalImageNode::pixmap() const
+{
+ if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) {
+ return pt->pixmap();
+ } else {
+ QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(m_texture);
+ return layer->pixmap();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
new file mode 100644
index 0000000000..f21667fdf7
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREINTERNALIMAGENODE_H
+#define QSGSOFTWAREINTERNALIMAGENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgtexturematerial_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGSoftwareHelpers {
+
+typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
+
+struct QTileRules
+{
+ inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
+ : horizontal(horizontalRule), vertical(verticalRule) {}
+ inline QTileRules(Qt::TileRule rule = Qt::StretchTile)
+ : horizontal(rule), vertical(rule) {}
+ Qt::TileRule horizontal;
+ Qt::TileRule vertical;
+};
+
+#ifndef Q_QDOC
+// For internal use only.
+namespace QDrawBorderPixmap
+{
+ enum DrawingHint
+ {
+ OpaqueTopLeft = 0x0001,
+ OpaqueTop = 0x0002,
+ OpaqueTopRight = 0x0004,
+ OpaqueLeft = 0x0008,
+ OpaqueCenter = 0x0010,
+ OpaqueRight = 0x0020,
+ OpaqueBottomLeft = 0x0040,
+ OpaqueBottom = 0x0080,
+ OpaqueBottomRight = 0x0100,
+ OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight,
+ OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom,
+ OpaqueFrame = OpaqueCorners | OpaqueEdges,
+ OpaqueAll = OpaqueCenter | OpaqueFrame
+ };
+
+ Q_DECLARE_FLAGS(DrawingHints, DrawingHint)
+}
+#endif
+
+void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins,
+ const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints);
+
+} // QSGSoftwareHelpers namespace
+
+class QSGSoftwareInternalImageNode : public QSGInternalImageNode
+{
+public:
+ QSGSoftwareInternalImageNode();
+
+ void setTargetRect(const QRectF &rect) override;
+ void setInnerTargetRect(const QRectF &rect) override;
+ void setInnerSourceRect(const QRectF &rect) override;
+ void setSubSourceRect(const QRectF &rect) override;
+ void setTexture(QSGTexture *texture) override;
+ void setMirror(bool mirror) override;
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void update() override;
+
+ void preprocess() override;
+
+ void paint(QPainter *painter);
+
+ QRectF rect() const;
+
+private:
+ const QPixmap &pixmap() const;
+
+ QRectF m_targetRect;
+ QRectF m_innerTargetRect;
+ QRectF m_innerSourceRect;
+ QRectF m_subSourceRect;
+
+ QSGTexture *m_texture;
+ QPixmap m_cachedMirroredPixmap;
+
+ bool m_mirror;
+ bool m_smooth;
+ bool m_tileHorizontal;
+ bool m_tileVertical;
+ bool m_cachedMirroredPixmapIsDirty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREINTERNALIMAGENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
new file mode 100644
index 0000000000..f6898b3879
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include <qmath.h>
+
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
+ : m_penWidth(0)
+ , m_radius(0)
+ , m_cornerPixmapIsDirty(true)
+ , m_devicePixelRatio(1)
+{
+ m_pen.setJoinStyle(Qt::MiterJoin);
+ m_pen.setMiterLimit(0);
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareInternalRectangleNode::setRect(const QRectF &rect)
+{
+ QRect alignedRect = rect.toAlignedRect();
+ if (m_rect != alignedRect) {
+ m_rect = alignedRect;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setPenColor(const QColor &color)
+{
+ if (m_penColor != color) {
+ m_penColor = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setPenWidth(qreal width)
+{
+ if (m_penWidth != width) {
+ m_penWidth = width;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+//Move first stop by pos relative to seconds
+static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGradientStop &secondStop, double newPos)
+{
+ double distance = secondStop.first - firstStop.first;
+ double distanceDelta = newPos - firstStop.first;
+ double modifierValue = distanceDelta / distance;
+ int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue;
+ int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue;
+ int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue;
+ int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue;
+
+ QGradientStop newStop;
+ newStop.first = newPos;
+ newStop.second = QColor(firstStop.second.red() + redDelta,
+ firstStop.second.green() + greenDelta,
+ firstStop.second.blue() + blueDelta,
+ firstStop.second.alpha() + alphaDelta);
+
+ return newStop;
+}
+
+void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &stops)
+{
+ //normalize stops
+ bool needsNormalization = false;
+ for (const QGradientStop &stop : qAsConst(stops)) {
+ if (stop.first < 0.0 || stop.first > 1.0) {
+ needsNormalization = true;
+ continue;
+ }
+ }
+
+ if (needsNormalization) {
+ QGradientStops normalizedStops;
+ if (stops.count() == 1) {
+ //If there is only one stop, then the position does not matter
+ //It is just treated as a color
+ QGradientStop stop = stops.at(0);
+ stop.first = 0.0;
+ normalizedStops.append(stop);
+ } else {
+ //Clip stops to only the first below 0.0 and above 1.0
+ int below = -1;
+ int above = -1;
+ QVector<int> between;
+ for (int i = 0; i < stops.count(); ++i) {
+ if (stops.at(i).first < 0.0) {
+ below = i;
+ } else if (stops.at(i).first > 1.0) {
+ above = i;
+ break;
+ } else {
+ between.append(i);
+ }
+ }
+
+ //Interpoloate new color values for above and below
+ if (below != -1 ) {
+ //If there are more than one stops left, interpolate
+ if (below + 1 < stops.count()) {
+ normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0));
+ } else {
+ QGradientStop singleStop;
+ singleStop.first = 0.0;
+ singleStop.second = stops.at(below).second;
+ normalizedStops.append(singleStop);
+ }
+ }
+
+ for (int i = 0; i < between.count(); ++i)
+ normalizedStops.append(stops.at(between.at(i)));
+
+ if (above != -1) {
+ //If there stops before above, interpolate
+ if (above >= 1) {
+ normalizedStops.append(interpolateStop(stops.at(above), stops.at(above - 1), 1.0));
+ } else {
+ QGradientStop singleStop;
+ singleStop.first = 1.0;
+ singleStop.second = stops.at(above).second;
+ normalizedStops.append(singleStop);
+ }
+ }
+ }
+
+ m_stops = normalizedStops;
+
+ } else {
+ m_stops = stops;
+ }
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
+{
+ if (m_radius != radius) {
+ m_radius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/)
+{
+}
+
+void QSGSoftwareInternalRectangleNode::update()
+{
+ if (!m_penWidth || m_penColor == Qt::transparent) {
+ m_pen = Qt::NoPen;
+ } else {
+ m_pen = QPen(m_penColor);
+ m_pen.setWidthF(m_penWidth);
+ }
+
+ if (!m_stops.isEmpty()) {
+ QLinearGradient gradient(QPoint(0,0), QPoint(0,1));
+ gradient.setStops(m_stops);
+ gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ m_brush = QBrush(gradient);
+ } else {
+ m_brush = QBrush(m_color);
+ }
+
+ if (m_cornerPixmapIsDirty) {
+ generateCornerPixmap();
+ m_cornerPixmapIsDirty = false;
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::paint(QPainter *painter)
+{
+ //We can only check for a device pixel ratio change when we know what
+ //paint device is being used.
+ if (painter->device()->devicePixelRatio() != m_devicePixelRatio) {
+ m_devicePixelRatio = painter->device()->devicePixelRatio();
+ generateCornerPixmap();
+ }
+
+ if (painter->transform().isRotating()) {
+ //Rotated rectangles lose the benefits of direct rendering, and have poor rendering
+ //quality when using only blits and fills.
+
+ if (m_radius == 0 && m_penWidth == 0) {
+ //Non-Rounded Rects without borders (fall back to drawRect)
+ //Most common case
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawRect(m_rect);
+ } else {
+ //Rounded Rects and Rects with Borders
+ //Avoids broken behaviors of QPainter::drawRect/roundedRect
+ QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio);
+ pixmap.fill(Qt::transparent);
+ pixmap.setDevicePixelRatio(m_devicePixelRatio);
+ QPainter pixmapPainter(&pixmap);
+ paintRectangle(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height()));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+ painter->drawPixmap(m_rect, pixmap);
+ painter->setRenderHints(previousRenderHints);
+ }
+
+
+ } else {
+ //Paint directly
+ paintRectangle(painter, m_rect);
+ }
+
+}
+
+bool QSGSoftwareInternalRectangleNode::isOpaque() const
+{
+ if (m_radius > 0.0f)
+ return false;
+ if (m_color.alpha() < 255)
+ return false;
+ if (m_penWidth > 0.0f && m_penColor.alpha() < 255)
+ return false;
+ if (m_stops.count() > 0) {
+ for (const QGradientStop &stop : qAsConst(m_stops)) {
+ if (stop.second.alpha() < 255)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QRectF QSGSoftwareInternalRectangleNode::rect() const
+{
+ //TODO: double check that this is correct.
+ return m_rect;
+}
+
+void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const QRect &rect)
+{
+ //Radius should never exceeds half of the width or half of the height
+ int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::Antialiasing, false);
+
+ if (m_penWidth > 0) {
+ //Fill border Rects
+
+ //Borders can not be more than half the height/width of a rect
+ double borderWidth = qMin(m_penWidth, rect.width() * 0.5);
+ double borderHeight = qMin(m_penWidth, rect.height() * 0.5);
+
+
+
+ if (borderWidth > radius) {
+ //4 Rects
+ QRectF borderTopOutside(QPointF(rect.x() + radius, rect.y()),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + radius));
+ QRectF borderTopInside(QPointF(rect.x() + borderWidth, rect.y() + radius),
+ QPointF(rect.x() + rect.width() - borderWidth, rect.y() + borderHeight));
+ QRectF borderBottomOutside(QPointF(rect.x() + radius, rect.y() + rect.height() - radius),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height()));
+ QRectF borderBottomInside(QPointF(rect.x() + borderWidth, rect.y() + rect.height() - borderHeight),
+ QPointF(rect.x() + rect.width() - borderWidth, rect.y() + rect.height() - radius));
+
+ if (borderTopOutside.isValid())
+ painter->fillRect(borderTopOutside, m_penColor);
+ if (borderTopInside.isValid())
+ painter->fillRect(borderTopInside, m_penColor);
+ if (borderBottomOutside.isValid())
+ painter->fillRect(borderBottomOutside, m_penColor);
+ if (borderBottomInside.isValid())
+ painter->fillRect(borderBottomInside, m_penColor);
+
+ } else {
+ //2 Rects
+ QRectF borderTop(QPointF(rect.x() + radius, rect.y()),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + borderHeight));
+ QRectF borderBottom(QPointF(rect.x() + radius, rect.y() + rect.height() - borderHeight),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height()));
+ if (borderTop.isValid())
+ painter->fillRect(borderTop, m_penColor);
+ if (borderBottom.isValid())
+ painter->fillRect(borderBottom, m_penColor);
+ }
+ QRectF borderLeft(QPointF(rect.x(), rect.y() + radius),
+ QPointF(rect.x() + borderWidth, rect.y() + rect.height() - radius));
+ QRectF borderRight(QPointF(rect.x() + rect.width() - borderWidth, rect.y() + radius),
+ QPointF(rect.x() + rect.width(), rect.y() + rect.height() - radius));
+ if (borderLeft.isValid())
+ painter->fillRect(borderLeft, m_penColor);
+ if (borderRight.isValid())
+ painter->fillRect(borderRight, m_penColor);
+ }
+
+
+ if (radius > 0) {
+
+ if (radius * 2 >= rect.width() && radius * 2 >= rect.height()) {
+ //Blit whole pixmap for circles
+ painter->drawPixmap(rect, m_cornerPixmap, m_cornerPixmap.rect());
+ } else {
+
+ //blit 4 corners to border
+ int scaledRadius = radius * m_devicePixelRatio;
+ QRectF topLeftCorner(QPointF(rect.x(), rect.y()),
+ QPointF(rect.x() + radius, rect.y() + radius));
+ painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius));
+ QRectF topRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y()),
+ QPointF(rect.x() + rect.width(), rect.y() + radius));
+ painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(scaledRadius, 0, scaledRadius, scaledRadius));
+ QRectF bottomLeftCorner(QPointF(rect.x(), rect.y() + rect.height() - radius),
+ QPointF(rect.x() + radius, rect.y() + rect.height()));
+ painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, scaledRadius, scaledRadius, scaledRadius));
+ QRectF bottomRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height() - radius),
+ QPointF(rect.x() + rect.width(), rect.y() + rect.height()));
+ painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(scaledRadius, scaledRadius, scaledRadius, scaledRadius));
+
+ }
+
+ }
+
+ QRectF brushRect = QRectF(rect).marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth));
+ if (brushRect.width() < 0)
+ brushRect.setWidth(0);
+ if (brushRect.height() < 0)
+ brushRect.setHeight(0);
+ double innerRectRadius = qMax(0.0, radius - m_penWidth);
+
+ //If not completely transparent or has a gradient
+ if (m_color.alpha() > 0 || !m_stops.empty()) {
+ if (innerRectRadius > 0) {
+ //Rounded Rect
+ if (m_stops.empty()) {
+ //Rounded Rects without gradient need 3 blits
+ QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()),
+ QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height()));
+ painter->fillRect(centerRect, m_color);
+ QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(leftRect, m_color);
+ QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(rightRect, m_color);
+ } else {
+ //Rounded Rect with gradient (slow)
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius);
+ }
+ } else {
+ //non-rounded rects only need 1 blit
+ painter->fillRect(brushRect, m_brush);
+ }
+ }
+
+ painter->setRenderHints(previousRenderHints);
+}
+
+void QSGSoftwareInternalRectangleNode::generateCornerPixmap()
+{
+ //Generate new corner Pixmap
+ int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius));
+
+ m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio);
+ m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio);
+ m_cornerPixmap.fill(Qt::transparent);
+
+ if (radius > 0) {
+ QPainter cornerPainter(&m_cornerPixmap);
+ cornerPainter.setRenderHint(QPainter::Antialiasing);
+ cornerPainter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ //Paint outer cicle
+ if (m_penWidth > 0) {
+ cornerPainter.setPen(Qt::NoPen);
+ cornerPainter.setBrush(m_penColor);
+ cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius);
+ }
+
+ //Paint inner circle
+ if (radius > m_penWidth) {
+ cornerPainter.setPen(Qt::NoPen);
+ if (m_stops.isEmpty())
+ cornerPainter.setBrush(m_brush);
+ else
+ cornerPainter.setBrush(Qt::transparent);
+
+ QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth);
+ QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins);
+ cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius);
+ }
+ cornerPainter.end();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
new file mode 100644
index 0000000000..f363e279e1
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREINTERNALRECTANGLENODE_H
+#define QSGSOFTWAREINTERNALRECTANGLENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QPen>
+#include <QBrush>
+#include <QPixmap>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareInternalRectangleNode : public QSGInternalRectangleNode
+{
+public:
+ QSGSoftwareInternalRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ void setColor(const QColor &color) override;
+ void setPenColor(const QColor &color) override;
+ void setPenWidth(qreal width) override;
+ void setGradientStops(const QGradientStops &stops) override;
+ void setRadius(qreal radius) override;
+ void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) }
+ void setAligned(bool aligned) override;
+
+ void update() override;
+
+ void paint(QPainter *);
+
+ bool isOpaque() const;
+ QRectF rect() const;
+private:
+ void paintRectangle(QPainter *painter, const QRect &rect);
+ void generateCornerPixmap();
+
+ QRect m_rect;
+ QColor m_color;
+ QColor m_penColor;
+ double m_penWidth;
+ QGradientStops m_stops;
+ double m_radius;
+ QPen m_pen;
+ QBrush m_brush;
+
+ bool m_cornerPixmapIsDirty;
+ QPixmap m_cornerPixmap;
+
+ int m_devicePixelRatio;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREINTERNALRECTANGLENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
new file mode 100644
index 0000000000..7020283898
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarelayer_p.h"
+
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarepixmaprenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext)
+ : m_item(0)
+ , m_context(renderContext)
+ , m_renderer(0)
+ , m_device_pixel_ratio(1)
+ , m_mirrorHorizontal(false)
+ , m_mirrorVertical(false)
+ , m_live(true)
+ , m_grab(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+{
+
+}
+
+QSGSoftwareLayer::~QSGSoftwareLayer()
+{
+ invalidated();
+}
+
+int QSGSoftwareLayer::textureId() const
+{
+ return 0;
+}
+
+QSize QSGSoftwareLayer::textureSize() const
+{
+ return m_pixmap.size();
+}
+
+bool QSGSoftwareLayer::hasAlphaChannel() const
+{
+ return m_pixmap.hasAlphaChannel();
+}
+
+bool QSGSoftwareLayer::hasMipmaps() const
+{
+ return false;
+}
+
+void QSGSoftwareLayer::bind()
+{
+}
+
+bool QSGSoftwareLayer::updateTexture()
+{
+ bool doGrab = (m_live || m_grab) && m_dirtyTexture;
+ if (doGrab)
+ grab();
+ if (m_grab)
+ emit scheduledUpdateCompleted();
+ m_grab = false;
+ return doGrab;
+}
+
+void QSGSoftwareLayer::setItem(QSGNode *item)
+{
+ if (item == m_item)
+ return;
+ m_item = item;
+
+ if (m_live && !m_item)
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+ m_size = size;
+
+ if (m_live && m_size.isNull())
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+ m_grab = true;
+ if (m_dirtyTexture) {
+ emit updateRequested();
+ }
+}
+
+QImage QSGSoftwareLayer::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QSGSoftwareLayer::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+ m_live = live;
+
+ if (m_live && (!m_item || m_size.isNull()))
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGSoftwareLayer::setFormat(uint)
+{
+}
+
+void QSGSoftwareLayer::setHasMipmaps(bool)
+{
+}
+
+void QSGSoftwareLayer::setDevicePixelRatio(qreal ratio)
+{
+ m_device_pixel_ratio = ratio;
+}
+
+void QSGSoftwareLayer::setMirrorHorizontal(bool mirror)
+{
+ if (m_mirrorHorizontal == mirror)
+ return;
+ m_mirrorHorizontal = mirror;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setMirrorVertical(bool mirror)
+{
+ if (m_mirrorVertical == mirror)
+ return;
+ m_mirrorVertical = mirror;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::markDirtyTexture()
+{
+ m_dirtyTexture = true;
+ if (m_live || m_grab) {
+ emit updateRequested();
+ }
+}
+
+void QSGSoftwareLayer::invalidated()
+{
+ delete m_renderer;
+ m_renderer = 0;
+}
+
+void QSGSoftwareLayer::grab()
+{
+ if (!m_item || m_size.isNull()) {
+ m_pixmap = QPixmap();
+ m_dirtyTexture = false;
+ return;
+ }
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = new QSGSoftwarePixmapRenderer(m_context);
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
+ }
+ m_renderer->setDevicePixelRatio(m_device_pixel_ratio);
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ if (m_pixmap.size() != m_size) {
+ m_pixmap = QPixmap(m_size);
+ m_pixmap.setDevicePixelRatio(m_device_pixel_ratio);
+ // This fill here is wasteful, but necessary because it is the only way
+ // to force a QImage based pixmap to have an alpha channel.
+ m_pixmap.fill(Qt::transparent);
+ }
+
+ // Render texture.
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+ m_dirtyTexture = false;
+
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRect mirrored(m_mirrorHorizontal ? m_rect.right() * m_device_pixel_ratio : m_rect.left() * m_device_pixel_ratio,
+ m_mirrorVertical ? m_rect.top() * m_device_pixel_ratio : m_rect.bottom() * m_device_pixel_ratio,
+ m_mirrorHorizontal ? -m_rect.width() * m_device_pixel_ratio : m_rect.width() * m_device_pixel_ratio,
+ m_mirrorVertical ? m_rect.height() * m_device_pixel_ratio : -m_rect.height() * m_device_pixel_ratio);
+ m_renderer->setProjectionRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ m_renderer->renderScene();
+ m_renderer->render(&m_pixmap);
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
new file mode 100644
index 0000000000..d3f13e40b1
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARELAYER_H
+#define QSGSOFTWARELAYER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapRenderer;
+
+class QSGSoftwareLayer : public QSGLayer
+{
+ Q_OBJECT
+public:
+ QSGSoftwareLayer(QSGRenderContext *renderContext);
+ ~QSGSoftwareLayer();
+
+ const QPixmap &pixmap() const { return m_pixmap; }
+
+ // QSGTexture interface
+public:
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ void bind() override;
+
+ // QSGDynamicTexture interface
+public:
+ bool updateTexture() override;
+
+ // QSGLayer interface
+public:
+ void setItem(QSGNode *item) override;
+ void setRect(const QRectF &rect) override;
+ void setSize(const QSize &size) override;
+ void scheduleUpdate() override;
+ QImage toImage() const override;
+ void setLive(bool live) override;
+ void setRecursive(bool recursive) override;
+ void setFormat(uint) override;
+ void setHasMipmaps(bool) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setMirrorHorizontal(bool mirror) override;
+ void setMirrorVertical(bool mirror) override;
+
+public slots:
+ void markDirtyTexture() override;
+ void invalidated() override;
+
+private:
+ void grab();
+
+ QSGNode *m_item;
+ QSGRenderContext *m_context;
+ QSGSoftwarePixmapRenderer *m_renderer;
+ QRectF m_rect;
+ QSize m_size;
+ QPixmap m_pixmap;
+ qreal m_device_pixel_ratio;
+ bool m_mirrorHorizontal;
+ bool m_mirrorVertical;
+ bool m_live;
+ bool m_grab;
+ bool m_recursive;
+ bool m_dirtyTexture;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARELAYER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
new file mode 100644
index 0000000000..34b0cd5b72
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePainterNode::QSGSoftwarePainterNode(QQuickPaintedItem *item)
+ : QSGPainterNode()
+ , m_preferredRenderTarget(QQuickPaintedItem::Image)
+ , m_item(item)
+ , m_texture(0)
+ , m_dirtyContents(false)
+ , m_opaquePainting(false)
+ , m_linear_filtering(false)
+ , m_mipmapping(false)
+ , m_smoothPainting(false)
+ , m_fastFBOResizing(false)
+ , m_fillColor(Qt::transparent)
+ , m_contentsScale(1.0)
+ , m_dirtyGeometry(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+QSGSoftwarePainterNode::~QSGSoftwarePainterNode()
+{
+ delete m_texture;
+}
+
+void QSGSoftwarePainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
+{
+ if (m_preferredRenderTarget == target)
+ return;
+
+ m_preferredRenderTarget = target;
+}
+
+void QSGSoftwarePainterNode::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+
+ m_size = size;
+
+ m_dirtyGeometry = true;
+}
+
+void QSGSoftwarePainterNode::setDirty(const QRect &dirtyRect)
+{
+ m_dirtyContents = true;
+ m_dirtyRect = dirtyRect;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setOpaquePainting(bool opaque)
+{
+ if (opaque == m_opaquePainting)
+ return;
+
+ m_opaquePainting = opaque;
+}
+
+void QSGSoftwarePainterNode::setLinearFiltering(bool linearFiltering)
+{
+ if (linearFiltering == m_linear_filtering)
+ return;
+
+ m_linear_filtering = linearFiltering;
+}
+
+void QSGSoftwarePainterNode::setMipmapping(bool mipmapping)
+{
+ if (mipmapping == m_mipmapping)
+ return;
+
+ m_mipmapping = mipmapping;
+}
+
+void QSGSoftwarePainterNode::setSmoothPainting(bool s)
+{
+ if (s == m_smoothPainting)
+ return;
+
+ m_smoothPainting = s;
+}
+
+void QSGSoftwarePainterNode::setFillColor(const QColor &c)
+{
+ if (c == m_fillColor)
+ return;
+
+ m_fillColor = c;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setContentsScale(qreal s)
+{
+ if (s == m_contentsScale)
+ return;
+
+ m_contentsScale = s;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setFastFBOResizing(bool dynamic)
+{
+ m_fastFBOResizing = dynamic;
+}
+
+QImage QSGSoftwarePainterNode::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QSGSoftwarePainterNode::update()
+{
+ if (m_dirtyGeometry) {
+ m_pixmap = QPixmap(m_textureSize);
+ if (!m_opaquePainting)
+ m_pixmap.fill(Qt::transparent);
+
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGSoftwarePixmapTexture(m_pixmap);
+ }
+
+ if (m_dirtyContents)
+ paint();
+
+ m_dirtyGeometry = false;
+ m_dirtyContents = false;
+}
+
+void QSGSoftwarePainterNode::paint(QPainter *painter)
+{
+ painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap);
+}
+
+void QSGSoftwarePainterNode::paint()
+{
+ QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
+
+ QPainter painter;
+
+ painter.begin(&m_pixmap);
+ if (m_smoothPainting) {
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+ }
+
+ QRect clipRect;
+
+ if (m_contentsScale == 1) {
+ qreal scaleX = m_textureSize.width() / (qreal) m_size.width();
+ qreal scaleY = m_textureSize.height() / (qreal) m_size.height();
+ painter.scale(scaleX, scaleY);
+ clipRect = dirtyRect;
+ } else {
+ painter.scale(m_contentsScale, m_contentsScale);
+
+ QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
+ qFloor(dirtyRect.y()/m_contentsScale),
+ qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
+ qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
+
+ clipRect = sclip;
+ }
+
+ if (!m_dirtyRect.isNull())
+ painter.setClipRect(clipRect);
+
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(clipRect, m_fillColor);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ m_item->paint(&painter);
+ painter.end();
+
+ m_dirtyRect = QRect();
+}
+
+
+void QSGSoftwarePainterNode::setTextureSize(const QSize &size)
+{
+ if (size == m_textureSize)
+ return;
+
+ m_textureSize = size;
+ m_dirtyGeometry = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h
new file mode 100644
index 0000000000..67b45354a2
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPAINTERNODE_H
+#define QSGSOFTWAREPAINTERNODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/qquickpainteditem.h>
+
+#include <QtGui/QPixmap>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePainterNode : public QSGPainterNode
+{
+public:
+ QSGSoftwarePainterNode(QQuickPaintedItem *item);
+ ~QSGSoftwarePainterNode();
+
+ void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) override;
+
+ void setSize(const QSize &size) override;
+ QSize size() const { return m_size; }
+
+ void setDirty(const QRect &dirtyRect = QRect()) override;
+
+ void setOpaquePainting(bool opaque) override;
+ bool opaquePainting() const { return m_opaquePainting; }
+
+ void setLinearFiltering(bool linearFiltering) override;
+ bool linearFiltering() const { return m_linear_filtering; }
+
+ void setMipmapping(bool mipmapping) override;
+ bool mipmapping() const { return m_mipmapping; }
+
+ void setSmoothPainting(bool s) override;
+ bool smoothPainting() const { return m_smoothPainting; }
+
+ void setFillColor(const QColor &c) override;
+ QColor fillColor() const { return m_fillColor; }
+
+ void setContentsScale(qreal s) override;
+ qreal contentsScale() const { return m_contentsScale; }
+
+ void setFastFBOResizing(bool dynamic) override;
+ bool fastFBOResizing() const { return m_fastFBOResizing; }
+
+ QImage toImage() const override;
+ void update() override;
+ QSGTexture *texture() const override { return m_texture; }
+
+ void paint(QPainter *painter);
+
+ void paint();
+
+ void setTextureSize(const QSize &size) override;
+ QSize textureSize() const { return m_textureSize; }
+
+private:
+
+ QQuickPaintedItem::RenderTarget m_preferredRenderTarget;
+
+ QQuickPaintedItem *m_item;
+
+ QPixmap m_pixmap;
+ QSGTexture *m_texture;
+
+ QSize m_size;
+ bool m_dirtyContents;
+ QRect m_dirtyRect;
+ bool m_opaquePainting;
+ bool m_linear_filtering;
+ bool m_mipmapping;
+ bool m_smoothPainting;
+ bool m_fastFBOResizing;
+ QColor m_fillColor;
+ qreal m_contentsScale;
+ QSize m_textureSize;
+
+ bool m_dirtyGeometry;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPAINTERNODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
new file mode 100644
index 0000000000..f8c1a3d90b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepixmaprenderer_p.h"
+#include "qsgsoftwarecontext_p.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcPixmapRenderer, "qt.scenegraph.softwarecontext.pixmapRenderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePixmapRenderer::QSGSoftwarePixmapRenderer(QSGRenderContext *context)
+ : QSGAbstractSoftwareRenderer(context)
+{
+
+}
+
+QSGSoftwarePixmapRenderer::~QSGSoftwarePixmapRenderer()
+{
+
+}
+
+void QSGSoftwarePixmapRenderer::renderScene(uint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void QSGSoftwarePixmapRenderer::render()
+{
+
+}
+
+void QSGSoftwarePixmapRenderer::render(QPaintDevice *target)
+{
+ QElapsedTimer renderTimer;
+
+ // Setup background item
+ setBackgroundSize(QSize(target->width(), target->height()));
+ setBackgroundColor(clearColor());
+
+ QPainter painter(target);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setWindow(m_projectionRect);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
+
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // Right now there is an assumption that when possible the same pixmap will
+ // be reused. So we can treat it like a backing store in that we can assume
+ // that when the pixmap is not being resized, the data can be reused. What is
+ // different though is that everything should be marked as dirty on a resize.
+ optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ QRegion paintedRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ rc->m_activePainter = prevPainter;
+ qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+}
+
+void QSGSoftwarePixmapRenderer::setProjectionRect(const QRect &projectionRect)
+{
+ m_projectionRect = projectionRect;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h
new file mode 100644
index 0000000000..3b4fb304e7
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPIXMAPRENDERER_H
+#define QSGSOFTWAREPIXMAPRENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapRenderer : public QSGAbstractSoftwareRenderer
+{
+public:
+ QSGSoftwarePixmapRenderer(QSGRenderContext *context);
+ virtual ~QSGSoftwarePixmapRenderer();
+
+ void renderScene(uint fboId = 0) final;
+ void render() final;
+
+ void render(QPaintDevice *target);
+ void setProjectionRect(const QRect &projectionRect);
+
+private:
+ QRect m_projectionRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPIXMAPRENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
new file mode 100644
index 0000000000..534a0a4ec6
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepixmaptexture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint flags)
+{
+ // Prevent pixmap format conversion to reduce memory consumption
+ // and surprises in calling code. (See QTBUG-47328)
+ if (flags & QSGRenderContext::CreateTexture_Alpha) {
+ //If texture should have an alpha
+ m_pixmap = QPixmap::fromImage(image, Qt::NoFormatConversion);
+ } else {
+ //Force opaque texture
+ m_pixmap = QPixmap::fromImage(image.convertToFormat(QImage::Format_RGB32), Qt::NoFormatConversion);
+ }
+}
+
+QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QPixmap &pixmap)
+ : m_pixmap(pixmap)
+{
+}
+
+
+int QSGSoftwarePixmapTexture::textureId() const
+{
+ return 0;
+}
+
+QSize QSGSoftwarePixmapTexture::textureSize() const
+{
+ return m_pixmap.size();
+}
+
+bool QSGSoftwarePixmapTexture::hasAlphaChannel() const
+{
+ return m_pixmap.hasAlphaChannel();
+}
+
+bool QSGSoftwarePixmapTexture::hasMipmaps() const
+{
+ return false;
+}
+
+void QSGSoftwarePixmapTexture::bind()
+{
+ Q_UNREACHABLE();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
new file mode 100644
index 0000000000..034fa25da9
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPIXMAPTEXTURE_H
+#define QSGSOFTWAREPIXMAPTEXTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgtexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ QSGSoftwarePixmapTexture(const QImage &image, uint flags);
+ QSGSoftwarePixmapTexture(const QPixmap &pixmap);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ void bind() override;
+
+ const QPixmap &pixmap() const { return m_pixmap; }
+
+private:
+ QPixmap m_pixmap;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPIXMAPTEXTURE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
new file mode 100644
index 0000000000..1fa5234377
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
+ : m_color(QColor(255, 255, 255))
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareRectangleNode::paint(QPainter *painter)
+{
+ painter->fillRect(m_rect, m_color);
+}
+
+QSGSoftwareImageNode::QSGSoftwareImageNode()
+ : m_texture(nullptr),
+ m_owns(false),
+ m_filtering(QSGTexture::None),
+ m_transformMode(NoTransform),
+ m_cachedMirroredPixmapIsDirty(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+QSGSoftwareImageNode::~QSGSoftwareImageNode()
+{
+ if (m_owns)
+ delete m_texture;
+}
+
+void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
+{
+ m_texture = texture; markDirty(DirtyMaterial);
+ m_cachedMirroredPixmapIsDirty = true;
+}
+
+void QSGSoftwareImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode)
+{
+ if (m_transformMode == transformNode)
+ return;
+
+ m_transformMode = transformNode;
+ m_cachedMirroredPixmapIsDirty = true;
+
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::paint(QPainter *painter)
+{
+ if (m_cachedMirroredPixmapIsDirty)
+ updateCachedMirroredPixmap();
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear));
+
+ if (!m_cachedPixmap.isNull()) {
+ painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect);
+ } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_rect, pm, m_sourceRect);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_rect, im, m_sourceRect);
+ }
+}
+
+void QSGSoftwareImageNode::updateCachedMirroredPixmap()
+{
+ if (m_transformMode == NoTransform) {
+ m_cachedPixmap = QPixmap();
+ } else {
+
+ if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ QTransform mirrorTransform;
+ if (m_transformMode.testFlag(MirrorVertically))
+ mirrorTransform = mirrorTransform.scale(1, -1);
+ if (m_transformMode.testFlag(MirrorHorizontally))
+ mirrorTransform = mirrorTransform.scale(-1, 1);
+ m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically)));
+ } else {
+ m_cachedPixmap = QPixmap();
+ }
+ }
+
+ m_cachedMirroredPixmapIsDirty = false;
+}
+
+QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture)
+{
+ QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(texture);
+ if (!pt) {
+ qWarning() << "Image used with invalid texture format.";
+ return;
+ }
+ m_pixmap = pt->pixmap();
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds)
+{
+ if (m_bounds == bounds)
+ return;
+
+ m_bounds = bounds;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::setDevicePixelRatio(qreal ratio)
+{
+ if (m_pixelRatio == ratio)
+ return;
+
+ m_pixelRatio = ratio;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
+{
+ QMargins margins(qRound(left), qRound(top), qRound(right), qRound(bottom));
+ if (m_margins == margins)
+ return;
+
+ m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom));
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::update()
+{
+}
+
+void QSGSoftwareNinePatchNode::paint(QPainter *painter)
+{
+ if (m_margins.isNull())
+ painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height()));
+ else
+ QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()),
+ m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0));
+}
+
+QRectF QSGSoftwareNinePatchNode::bounds() const
+{
+ return m_bounds;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
new file mode 100644
index 0000000000..9f1913205b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPUBLICNODES_H
+#define QSGSOFTWAREPUBLICNODES_H
+
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+#include <QtGui/qpixmap.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGSoftwareRectangleNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setColor(const QColor &color) override { m_color = color; markDirty(DirtyMaterial); }
+ QColor color() const override { return m_color; }
+
+ void paint(QPainter *painter);
+
+private:
+ QRectF m_rect;
+ QColor m_color;
+};
+
+class QSGSoftwareImageNode : public QSGImageNode
+{
+public:
+ QSGSoftwareImageNode();
+ ~QSGSoftwareImageNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setSourceRect(const QRectF &r) override { m_sourceRect = r; }
+ QRectF sourceRect() const override { return m_sourceRect; }
+
+ void setTexture(QSGTexture *texture) override;
+ QSGTexture *texture() const override { return m_texture; }
+
+ void setFiltering(QSGTexture::Filtering filtering) override { m_filtering = filtering; markDirty(DirtyMaterial); }
+ QSGTexture::Filtering filtering() const override { return m_filtering; }
+
+ void setMipmapFiltering(QSGTexture::Filtering) override { }
+ QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; }
+
+ void setTextureCoordinatesTransform(TextureCoordinatesTransformMode transformNode) override;
+ TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return m_transformMode; }
+
+ void setOwnsTexture(bool owns) override { m_owns = owns; }
+ bool ownsTexture() const override { return m_owns; }
+
+ void paint(QPainter *painter);
+
+private:
+ void updateCachedMirroredPixmap();
+
+ QPixmap m_cachedPixmap;
+ QSGTexture *m_texture;
+ QRectF m_rect;
+ QRectF m_sourceRect;
+ bool m_owns;
+ QSGTexture::Filtering m_filtering;
+ TextureCoordinatesTransformMode m_transformMode;
+ bool m_cachedMirroredPixmapIsDirty;
+};
+
+class QSGSoftwareNinePatchNode : public QSGNinePatchNode
+{
+public:
+ QSGSoftwareNinePatchNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setBounds(const QRectF &bounds) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setPadding(qreal left, qreal top, qreal right, qreal bottom) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+
+ QRectF bounds() const;
+
+private:
+ QPixmap m_pixmap;
+ QRectF m_bounds;
+ qreal m_pixelRatio;
+ QMargins m_margins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPUBLICNODES_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
new file mode 100644
index 0000000000..59c47db0c4
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#if QT_CONFIG(quick_sprite)
+#include "qsgsoftwarespritenode_p.h"
+#endif
+
+#include <qsgsimplerectnode.h>
+#include <qsgsimpletexturenode.h>
+#include <private/qsgrendernode_p.h>
+#include <private/qsgtexture_p.h>
+
+Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
+ : m_nodeType(type)
+ , m_isOpaque(true)
+ , m_isDirty(true)
+ , m_hasClipRegion(false)
+ , m_opacity(1.0f)
+{
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
+ break;
+#endif
+ case QSGSoftwareRenderableNode::RenderNode:
+ m_handle.renderNode = static_cast<QSGRenderNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Invalid:
+ m_handle.simpleRectNode = nullptr;
+ break;
+ }
+}
+
+QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode()
+{
+
+}
+
+void QSGSoftwareRenderableNode::update()
+{
+ // Update the Node properties
+ m_isDirty = true;
+
+ QRect boundingRect;
+
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleTextureNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ // There isn't a way to tell, so assume it's not
+ m_isOpaque = false;
+
+ boundingRect = m_handle.imageNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.rectangleNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ // Always has alpha
+ m_isOpaque = false;
+
+ boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect();
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ // Difficult to tell, assume non-opaque
+ m_isOpaque = false;
+
+ boundingRect = m_handle.ninePatchNode->bounds().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectangleNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleImageNode->rect().toRect();
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_isOpaque = m_handle.spriteNode->isOpaque();
+ boundingRect = m_handle.spriteNode->rect().toRect();
+ break;
+#endif
+ case QSGSoftwareRenderableNode::RenderNode:
+ if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.renderNode->rect().toRect();
+ break;
+ default:
+ break;
+ }
+
+ m_boundingRect = m_transform.mapRect(boundingRect);
+
+ if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
+ // If there is a clipRegion, and it is empty, the item wont be rendered
+ if (m_clipRegion.isEmpty())
+ m_boundingRect = QRect();
+ else
+ m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
+ }
+
+ // Overrides
+ if (m_opacity < 1.0f)
+ m_isOpaque = false;
+
+ m_dirtyRegion = QRegion(m_boundingRect);
+}
+
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return &ident; }
+ QRect scissorRect() const override { return QRect(); }
+ bool scissorEnabled() const override { return false; }
+ int stencilValue() const override { return 0; }
+ bool stencilEnabled() const override { return false; }
+ const QRegion *clipRegion() const override { return &cr; }
+ QMatrix4x4 ident;
+ QRegion cr;
+};
+
+QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
+{
+ Q_ASSERT(painter);
+
+ // Check for don't paint conditions
+ if (m_nodeType != RenderNode) {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ }
+ } else {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ } else {
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
+ QMatrix4x4 m = m_transform;
+ rd->m_matrix = &m;
+ rd->m_opacity = m_opacity;
+ RenderNodeState rs;
+ rs.cr = m_clipRegion;
+
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRect :
+ QRect(0, 0, painter->device()->width(), painter->device()->height());
+
+ painter->save();
+ painter->setClipRegion(br, Qt::ReplaceClip);
+ m_handle.renderNode->render(&rs);
+ painter->restore();
+
+ m_previousDirtyRegion = QRegion(br);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return br;
+ }
+ }
+
+ painter->save();
+ painter->setOpacity(m_opacity);
+
+ // Set clipRegion to m_dirtyRegion (in world coordinates)
+ // as m_dirtyRegion already accounts for clipRegion
+ painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
+ if (m_clipRegion.rectCount() > 1)
+ painter->setClipRegion(m_clipRegion, Qt::IntersectClip);
+
+ painter->setTransform(m_transform, false); //precalculated worldTransform
+ if (forceOpaquePainting || m_isOpaque)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ {
+ QSGTexture *texture = m_handle.simpleTextureNode->texture();
+ if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
+ }
+ }
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ m_handle.imageNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ m_handle.painterNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ m_handle.rectangleNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ m_handle.glpyhNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ m_handle.ninePatchNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ painter->restore();
+
+ QRegion areaToBeFlushed = m_dirtyRegion;
+ m_previousDirtyRegion = QRegion(m_boundingRect);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+
+ return areaToBeFlushed;
+}
+
+QRect QSGSoftwareRenderableNode::boundingRect() const
+{
+ // This returns the bounding area of a renderable node in world coordinates
+ return m_boundingRect;
+}
+
+bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
+{
+ return m_dirtyRegion.isEmpty();
+}
+
+void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
+{
+ if (m_transform == transform)
+ return;
+ m_transform = transform;
+ update();
+}
+
+void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion)
+{
+ if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
+ return;
+
+ m_clipRegion = clipRect;
+ m_hasClipRegion = hasClipRegion;
+ update();
+}
+
+void QSGSoftwareRenderableNode::setOpacity(float opacity)
+{
+ if (qFuzzyCompare(m_opacity, opacity))
+ return;
+
+ m_opacity = opacity;
+ update();
+}
+
+void QSGSoftwareRenderableNode::markGeometryDirty()
+{
+ update();
+}
+
+void QSGSoftwareRenderableNode::markMaterialDirty()
+{
+ update();
+}
+
+void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
+{
+ // Check if the dirty region applys to this node
+ QRegion prev = m_dirtyRegion;
+ if (dirtyRegion.intersects(boundingRect())) {
+ if (forceDirty)
+ m_isDirty = true;
+ m_dirtyRegion += dirtyRegion.intersected(boundingRect());
+ }
+ qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
+{
+ QRegion prev = m_dirtyRegion;
+ if (m_isDirty) {
+ // Check if this rect concerns us
+ if (dirtyRegion.intersects(QRegion(boundingRect()))) {
+ m_dirtyRegion -= dirtyRegion;
+ if (m_dirtyRegion.isEmpty())
+ m_isDirty = false;
+ }
+ }
+ qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
+{
+ // When removing a node, the boundingRect shouldn't be subtracted
+ // because a deleted node has no valid boundingRect
+ if (wasRemoved)
+ return m_previousDirtyRegion;
+
+ return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect));
+}
+
+QRegion QSGSoftwareRenderableNode::dirtyRegion() const
+{
+ return m_dirtyRegion;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
new file mode 100644
index 0000000000..473578c185
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERABLENODE_H
+#define QSGSOFTWARERENDERABLENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+
+#include <QtGui/QRegion>
+#include <QtCore/QRect>
+#include <QtGui/QTransform>
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSimpleRectNode;
+class QSGSimpleTextureNode;
+class QSGSoftwareInternalImageNode;
+class QSGSoftwarePainterNode;
+class QSGSoftwareInternalRectangleNode;
+class QSGSoftwareGlyphNode;
+class QSGSoftwareNinePatchNode;
+class QSGSoftwareSpriteNode;
+class QSGRenderNode;
+
+class QSGSoftwareRenderableNode
+{
+public:
+ enum NodeType {
+ Invalid = -1,
+ SimpleRect,
+ SimpleTexture,
+ Image,
+ Painter,
+ Rectangle,
+ Glyph,
+ NinePatch,
+ SimpleRectangle,
+ SimpleImage,
+#if QT_CONFIG(quick_sprite)
+ SpriteNode,
+#endif
+ RenderNode
+ };
+
+ QSGSoftwareRenderableNode(NodeType type, QSGNode *node);
+ ~QSGSoftwareRenderableNode();
+
+ void update();
+
+ QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false);
+ QRect boundingRect() const;
+ NodeType type() const { return m_nodeType; }
+ bool isOpaque() const { return m_isOpaque; }
+ bool isDirty() const { return m_isDirty; }
+ bool isDirtyRegionEmpty() const;
+
+ void setTransform(const QTransform &transform);
+ void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true);
+ void setOpacity(float opacity);
+ QTransform transform() const { return m_transform; }
+ QRegion clipRegion() const { return m_clipRegion; }
+ float opacity() const { return m_opacity; }
+
+ void markGeometryDirty();
+ void markMaterialDirty();
+
+ void addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty = true);
+ void subtractDirtyRegion(const QRegion &dirtyRegion);
+
+ QRegion previousDirtyRegion(bool wasRemoved = false) const;
+ QRegion dirtyRegion() const;
+
+private:
+ union RenderableNodeHandle {
+ QSGSimpleRectNode *simpleRectNode;
+ QSGSimpleTextureNode *simpleTextureNode;
+ QSGSoftwareInternalImageNode *imageNode;
+ QSGSoftwarePainterNode *painterNode;
+ QSGSoftwareInternalRectangleNode *rectangleNode;
+ QSGSoftwareGlyphNode *glpyhNode;
+ QSGSoftwareNinePatchNode *ninePatchNode;
+ QSGRectangleNode *simpleRectangleNode;
+ QSGImageNode *simpleImageNode;
+ QSGSoftwareSpriteNode *spriteNode;
+ QSGRenderNode *renderNode;
+ };
+
+ const NodeType m_nodeType;
+ RenderableNodeHandle m_handle;
+
+ bool m_isOpaque;
+
+ bool m_isDirty;
+ QRegion m_dirtyRegion;
+ QRegion m_previousDirtyRegion;
+
+ QTransform m_transform;
+ QRegion m_clipRegion;
+ bool m_hasClipRegion;
+ float m_opacity;
+
+ QRect m_boundingRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERABLENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
new file mode 100644
index 0000000000..4937565aa9
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+
+#include "qsgabstractsoftwarerenderer_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/qsgsimplerectnode.h>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderableNodeUpdater::QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+ m_opacityState.push(1.0f);
+ // Invalid RectF by default for no clip
+ m_clipState.push(QRegion());
+ m_hasClip = false;
+ m_transformState.push(QTransform());
+}
+
+QSGSoftwareRenderableNodeUpdater::~QSGSoftwareRenderableNodeUpdater()
+{
+
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGTransformNode *node)
+{
+ m_transformState.push(node->matrix().toTransform() * m_transformState.top());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *)
+{
+ m_transformState.pop();
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node)
+{
+ // Make sure to translate the clip rect into world coordinates
+ if (m_clipState.count() == 1) {
+ m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect())));
+ m_hasClip = true;
+ } else {
+ const QRegion transformedClipRect = m_transformState.top().map(QRegion(node->clipRect().toRect()));
+ m_clipState.push(transformedClipRect.intersected(m_clipState.top()));
+ }
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *)
+{
+ m_clipState.pop();
+ if (m_clipState.count() == 1)
+ m_hasClip = false;
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node)
+{
+ if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRect, rectNode);
+ } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleTexture, tn);
+ } else if (QSGNinePatchNode *nn = dynamic_cast<QSGNinePatchNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, nn);
+ } else if (QSGRectangleNode *rn = dynamic_cast<QSGRectangleNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRectangle, rn);
+ } else if (QSGImageNode *n = dynamic_cast<QSGImageNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleImage, n);
+ } else {
+ // We dont know, so skip
+ return false;
+ }
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGeometryNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGOpacityNode *node)
+{
+ m_opacityState.push(m_opacityState.top() * node->opacity());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGOpacityNode *)
+{
+ m_opacityState.pop();
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalImageNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Image, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalImageNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGPainterNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Painter, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalRectangleNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGGlyphNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Glyph, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node)
+{
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *)
+{
+}
+
+#if QT_CONFIG(quick_sprite)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *)
+{
+
+}
+#endif
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRenderNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::RenderNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRenderNode *)
+{
+}
+
+void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved)
+{
+ m_opacityState.clear();
+ m_clipState.clear();
+ m_transformState.clear();
+
+ auto parentNode = node->parent();
+ // If the node was deleted, it will have no parent
+ // check if the state map has the previous parent
+ if ((!parentNode || isNodeRemoved ) && m_stateMap.contains(node))
+ parentNode = m_stateMap[node].parent;
+
+ // If we find a parent, use its state for updating the new children
+ if (parentNode && m_stateMap.contains(parentNode)) {
+ auto state = m_stateMap[parentNode];
+ m_opacityState.push(state.opacity);
+ m_transformState.push(state.transform);
+ m_clipState.push(state.clip);
+ m_hasClip = state.hasClip;
+ } else {
+ // There is no parent, and no previous parent, so likely a root node
+ m_opacityState.push(1.0f);
+ m_transformState.push(QTransform());
+ m_clipState.push(QRegion());
+ m_hasClip = false;
+ }
+
+ // If the node is being removed, then cleanup the state data
+ // Then just visit the children without visiting the now removed node
+ if (isNodeRemoved) {
+ m_stateMap.remove(node);
+ return;
+ }
+
+ // Visit the current node itself first
+ switch (node->type()) {
+ case QSGNode::ClipNodeType: {
+ QSGClipNode *c = static_cast<QSGClipNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::TransformNodeType: {
+ QSGTransformNode *c = static_cast<QSGTransformNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::OpacityNodeType: {
+ QSGOpacityNode *c = static_cast<QSGOpacityNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::GeometryNodeType: {
+ if (node->flags() & QSGNode::IsVisitableNode) {
+ QSGVisitableNode *v = static_cast<QSGVisitableNode*>(node);
+ v->accept(this);
+ } else {
+ QSGGeometryNode *c = static_cast<QSGGeometryNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ }
+ break;
+ }
+ case QSGNode::RootNodeType: {
+ QSGRootNode *root = static_cast<QSGRootNode*>(node);
+ if (visit(root))
+ visitChildren(root);
+ endVisit(root);
+ break;
+ }
+ case QSGNode::BasicNodeType: {
+ visitChildren(node);
+ break;
+ }
+ case QSGNode::RenderNodeType: {
+ QSGRenderNode *r = static_cast<QSGRenderNode*>(node);
+ if (visit(r))
+ visitChildren(r);
+ endVisit(r);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QSGSoftwareRenderableNodeUpdater::NodeState QSGSoftwareRenderableNodeUpdater::currentState(QSGNode *node) const
+{
+ NodeState state;
+ state.opacity = m_opacityState.top();
+ state.clip = m_clipState.top();
+ state.hasClip = m_hasClip;
+ state.transform = m_transformState.top();
+ state.parent = node->parent();
+ return state;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
new file mode 100644
index 0000000000..ca16f77c22
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERABLENODEUPDATER_H
+#define QSGSOFTWARERENDERABLENODEUPDATER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgsoftwarerenderablenode_p.h"
+#include "qsgabstractsoftwarerenderer_p.h"
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QTransform>
+#include <QStack>
+#include <QRectF>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderableNodeUpdater : public QSGNodeVisitorEx
+{
+public:
+ QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer);
+ virtual ~QSGSoftwareRenderableNodeUpdater();
+
+ bool visit(QSGTransformNode *) override;
+ void endVisit(QSGTransformNode *) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+#if QT_CONFIG(quick_sprite)
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+#endif
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) override;
+
+ void updateNodes(QSGNode *node, bool isNodeRemoved = false);
+
+private:
+ struct NodeState {
+ float opacity;
+ QRegion clip;
+ bool hasClip;
+ QTransform transform;
+ QSGNode *parent;
+ };
+
+ NodeState currentState(QSGNode *node) const;
+
+ template<class NODE>
+ bool updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node);
+
+ QSGAbstractSoftwareRenderer *m_renderer;
+ QStack<float> m_opacityState;
+ QStack<QRegion> m_clipState;
+ bool m_hasClip;
+ QStack<QTransform> m_transformState;
+ QHash<QSGNode*,NodeState> m_stateMap;
+};
+
+template<class NODE>
+bool QSGSoftwareRenderableNodeUpdater::updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node)
+{
+ //Check if we already know about node
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ renderableNode = new QSGSoftwareRenderableNode(type, node);
+ m_renderer->addNodeMapping(node, renderableNode);
+ }
+
+ //Update the node
+ renderableNode->setTransform(m_transformState.top());
+ renderableNode->setOpacity(m_opacityState.top());
+ renderableNode->setClipRegion(m_clipState.top(), m_hasClip);
+
+ renderableNode->update();
+ m_stateMap[node] = currentState(node);
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERABLENODEUPDATER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
new file mode 100644
index 0000000000..cad826fb27
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderer_p.h"
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+#include "qsgsoftwarerenderlistbuilder_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include <QtGui/QPaintDevice>
+#include <QtGui/QBackingStore>
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context)
+ : QSGAbstractSoftwareRenderer(context)
+ , m_paintDevice(nullptr)
+ , m_backingStore(nullptr)
+{
+}
+
+QSGSoftwareRenderer::~QSGSoftwareRenderer()
+{
+}
+
+void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device)
+{
+ m_paintDevice = device;
+ m_backingStore = nullptr;
+}
+
+QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const
+{
+ return m_paintDevice;
+}
+
+void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore)
+{
+ m_backingStore = backingStore;
+ m_paintDevice = nullptr;
+}
+
+QRegion QSGSoftwareRenderer::flushRegion() const
+{
+ return m_flushRegion;
+}
+
+void QSGSoftwareRenderer::renderScene(uint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void QSGSoftwareRenderer::render()
+{
+ if (!m_paintDevice && !m_backingStore)
+ return;
+
+ // If there is a backingstore, set the current paint device
+ if (m_backingStore) {
+ // For HiDPI QBackingStores, the paint device is not valid
+ // until begin() has been called. See: QTBUG-55875
+ m_backingStore->beginPaint(QRegion());
+ m_paintDevice = m_backingStore->paintDevice();
+ m_backingStore->endPaint();
+ }
+
+ QElapsedTimer renderTimer;
+
+ setBackgroundColor(clearColor());
+ setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
+ m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
+
+ // Build Renderlist
+ // The renderlist is created by visiting each node in the tree and when a
+ // renderable node is reach, we find the coorosponding RenderableNode object
+ // and append it to the renderlist. At this point the RenderableNode object
+ // should not need any further updating so it is just a matter of appending
+ // RenderableNodes
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // This is a pass through the renderlist to determine what actually needs to
+ // be painted. Without this pass the renderlist will simply render each item
+ // from back to front, with a high potential for overdraw. It would also lead
+ // to the entire window being flushed every frame. The objective of the
+ // optimization pass is to only paint dirty nodes that are not occuluded. A
+ // side effect of this is that additional nodes may need to be marked dirty to
+ // force a repaint. It is also important that any item that needs to be
+ // repainted only paints what is needed, via the use of clip regions.
+ const QRegion updateRegion = optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ // If Rendering to a backingstore, prepare it to be updated
+ if (m_backingStore != nullptr) {
+ m_backingStore->beginPaint(updateRegion);
+ // It is possible that a QBackingStore's paintDevice() will change
+ // when begin() is called.
+ m_paintDevice = m_backingStore->paintDevice();
+ }
+
+ QPainter painter(m_paintDevice);
+ painter.setRenderHint(QPainter::Antialiasing);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
+
+ // Render the contents Renderlist
+ m_flushRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ if (m_backingStore != nullptr)
+ m_backingStore->endPaint();
+
+ rc->m_activePainter = prevPainter;
+ qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
new file mode 100644
index 0000000000..bb28da4ca5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERER_H
+#define QSGSOFTWARERENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QBackingStore;
+
+class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
+{
+public:
+ QSGSoftwareRenderer(QSGRenderContext *context);
+ virtual ~QSGSoftwareRenderer();
+
+ void setCurrentPaintDevice(QPaintDevice *device);
+ QPaintDevice *currentPaintDevice() const;
+ void setBackingStore(QBackingStore *backingStore);
+ QRegion flushRegion() const;
+
+protected:
+ void renderScene(uint fboId = 0) final;
+ void render() final;
+
+private:
+ QPaintDevice* m_paintDevice;
+ QBackingStore* m_backingStore;
+ QRegion m_flushRegion;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
new file mode 100644
index 0000000000..70dce71801
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderlistbuilder_p.h"
+
+#include "qsgsoftwarerenderablenode_p.h"
+#include "qsgabstractsoftwarerenderer_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/qsgsimplerectnode.h>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderListBuilder::QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGTransformNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGTransformNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGClipNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGClipNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGGeometryNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGGeometryNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGOpacityNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGOpacityNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGInternalImageNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalImageNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGPainterNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGInternalRectangleNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGGlyphNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *)
+{
+}
+
+#if QT_CONFIG(quick_sprite)
+bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *)
+{
+
+}
+#endif
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRenderNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRenderNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::addRenderableNode(QSGNode *node)
+{
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ // Not a node we can render
+ return false;
+ }
+ m_renderer->appendRenderableNode(renderableNode);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
new file mode 100644
index 0000000000..f5db30269f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERLISTBUILDER_H
+#define QSGSOFTWARERENDERLISTBUILDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGAbstractSoftwareRenderer;
+
+class QSGSoftwareRenderListBuilder : public QSGNodeVisitorEx
+{
+public:
+ QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer);
+
+ bool visit(QSGTransformNode *) override;
+ void endVisit(QSGTransformNode *) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+#if QT_CONFIG(quick_sprite)
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+#endif
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) override;
+
+private:
+ bool addRenderableNode(QSGNode *node);
+
+ QSGAbstractSoftwareRenderer *m_renderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERLISTBUILDER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
new file mode 100644
index 0000000000..19a963b403
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderloop_p.h"
+
+#include "qsgsoftwarecontext_p.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <private/qquickwindow_p.h>
+#include <QElapsedTimer>
+#include <private/qquickprofiler_p.h>
+#include <private/qsgsoftwarerenderer_p.h>
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtGui/QBackingStore>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderLoop::QSGSoftwareRenderLoop()
+{
+ sg = new QSGSoftwareContext();
+ rc = sg->createRenderContext();
+}
+
+QSGSoftwareRenderLoop::~QSGSoftwareRenderLoop()
+{
+ delete rc;
+ delete sg;
+}
+
+void QSGSoftwareRenderLoop::show(QQuickWindow *window)
+{
+ WindowData data;
+ data.updatePending = false;
+ data.grabOnly = false;
+ m_windows[window] = data;
+
+ if (m_backingStores[window] == nullptr) {
+ m_backingStores[window] = new QBackingStore(window);
+ }
+
+ maybeUpdate(window);
+}
+
+void QSGSoftwareRenderLoop::hide(QQuickWindow *window)
+{
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->fireAboutToStop();
+}
+
+void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ m_windows.remove(window);
+ delete m_backingStores[window];
+ m_backingStores.remove(window);
+ hide(window);
+
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ d->cleanupNodesOnShutdown();
+
+ if (m_windows.size() == 0) {
+ rc->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+}
+
+void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
+{
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (!m_windows.contains(window))
+ return;
+
+ WindowData &data = const_cast<WindowData &>(m_windows[window]);
+
+ //If were not in grabOnly mode, dont render a non-renderable window
+ if (!data.grabOnly && !cd->isRenderable())
+ return;
+
+ //Resize the backing store if necessary
+ if (m_backingStores[window]->size() != window->size()) {
+ m_backingStores[window]->resize(window->size());
+ }
+
+ // ### create QPainter and set up pointer to current window/painter
+ QSGSoftwareRenderContext *ctx = static_cast<QSGSoftwareRenderContext*>(cd->context);
+ ctx->initializeIfNeeded();
+
+ bool alsoSwap = data.updatePending;
+ data.updatePending = false;
+
+ if (!data.grabOnly) {
+ cd->flushFrameSynchronousEvents();
+ // Event delivery/processing triggered the window to be deleted or stop rendering.
+ if (!m_windows.contains(window))
+ return;
+ }
+ QElapsedTimer renderTimer;
+ qint64 renderTime = 0, syncTime = 0, polishTime = 0;
+ bool profileFrames = QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled();
+ if (profileFrames)
+ renderTimer.start();
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
+
+ cd->polishItems();
+
+ if (profileFrames)
+ polishTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
+ QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ emit window->afterAnimating();
+
+ cd->syncSceneGraph();
+
+ if (profileFrames)
+ syncTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ //Tell the renderer about the windows backing store
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(m_backingStores[window]);
+
+ cd->renderSceneGraph(window->size());
+
+ if (profileFrames)
+ renderTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (data.grabOnly) {
+ grabContent = m_backingStores[window]->handle()->toImage();
+ data.grabOnly = false;
+ }
+
+ if (alsoSwap && window->isVisible()) {
+ //Flush backingstore to window
+ m_backingStores[window]->flush(softwareRenderer->flushRegion());
+ cd->fireFrameSwapped();
+ }
+
+ qint64 swapTime = 0;
+ if (profileFrames)
+ swapTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
+ static QTime lastFrameTime = QTime::currentTime();
+ qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP,
+ "Frame rendered with 'software' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
+ int(swapTime / 1000000),
+ int(polishTime / 1000000),
+ int((syncTime - polishTime) / 1000000),
+ int((renderTime - syncTime) / 1000000),
+ int((swapTime - renderTime) / 10000000),
+ int(lastFrameTime.msecsTo(QTime::currentTime())));
+ lastFrameTime = QTime::currentTime();
+ }
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(window);
+}
+
+void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (window->isExposed()) {
+ m_windows[window].updatePending = true;
+ renderWindow(window);
+ }
+}
+
+QImage QSGSoftwareRenderLoop::grab(QQuickWindow *window)
+{
+ //If the window was never shown, create a new backing store
+ if (!m_backingStores.contains(window)) {
+ m_backingStores[window] = new QBackingStore(window);
+ // Call create on window to make sure platform window is created
+ window->create();
+ }
+
+ //If there is no WindowData, add one
+ if (!m_windows.contains(window)) {
+ WindowData data;
+ data.updatePending = false;
+ m_windows[window] = data;
+ }
+
+ m_windows[window].grabOnly = true;
+
+ renderWindow(window);
+
+ QImage grabbed = grabContent;
+ grabbed.detach();
+ grabContent = QImage();
+ return grabbed;
+}
+
+
+
+void QSGSoftwareRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows[window].updatePending = true;
+ window->requestUpdate();
+}
+
+QSurface::SurfaceType QSGSoftwareRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+
+
+QSGContext *QSGSoftwareRenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+
+void QSGSoftwareRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ renderWindow(window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h
new file mode 100644
index 0000000000..02dcf4eefa
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERLOOP_H
+#define QSGSOFTWARERENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QBackingStore;
+
+class QSGSoftwareRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderLoop();
+ ~QSGSoftwareRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+
+ void windowDestroyed(QQuickWindow *window) override;
+
+ void renderWindow(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+
+ void maybeUpdate(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation.
+ void handleUpdateRequest(QQuickWindow *) override;
+
+ void releaseResources(QQuickWindow *) override { }
+
+ QSurface::SurfaceType windowSurfaceType() const override;
+
+ QAnimationDriver *animationDriver() const override { return 0; }
+
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; }
+
+ struct WindowData {
+ bool updatePending : 1;
+ bool grabOnly : 1;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+ QHash<QQuickWindow *, QBackingStore *> m_backingStores;
+
+ QSGContext *sg;
+ QSGRenderContext *rc;
+
+ QImage grabContent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
new file mode 100644
index 0000000000..ba7bbc2d11
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarespritenode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareSpriteNode::QSGSoftwareSpriteNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareSpriteNode::setTexture(QSGTexture *texture)
+{
+ m_texture = qobject_cast<QSGSoftwarePixmapTexture*>(texture);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareSpriteNode::setTime(float time)
+{
+ if (m_time != time) {
+ m_time = time;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceA(const QPoint &source)
+{
+ if (m_sourceA != source) {
+ m_sourceA = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceB(const QPoint &source)
+{
+ if (m_sourceB != source) {
+ m_sourceB = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSpriteSize(const QSize &size)
+{
+ if (m_spriteSize != size) {
+ m_spriteSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSheetSize(const QSize &size)
+{
+ if (m_sheetSize != size) {
+ m_sheetSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSize(const QSizeF &size)
+{
+ if (m_size != size) {
+ m_size = size;
+ markDirty(DirtyGeometry);
+ }
+}
+
+void QSGSoftwareSpriteNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ Q_UNUSED(filtering);
+}
+
+void QSGSoftwareSpriteNode::update()
+{
+}
+
+void QSGSoftwareSpriteNode::paint(QPainter *painter)
+{
+ //Get the pixmap handle from the texture
+ if (!m_texture)
+ return;
+
+ const QPixmap &pixmap = m_texture->pixmap();
+
+ // XXX try to do some kind of interpolation between sourceA and sourceB using time
+ painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()),
+ pixmap,
+ QRectF(m_sourceA, m_spriteSize));
+}
+
+bool QSGSoftwareSpriteNode::isOpaque() const
+{
+ return false;
+}
+
+QRectF QSGSoftwareSpriteNode::rect() const
+{
+ return QRectF(0, 0, m_size.width(), m_size.height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
new file mode 100644
index 0000000000..577a30c051
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARESPRITENODE_H
+#define QSGSOFTWARESPRITENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapTexture;
+class QSGSoftwareSpriteNode : public QSGSpriteNode
+{
+public:
+ QSGSoftwareSpriteNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setTime(float time) override;
+ void setSourceA(const QPoint &source) override;
+ void setSourceB(const QPoint &source) override;
+ void setSpriteSize(const QSize &size) override;
+ void setSheetSize(const QSize &size) override;
+ void setSize(const QSizeF &size) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+ bool isOpaque() const;
+ QRectF rect() const;
+
+private:
+
+ QSGSoftwarePixmapTexture *m_texture;
+ float m_time;
+ QPoint m_sourceA;
+ QPoint m_sourceB;
+ QSize m_spriteSize;
+ QSize m_sheetSize;
+ QSizeF m_size;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARESPRITENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
new file mode 100644
index 0000000000..5d5485ed8f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -0,0 +1,991 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarethreadedrenderloop_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderer_p.h"
+
+#include <private/qsgrenderer_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtCore/QQueue>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QBackingStore>
+#include <QtQuick/QQuickWindow>
+
+QT_BEGIN_NAMESPACE
+
+// Passed from the RL to the RT when a window is removed obscured and should be
+// removed from the render loop.
+const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync.
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
+
+// Passed by the RT to itself to trigger another render pass. This is typically
+// a result of QQuickWindow::update().
+const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
+
+// Passed by the RL to the RT to maybe release resource if no windows are
+// rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+
+// Passed by the window when there is a render job to run.
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
+class QSGSoftwareWindowEvent : public QEvent
+{
+public:
+ QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
+ : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ bool destroying;
+};
+
+class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
+ : QSGSoftwareWindowEvent(c, WM_RequestSync)
+ , size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
+ , syncInExpose(inExpose)
+ , forceRenderPass(force) { }
+ QSize size;
+ float dpr;
+ bool syncInExpose;
+ bool forceRenderPass;
+};
+
+class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
+ : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { }
+ QImage *image;
+};
+
+class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { }
+ ~QSGSoftwareJobEvent() { delete job; }
+ QRunnable *job;
+};
+
+class QSGSoftwareEventQueue : public QQueue<QEvent *>
+{
+public:
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (isEmpty() && wait) {
+ waiting = true;
+ condition.wait(&mutex);
+ waiting = false;
+ }
+ QEvent *e = dequeue();
+ mutex.unlock();
+ return e;
+ }
+
+ bool hasMoreEvents() {
+ mutex.lock();
+ bool has = !isEmpty();
+ mutex.unlock();
+ return has;
+ }
+
+private:
+ QMutex mutex;
+ QWaitCondition condition;
+ bool waiting = false;
+};
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+class QSGSoftwareRenderThread : public QThread
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
+ : renderLoop(rl)
+ {
+ rc = static_cast<QSGSoftwareRenderContext *>(renderContext);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+ ~QSGSoftwareRenderThread()
+ {
+ delete rc;
+ }
+
+ bool event(QEvent *e);
+ void run();
+
+ void syncAndRender();
+ void sync(bool inExpose);
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (exposedWindow)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02,
+ ExposeRequest = 0x04 | RepaintRequest | SyncRequest
+ };
+
+ QSGSoftwareThreadedRenderLoop *renderLoop;
+ QSGSoftwareRenderContext *rc;
+ QAnimationDriver *rtAnim = nullptr;
+ volatile bool active = false;
+ uint pendingUpdate = 0;
+ bool sleeping = false;
+ bool syncResultedInChanges = false;
+ float vsyncDelta;
+ QMutex mutex;
+ QWaitCondition waitCondition;
+ QQuickWindow *exposedWindow = nullptr;
+ QBackingStore *backingStore = nullptr;
+ bool stopEventProcessing = false;
+ QSGSoftwareEventQueue eventQueue;
+ QElapsedTimer renderThrottleTimer;
+ qint64 syncTime;
+ qint64 renderTime;
+ qint64 sinceLastTime;
+
+public slots:
+ void onSceneGraphChanged() {
+ syncResultedInChanges = true;
+ }
+};
+
+bool QSGSoftwareRenderThread::event(QEvent *e)
+{
+ switch ((int)e->type()) {
+
+ case WM_Obscure:
+ Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
+ mutex.lock();
+ if (exposedWindow) {
+ QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
+ exposedWindow = nullptr;
+ delete backingStore;
+ backingStore = nullptr;
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_RequestSync: {
+ QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
+ if (sleeping)
+ stopEventProcessing = true;
+ exposedWindow = wme->window;
+ if (backingStore == nullptr)
+ backingStore = new QBackingStore(exposedWindow);
+ if (backingStore->size() != exposedWindow->size())
+ backingStore->resize(exposedWindow->size());
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
+ pendingUpdate |= SyncRequest;
+ if (wme->syncInExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
+ pendingUpdate |= ExposeRequest;
+ }
+ if (wme->forceRenderPass) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
+ pendingUpdate |= RepaintRequest;
+ }
+ return true;
+ }
+
+ case WM_TryRelease: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
+ mutex.lock();
+ renderLoop->lockedForSync = true;
+ QSGSoftwareTryReleaseEvent *wme = static_cast<QSGSoftwareTryReleaseEvent *>(e);
+ // Only when no windows are exposed anymore or we are shutting down.
+ if (!exposedWindow || wme->destroying) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
+ if (wme->window) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ if (wme->destroying) {
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+ }
+ rc->invalidate();
+ QCoreApplication::processEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ if (wme->destroying)
+ delete wd->animationController;
+ }
+ if (wme->destroying)
+ active = false;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
+ }
+ waitCondition.wakeOne();
+ renderLoop->lockedForSync = false;
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_Grab: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
+ QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
+ Q_ASSERT(wme->window);
+ Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
+ mutex.lock();
+ if (wme->window) {
+ // Grabbing is generally done by rendering a frame and reading the
+ // color buffer contents back, without presenting, and then
+ // creating a QImage from the returned data. It is terribly
+ // inefficient since it involves a full blocking wait for the GPU.
+ // However, our hands are tied by the existing, synchronous APIs of
+ // QQuickWindow and such.
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+ wd->renderSceneGraph(wme->window->size());
+ *wme->image = backingStore->handle()->toImage();
+ }
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_PostJob: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
+ QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
+ Q_ASSERT(wme->window == exposedWindow);
+ if (exposedWindow) {
+ wme->job->run();
+ delete wme->job;
+ wme->job = nullptr;
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
+ }
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint");
+ // When GUI posts this event, it is followed by a polishAndSync, so we
+ // must not exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+
+ return QThread::event(e);
+}
+
+void QSGSoftwareRenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+void QSGSoftwareRenderThread::processEvents()
+{
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::processEventsAndWaitForMore()
+{
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::run()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
+
+ rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
+ rtAnim->install();
+
+ if (QQmlDebugConnector::service<QQmlProfilerService>())
+ QQuickProfiler::registerAnimationCallback();
+
+ renderThrottleTimer.start();
+
+ while (active) {
+ if (exposedWindow)
+ syncAndRender();
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (pendingUpdate == 0 || !exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
+
+ delete rtAnim;
+ rtAnim = nullptr;
+
+ rc->moveToThread(renderLoop->thread());
+ moveToThread(renderLoop->thread());
+}
+
+void QSGSoftwareRenderThread::sync(bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
+
+ mutex.lock();
+ Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked");
+
+ if (exposedWindow) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+ bool hadRenderer = wd->renderer != nullptr;
+ // If the scene graph was touched since the last sync() make sure it sends the
+ // changed signal.
+ if (wd->renderer)
+ wd->renderer->clearChangedFlag();
+
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+
+ if (!hadRenderer && wd->renderer) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
+ syncResultedInChanges = true;
+ connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
+ &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
+ }
+
+ // Process deferred deletes now, directly after the sync as deleteLater
+ // on the GUI must now also have resulted in SG changes and the delete
+ // is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+ if (!inExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+}
+
+void QSGSoftwareRenderThread::syncAndRender()
+{
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
+
+ syncResultedInChanges = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage;
+ const bool syncRequested = pendingUpdate & SyncRequest;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ pendingUpdate = 0;
+
+ if (syncRequested)
+ sync(exposeRequested);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!syncResultedInChanges && !repaintRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
+
+ if (rtAnim->isRunning()) {
+ wd->animationController->lock();
+ rtAnim->advance();
+ wd->animationController->unlock();
+ }
+
+ bool canRender = wd->renderer != nullptr;
+
+ if (canRender) {
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ wd->renderSceneGraph(exposedWindow->size());
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap()))
+ backingStore->flush(softwareRenderer->flushRegion());
+
+ // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
+ // rate of the current screen the window is on.
+ int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
+ if (blockTime > 0) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms";
+ msleep(blockTime);
+ }
+ renderThrottleTimer.restart();
+
+ wd->fireFrameSwapped();
+ } else {
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
+
+ if (exposeRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+}
+
+template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+{
+ for (const T &t : list) {
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return nullptr;
+}
+
+
+QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
+ m_sg = new QSGSoftwareContext;
+ m_anim = m_sg->createAnimationDriver(this);
+ connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
+ connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
+ m_anim->install();
+}
+
+QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
+ delete m_sg;
+}
+
+void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
+}
+
+void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(m_windows, window));
+
+ releaseResources(window);
+}
+
+void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
+}
+
+void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ handleObscurity(w);
+ handleResourceRelease(w, true);
+
+ QSGSoftwareRenderThread *thread = w->thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleObscurity(w);
+ }
+}
+
+QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ // Have to support invisible (but created()'ed) windows as well.
+ // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
+ const bool tempExpose = !w;
+ if (tempExpose) {
+ handleExposure(window);
+ w = windowFor(m_windows, window);
+ Q_ASSERT(w);
+ }
+
+ if (!w->thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ QImage result;
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+
+ result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
+
+ if (tempExpose)
+ handleObscurity(w);
+
+ return result;
+}
+
+void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ w->thread->requestRepaint();
+ return;
+ }
+
+ // We set forceRenderPass because we want to make sure the QQuickWindow
+ // actually does a full render pass after the next sync.
+ w->forceRenderPass = true;
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const
+{
+ return m_anim;
+}
+
+QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const
+{
+ return m_sg;
+}
+
+QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const
+{
+ return m_sg->createRenderContext();
+}
+
+void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w && w->thread && w->thread->exposedWindow)
+ w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
+ else
+ delete job;
+}
+
+QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const
+{
+ bool somethingVisible = false;
+ for (const WindowData &w : m_windows) {
+ if (w.window->isVisible() && w.window->isExposed()) {
+ somethingVisible = true;
+ break;
+ }
+ }
+ return somethingVisible && m_anim->isRunning();
+}
+
+int QSGSoftwareThreadedRenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGSoftwareThreadedRenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == animationTimer) {
+ m_anim->advance();
+ emit timeToIncubate();
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(m_windows))
+ w.window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindowCount = 0;
+ const WindowData *exposed = nullptr;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ const WindowData &w(m_windows[i]);
+ if (w.window->isVisible() && w.window->isExposed()) {
+ ++exposedWindowCount;
+ exposed = &w;
+ }
+ }
+
+ if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
+ killTimer(animationTimer);
+ animationTimer = 0;
+ // If animations are running, make sure we keep on animating
+ if (m_anim->isRunning())
+ exposed->window->requestUpdate();
+ } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
+ animationTimer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
+ WindowData win;
+ win.window = window;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
+ win.thread = new QSGSoftwareRenderThread(this, rc);
+ win.updateDuringSync = false;
+ win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
+ m_windows.append(win);
+ w = &m_windows.last();
+ }
+
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // special casing exposure in polishAndSync.
+ w->thread->exposedWindow = window;
+
+ if (w->window->size().isEmpty()
+ || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
+#ifndef QT_NO_DEBUG
+ qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
+ << w->window << " with invalid geometry: " << w->window->geometry()
+ << " on " << w->window->screen();
+#endif
+ }
+
+ if (!w->window->handle())
+ w->window->create();
+
+ // Start render thread if it is not running
+ if (!w->thread->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
+ // Push a few things to the render thread.
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->rc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+
+ w->thread->active = true;
+ w->thread->start();
+
+ if (!w->thread->isRunning())
+ qFatal("Render thread failed to start, aborting application.");
+ }
+
+ polishAndSync(w, true);
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
+
+ if (w->thread->isRunning()) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ if (!QCoreApplication::instance())
+ return;
+
+ if (!w || !w->thread->isRunning())
+ return;
+
+ QThread *current = QThread::currentThread();
+ if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
+ qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
+ return;
+ }
+
+ if (current == w->thread) {
+ w->updateDuringSync = true;
+ return;
+ }
+
+ w->window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
+ QQuickWindow *window = w->window;
+
+ // Note that window->handle() is typically null by this time because
+ // the platform window is already destroyed. This should not be a
+ // problem for the D3D cleanup.
+
+ w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+
+ // Avoid a shutdown race condition.
+ // If SG is invalidated and 'active' becomes false, the thread's run()
+ // method will exit. handleExposure() relies on QThread::isRunning() (because it
+ // potentially needs to start the thread again) and our mutex cannot be used to
+ // track the thread stopping, so we wait a few nanoseconds extra so the thread
+ // can exit properly.
+ if (!w->thread->active)
+ w->thread->wait();
+ }
+ w->thread->mutex.unlock();
+}
+
+void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
+
+ QQuickWindow *window = w->window;
+ if (!w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
+ return;
+ }
+
+ // Flush pending touch events.
+ QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents();
+ // The delivery of the event might have caused the window to stop rendering
+ w = windowFor(m_windows, window);
+ if (!w || !w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
+ return;
+ }
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ w->updateDuringSync = false;
+
+ emit window->afterAnimating();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
+ w->forceRenderPass = false;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ if (!animationTimer && m_anim->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
+ m_anim->advance();
+ // We need to trigger another sync to keep animations running...
+ w->window->requestUpdate();
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ w->window->requestUpdate();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
+}
+
+#include "qsgsoftwarethreadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
new file mode 100644
index 0000000000..99993d651c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H
+#define QSGSOFTWARETHREADEDRENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderThread;
+class QSGSoftwareContext;
+
+class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareThreadedRenderLoop();
+ ~QSGSoftwareThreadedRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+ void windowDestroyed(QQuickWindow *window) override;
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+ QAnimationDriver *animationDriver() const override;
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+ QSurface::SurfaceType windowSurfaceType() const override;
+ bool interleaveIncubation() const override;
+ int flags() const override;
+
+ bool event(QEvent *e) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ struct WindowData {
+ QQuickWindow *window;
+ QSGSoftwareRenderThread *thread;
+ uint updateDuringSync : 1;
+ uint forceRenderPass : 1;
+ };
+
+ void startOrStopAnimationTimer();
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(WindowData *w);
+ void scheduleUpdate(WindowData *w);
+ void handleResourceRelease(WindowData *w, bool destroying);
+ void polishAndSync(WindowData *w, bool inExpose);
+
+ QSGSoftwareContext *m_sg;
+ QAnimationDriver *m_anim;
+ int animationTimer = 0;
+ bool lockedForSync = false;
+ QVector<WindowData> m_windows;
+
+ friend class QSGSoftwareRenderThread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARETHREADEDRENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
new file mode 100644
index 0000000000..de5f01cdee
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -0,0 +1,48 @@
+QT += gui-private core-private qml-private
+
+#DEFINES += QTQUICK2D_DEBUG_FLUSH
+
+SOURCES += \
+ $$PWD/qsgsoftwarecontext.cpp \
+ $$PWD/qsgabstractsoftwarerenderer.cpp \
+ $$PWD/qsgsoftwareglyphnode.cpp \
+ $$PWD/qsgsoftwareinternalimagenode.cpp \
+ $$PWD/qsgsoftwarepublicnodes.cpp \
+ $$PWD/qsgsoftwarepainternode.cpp \
+ $$PWD/qsgsoftwareinternalrectanglenode.cpp \
+ $$PWD/qsgsoftwarepixmaprenderer.cpp \
+ $$PWD/qsgsoftwarepixmaptexture.cpp \
+ $$PWD/qsgsoftwarerenderablenode.cpp \
+ $$PWD/qsgsoftwarerenderablenodeupdater.cpp \
+ $$PWD/qsgsoftwarerenderer.cpp \
+ $$PWD/qsgsoftwarerenderlistbuilder.cpp \
+ $$PWD/qsgsoftwarerenderloop.cpp \
+ $$PWD/qsgsoftwarelayer.cpp \
+ $$PWD/qsgsoftwareadaptation.cpp \
+ $$PWD/qsgsoftwarethreadedrenderloop.cpp
+
+HEADERS += \
+ $$PWD/qsgsoftwarecontext_p.h \
+ $$PWD/qsgabstractsoftwarerenderer_p.h \
+ $$PWD/qsgsoftwareglyphnode_p.h \
+ $$PWD/qsgsoftwareinternalimagenode_p.h \
+ $$PWD/qsgsoftwarepublicnodes_p.h \
+ $$PWD/qsgsoftwarepainternode_p.h \
+ $$PWD/qsgsoftwarepixmaprenderer_p.h \
+ $$PWD/qsgsoftwarepixmaptexture_p.h \
+ $$PWD/qsgsoftwareinternalrectanglenode_p.h \
+ $$PWD/qsgsoftwarerenderablenode_p.h \
+ $$PWD/qsgsoftwarerenderablenodeupdater_p.h \
+ $$PWD/qsgsoftwarerenderer_p.h \
+ $$PWD/qsgsoftwarerenderlistbuilder_p.h \
+ $$PWD/qsgsoftwarerenderloop_p.h \
+ $$PWD/qsgsoftwarelayer_p.h \
+ $$PWD/qsgsoftwareadaptation_p.h \
+ $$PWD/qsgsoftwarethreadedrenderloop_p.h
+
+qtConfig(quick-sprite) {
+ SOURCES += \
+ $$PWD/qsgsoftwarespritenode.cpp
+ HEADERS += \
+ $$PWD/qsgsoftwarespritenode_p.h
+}