diff options
author | Andy Nichols <andy.nichols@theqtcompany.com> | 2016-03-08 15:17:08 +0100 |
---|---|---|
committer | Andy Nichols <andy.nichols@theqtcompany.com> | 2016-03-08 15:20:38 +0100 |
commit | 2e007a4ab09da9cd931b9d7a4ba96b34bfa13157 (patch) | |
tree | 864c5cad3da40b210af604ee6ba3f2caefe825a6 /src/plugins | |
parent | 75423185e4b03fabfeeee403f3bd9a6063078ee6 (diff) | |
parent | d26f29afb5a9a563da74767a348422ca04da945e (diff) |
Merge remote-tracking branch 'qtdeclarative-2drender/dev' into qtdeclarative
This integrates the Qt Quick 2D Renderer into the qtdeclarative module
Change-Id: I3080b3ca0194794e2145b240526f5a693418543b
Diffstat (limited to 'src/plugins')
36 files changed, 4897 insertions, 0 deletions
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 273407c19d..b2ddb5c2c1 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,5 @@ TEMPLATE = subdirs + !contains(QT_CONFIG, no-qml-debug):SUBDIRS += qmltooling + +SUBDIRS += scenegraph diff --git a/src/plugins/scenegraph/scenegraph.pro b/src/plugins/scenegraph/scenegraph.pro new file mode 100644 index 0000000000..807b6871b3 --- /dev/null +++ b/src/plugins/scenegraph/scenegraph.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += softwarecontext diff --git a/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp new file mode 100644 index 0000000000..13aaa6076e --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsoftwarerenderer.h" + +#include "renderablenodeupdater.h" +#include "renderlistbuilder.h" +#include "context.h" +#include "renderablenode.h" + +#include <QtCore/QLoggingCategory> +#include <QtGui/QWindow> +#include <QtQuick/QSGSimpleRectNode> + +Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer") + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext{ + +AbstractSoftwareRenderer::AbstractSoftwareRenderer(QSGRenderContext *context) + : QSGRenderer(context) + , m_background(new QSGSimpleRectNode) + , m_nodeUpdater(new RenderableNodeUpdater(this)) +{ + // Setup special background node + auto backgroundRenderable = new RenderableNode(RenderableNode::SimpleRect, m_background); + addNodeMapping(m_background, backgroundRenderable); +} + +AbstractSoftwareRenderer::~AbstractSoftwareRenderer() +{ + // Cleanup RenderableNodes + delete m_background; + + for (RenderableNode *node : m_nodes.values()) { + delete node; + } + + delete m_nodeUpdater; +} + +RenderableNode *AbstractSoftwareRenderer::renderableNode(QSGNode *node) const +{ + return m_nodes.value(node, nullptr); +} + +void AbstractSoftwareRenderer::addNodeMapping(QSGNode *node, RenderableNode *renderableNode) +{ + m_nodes.insert(node, renderableNode); +} + +void AbstractSoftwareRenderer::appendRenderableNode(RenderableNode *node) +{ + m_renderableNodes.append(node); +} + +void AbstractSoftwareRenderer::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 AbstractSoftwareRenderer::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 AbstractSoftwareRenderer::buildRenderList() +{ + // Clear the previous renderlist + m_renderableNodes.clear(); + // Add the background renderable (always first) + m_renderableNodes.append(renderableNode(m_background)); + // Build the renderlist + RenderListBuilder(this).visitChildren(rootNode()); +} + +void AbstractSoftwareRenderer::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(); + } + + // Empty dirtyRegion + m_dirtyRegion = QRegion(); + m_obscuredRegion = QRegion(); +} + +void AbstractSoftwareRenderer::setBackgroundColor(const QColor &color) +{ + if (m_background->color() == color) + return; + m_background->setColor(color); + renderableNode(m_background)->markMaterialDirty(); +} + +void AbstractSoftwareRenderer::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 + m_dirtyRegion = QRegion(m_background->rect().toRect()); +} + +QColor AbstractSoftwareRenderer::backgroundColor() +{ + return m_background->color(); +} + +QSize AbstractSoftwareRenderer::backgroundSize() +{ + return m_background->rect().size().toSize(); +} + +void AbstractSoftwareRenderer::nodeAdded(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeAdded" << (void*)node; + + m_nodeUpdater->updateNodes(node); +} + +void AbstractSoftwareRenderer::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(); + 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 AbstractSoftwareRenderer::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 AbstractSoftwareRenderer::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 AbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeMaterialUpdated"; + + // Update children nodes + m_nodeUpdater->updateNodes(node); +} + +void AbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeOpacityUpdated"; + + // Update children nodes + m_nodeUpdater->updateNodes(node); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h new file mode 100644 index 0000000000..967c2a1c9a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTSOFTWARERENDERER_H +#define ABSTRACTSOFTWARERENDERER_H + +#include <private/qsgrenderer_p.h> + +#include <QtCore/QHash> +#include <QtCore/QLinkedList> + +QT_BEGIN_NAMESPACE + +class QSGSimpleRectNode; + +namespace SoftwareContext{ + +class RenderableNode; +class RenderableNodeUpdater; + +class AbstractSoftwareRenderer : public QSGRenderer +{ +public: + AbstractSoftwareRenderer(QSGRenderContext *context); + virtual ~AbstractSoftwareRenderer(); + + RenderableNode *renderableNode(QSGNode *node) const; + void addNodeMapping(QSGNode *node, RenderableNode *renderableNode); + void appendRenderableNode(RenderableNode *node); + + void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; + +protected: + QRegion renderNodes(QPainter *painter); + void buildRenderList(); + void 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*, RenderableNode*> m_nodes; + QLinkedList<RenderableNode*> m_renderableNodes; + + QSGSimpleRectNode *m_background; + + QRegion m_dirtyRegion; + QRegion m_obscuredRegion; + + RenderableNodeUpdater *m_nodeUpdater; +}; + +} // namespace + +QT_END_NAMESPACE + +#endif // ABSTRACTSOFTWARERENDERER_H diff --git a/src/plugins/scenegraph/softwarecontext/context.cpp b/src/plugins/scenegraph/softwarecontext/context.cpp new file mode 100644 index 0000000000..b5d3186864 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/context.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "context.h" + +#include "rectanglenode.h" +#include "imagenode.h" +#include "painternode.h" +#include "pixmaptexture.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "softwarelayer.h" +#include "renderer.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QElapsedTimer> + +#include <QtGui/QWindow> + +#include <QtQuick/QSGFlatColorMaterial> +#include <QtQuick/QSGVertexColorMaterial> +#include <QtQuick/QSGOpaqueTextureMaterial> +#include <QtQuick/QSGTextureMaterial> +#include <private/qsgdefaultimagenode_p.h> +#include <private/qsgdefaultrectanglenode_p.h> +#include <private/qsgdistancefieldglyphnode_p_p.h> +#include <private/qsgdefaultglyphnode_p.h> + +#ifndef QSG_NO_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); +#endif + +// 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 + +namespace SoftwareContext +{ + +RenderContext::RenderContext(QSGContext *ctx) + : QSGRenderContext(ctx) + , currentWindow(0) + , m_initialized(false) +{ +} +Context::Context(QObject *parent) + : QSGContext(parent) +{ + setDistanceFieldEnabled(false); +} + +QSGRectangleNode *Context::createRectangleNode() +{ + return new RectangleNode(); +} + +QSGImageNode *Context::createImageNode() +{ + return new ImageNode(); +} + +QSGPainterNode *Context::createPainterNode(QQuickPaintedItem *item) +{ + return new PainterNode(item); +} + +QSGGlyphNode *Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) +{ + Q_UNUSED(rc); + Q_UNUSED(preferNativeGlyphNode); + return new GlyphNode(); +} + +QSGNinePatchNode *Context::createNinePatchNode() +{ + return new NinePatchNode(); +} + +QSGLayer *Context::createLayer(QSGRenderContext *renderContext) +{ + return new SoftwareLayer(renderContext); +} + +QSurfaceFormat Context::defaultSurfaceFormat() const +{ + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + format.setRenderableType(QSurfaceFormat::DefaultRenderableType); + format.setMajorVersion(0); + format.setMinorVersion(0); + return format; +} + +void RenderContext::initialize(QOpenGLContext *context) +{ + Q_UNUSED(context) + Q_UNREACHABLE(); +} + +void RenderContext::initializeIfNeeded() +{ + if (m_initialized) + return; + m_initialized = true; + emit initialized(); +} + +void RenderContext::invalidate() +{ + QSGRenderContext::invalidate(); +} + +QSGTexture *RenderContext::createTexture(const QImage &image, uint flags) const +{ + Q_UNUSED(flags) + return new PixmapTexture(image); +} + +QSGRenderer *RenderContext::createRenderer() +{ + return new Renderer(this); +} + + +void RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo) +{ + QSGRenderContext::renderNextFrame(renderer, fbo); +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/context.h b/src/plugins/scenegraph/softwarecontext/context.h new file mode 100644 index 0000000000..f07e8f532a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/context.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTEXT_H +#define CONTEXT_H + +#include <private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.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 + +namespace SoftwareContext +{ + +class RenderContext : public QSGRenderContext +{ +public: + RenderContext(QSGContext *ctx); + void initialize(QOpenGLContext *context) override; + void initializeIfNeeded(); + void invalidate() override; + void renderNextFrame(QSGRenderer *renderer, GLuint fbo) override; + QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override; + QSGRenderer *createRenderer() override; + + QWindow *currentWindow; + bool m_initialized; +}; + +class Context : public QSGContext +{ + Q_OBJECT +public: + explicit Context(QObject *parent = nullptr); + + QSGRenderContext *createRenderContext() override { return new RenderContext(this); } + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; + QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override; + QSGNinePatchNode *createNinePatchNode() override; + QSGLayer *createLayer(QSGRenderContext *renderContext) override; + QSurfaceFormat defaultSurfaceFormat() const override; +}; + +} // namespace + +QT_END_NAMESPACE + +#endif // CONTEXT_H diff --git a/src/plugins/scenegraph/softwarecontext/glyphnode.cpp b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp new file mode 100644 index 0000000000..08d9d93fee --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glyphnode.h" + +QT_BEGIN_NAMESPACE + +GlyphNode::GlyphNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) + , m_style(QQuickText::Normal) +{ + setMaterial((QSGMaterial*)1); + setGeometry(&m_geometry); +} + + +void GlyphNode::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 GlyphNode::setColor(const QColor &color) +{ + m_color = color; +} + +void GlyphNode::setStyle(QQuickText::TextStyle style) +{ + m_style = style; +} + +void GlyphNode::setStyleColor(const QColor &color) +{ + m_styleColor = color; +} + +QPointF GlyphNode::baseLine() const +{ + return QPointF(); +} + +void GlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode) +{ +} + +void GlyphNode::update() +{ +} + +void GlyphNode::paint(QPainter *painter) +{ + painter->setBrush(QBrush()); + QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent()); + + switch (m_style) { + case QQuickText::Normal: break; + case QQuickText::Outline: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(1, 0), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(-1, 0), m_glyphRun); + break; + case QQuickText::Raised: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); + break; + case QQuickText::Sunken: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); + break; + } + + painter->setPen(m_color); + painter->drawGlyphRun(pos, m_glyphRun); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/glyphnode.h b/src/plugins/scenegraph/softwarecontext/glyphnode.h new file mode 100644 index 0000000000..12b0ec2543 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/glyphnode.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GLYPHNODE_H +#define GLYPHNODE_H + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class GlyphNode : public QSGGlyphNode +{ +public: + GlyphNode(); + + 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 // GLYPHNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.cpp b/src/plugins/scenegraph/softwarecontext/imagenode.cpp new file mode 100644 index 0000000000..48304ec9ba --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/imagenode.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "imagenode.h" + +#include "pixmaptexture.h" +#include "softwarelayer.h" +#include <QPainter> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +// Helper from widgets/styles/qdrawutil.cpp + +namespace SoftwareContext { + +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); +} + +} + +ImageNode::ImageNode() + : 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 ImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + markDirty(DirtyGeometry); +} + +void ImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + markDirty(DirtyGeometry); +} + +void ImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + markDirty(DirtyGeometry); +} + +void ImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + markDirty(DirtyGeometry); +} + +void ImageNode::setTexture(QSGTexture *texture) +{ + if (m_texture != texture) { + m_texture = texture; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void ImageNode::setMirror(bool mirror) +{ + if (m_mirror != mirror) { + m_mirror = mirror; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void ImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) +{ +} + +void ImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + bool smooth = (filtering == QSGTexture::Linear); + if (smooth == m_smooth) + return; + + m_smooth = smooth; + markDirty(DirtyMaterial); +} + +void ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileHorizontal = (wrapMode == QSGTexture::Repeat); + if (tileHorizontal == m_tileHorizontal) + return; + + m_tileHorizontal = tileHorizontal; + markDirty(DirtyMaterial); +} + +void ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileVertical = (wrapMode == QSGTexture::Repeat); + if (tileVertical == m_tileVertical) + return; + + m_tileVertical = (wrapMode == QSGTexture::Repeat); + markDirty(DirtyMaterial); +} + +void ImageNode::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 ImageNode::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 ImageNode::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()); + QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); + SoftwareContext::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), + margins, tilerules, 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 ImageNode::rect() const +{ + return m_targetRect; +} + +const QPixmap &ImageNode::pixmap() const +{ + if (PixmapTexture *pt = qobject_cast<PixmapTexture*>(m_texture)) { + return pt->pixmap(); + } else if (SoftwareLayer *layer = qobject_cast<SoftwareLayer*>(m_texture)) { + return layer->pixmap(); + } else { + qFatal("Image used with invalid texture format."); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.h b/src/plugins/scenegraph/softwarecontext/imagenode.h new file mode 100644 index 0000000000..a2c6ec61c8 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/imagenode.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IMAGENODE_H +#define IMAGENODE_H + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgtexturematerial_p.h> + +QT_BEGIN_NAMESPACE + +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 + +namespace SoftwareContext { + +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); + +} + +class ImageNode : public QSGImageNode +{ +public: + ImageNode(); + + 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 // IMAGENODE_H diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp new file mode 100644 index 0000000000..b358804968 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ninepatchnode.h" +#include "pixmaptexture.h" +#include "imagenode.h" + +QT_BEGIN_NAMESPACE + +NinePatchNode::NinePatchNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void NinePatchNode::setTexture(QSGTexture *texture) +{ + PixmapTexture *pt = qobject_cast<PixmapTexture*>(texture); + if (!pt) { + qWarning() << "Image used with invalid texture format."; + return; + } + m_pixmap = pt->pixmap(); + markDirty(DirtyMaterial); +} + +void NinePatchNode::setBounds(const QRectF &bounds) +{ + if (m_bounds == bounds) + return; + + m_bounds = bounds; + markDirty(DirtyGeometry); +} + +void NinePatchNode::setDevicePixelRatio(qreal ratio) +{ + if (m_pixelRatio == ratio) + return; + + m_pixelRatio = ratio; + markDirty(DirtyGeometry); +} + +void NinePatchNode::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 NinePatchNode::update() +{ +} + +void NinePatchNode::paint(QPainter *painter) +{ + if (m_margins.isNull()) + painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); + else + SoftwareContext::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), + m_margins, Qt::StretchTile, QDrawBorderPixmap::DrawingHints(0)); +} + +QRectF NinePatchNode::bounds() const +{ + return m_bounds; +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.h b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h new file mode 100644 index 0000000000..6f46e987c6 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NINEPATCHNODE_H +#define NINEPATCHNODE_H + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class NinePatchNode : public QSGNinePatchNode +{ +public: + NinePatchNode(); + + 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 // NINEPATCHNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/painternode.cpp b/src/plugins/scenegraph/softwarecontext/painternode.cpp new file mode 100644 index 0000000000..907e48bf12 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/painternode.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "painternode.h" +#include "pixmaptexture.h" +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +PainterNode::PainterNode(QQuickPaintedItem *item) + : QSGPainterNode() + , m_preferredRenderTarget(QQuickPaintedItem::Image) + , m_actualRenderTarget(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_extensionsChecked(false) + , m_multisamplingSupported(false) + , m_fastFBOResizing(false) + , m_fillColor(Qt::transparent) + , m_contentsScale(1.0) + , m_dirtyGeometry(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +PainterNode::~PainterNode() +{ + delete m_texture; +} + +void PainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) +{ + if (m_preferredRenderTarget == target) + return; + + m_preferredRenderTarget = target; +} + +void PainterNode::setSize(const QSize &size) +{ + if (size == m_size) + return; + + m_size = size; + + m_dirtyGeometry = true; +} + +void PainterNode::setDirty(const QRect &dirtyRect) +{ + m_dirtyContents = true; + m_dirtyRect = dirtyRect; + markDirty(DirtyMaterial); +} + +void PainterNode::setOpaquePainting(bool opaque) +{ + if (opaque == m_opaquePainting) + return; + + m_opaquePainting = opaque; +} + +void PainterNode::setLinearFiltering(bool linearFiltering) +{ + if (linearFiltering == m_linear_filtering) + return; + + m_linear_filtering = linearFiltering; +} + +void PainterNode::setMipmapping(bool mipmapping) +{ + if (mipmapping == m_mipmapping) + return; + + m_mipmapping = mipmapping; +} + +void PainterNode::setSmoothPainting(bool s) +{ + if (s == m_smoothPainting) + return; + + m_smoothPainting = s; +} + +void PainterNode::setFillColor(const QColor &c) +{ + if (c == m_fillColor) + return; + + m_fillColor = c; + markDirty(DirtyMaterial); +} + +void PainterNode::setContentsScale(qreal s) +{ + if (s == m_contentsScale) + return; + + m_contentsScale = s; + markDirty(DirtyMaterial); +} + +void PainterNode::setFastFBOResizing(bool dynamic) +{ + m_fastFBOResizing = dynamic; +} + +QImage PainterNode::toImage() const +{ + return m_pixmap.toImage(); +} + +void PainterNode::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 PixmapTexture(m_pixmap); + } + + if (m_dirtyContents) + paint(); + + m_dirtyGeometry = false; + m_dirtyContents = false; +} + +void PainterNode::paint(QPainter *painter) +{ + painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap); +} + +void PainterNode::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 PainterNode::setTextureSize(const QSize &size) +{ + if (size == m_textureSize) + return; + + m_textureSize = size; + m_dirtyGeometry = true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/painternode.h b/src/plugins/scenegraph/softwarecontext/painternode.h new file mode 100644 index 0000000000..3d77f7aa39 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/painternode.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PAINTERNODE_H +#define PAINTERNODE_H + +#include <private/qsgadaptationlayer_p.h> +#include <QtQuick/qquickpainteditem.h> + +#include <QtGui/QPixmap> + +QT_BEGIN_NAMESPACE + +class PainterNode : public QSGPainterNode +{ +public: + PainterNode(QQuickPaintedItem *item); + ~PainterNode(); + + 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::RenderTarget m_actualRenderTarget; + + 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_extensionsChecked; + bool m_multisamplingSupported; + bool m_fastFBOResizing; + QColor m_fillColor; + qreal m_contentsScale; + QSize m_textureSize; + + bool m_dirtyGeometry; +}; + +QT_END_NAMESPACE + +#endif // PAINTERNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp new file mode 100644 index 0000000000..4cc12f37ef --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pixmaprenderer.h" + +#include <QtQuick/QSGSimpleRectNode> + +#include <QElapsedTimer> + +Q_LOGGING_CATEGORY(lcPixmapRenderer, "qt.scenegraph.softwarecontext.pixmapRenderer") + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +PixmapRenderer::PixmapRenderer(QSGRenderContext *context) + : AbstractSoftwareRenderer(context) +{ + +} + +PixmapRenderer::~PixmapRenderer() +{ + +} + +void PixmapRenderer::renderScene(GLuint) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void PixmapRenderer::render() +{ + +} + +void PixmapRenderer::render(QPixmap *target) +{ + QElapsedTimer renderTimer; + + // Setup background item + setBackgroundSize(target->size()); + setBackgroundColor(clearColor()); + + QPainter painter(target); + painter.setRenderHint(QPainter::Antialiasing); + painter.setWindow(m_projectionRect); + + 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(); + + qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime; +} + +void PixmapRenderer::setProjectionRect(const QRect &projectionRect) +{ + m_projectionRect = projectionRect; +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h new file mode 100644 index 0000000000..47077ed0a0 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PIXMAPRENDERER_H +#define PIXMAPRENDERER_H + +#include "abstractsoftwarerenderer.h" + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +class PixmapRenderer : public AbstractSoftwareRenderer +{ +public: + PixmapRenderer(QSGRenderContext *context); + virtual ~PixmapRenderer(); + + void renderScene(GLuint fboId = 0) final; + void render() final; + + void render(QPixmap *target); + void setProjectionRect(const QRect &projectionRect); + +private: + QRect m_projectionRect; +}; + +} // namespace + +QT_END_NAMESPACE + +#endif // PIXMAPRENDERER_H diff --git a/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp b/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp new file mode 100644 index 0000000000..3a646e6c7f --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pixmaptexture.h" + +QT_BEGIN_NAMESPACE + +PixmapTexture::PixmapTexture(const QImage &image) + // Prevent pixmap format conversion to reduce memory consumption + // and surprises in calling code. (See QTBUG-47328) + : m_pixmap(QPixmap::fromImage(image, Qt::NoFormatConversion)) +{ +} + +PixmapTexture::PixmapTexture(const QPixmap &pixmap) + : m_pixmap(pixmap) +{ +} + + +int PixmapTexture::textureId() const +{ + return 0; +} + +QSize PixmapTexture::textureSize() const +{ + return m_pixmap.size(); +} + +bool PixmapTexture::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool PixmapTexture::hasMipmaps() const +{ + return false; +} + +void PixmapTexture::bind() +{ + Q_UNREACHABLE(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/pixmaptexture.h b/src/plugins/scenegraph/softwarecontext/pixmaptexture.h new file mode 100644 index 0000000000..c510c5d808 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaptexture.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PIXMAPTEXTURE_H +#define PIXMAPTEXTURE_H + +#include <private/qsgtexture_p.h> + +QT_BEGIN_NAMESPACE + +class PixmapTexture : public QSGTexture +{ + Q_OBJECT +public: + PixmapTexture(const QImage &image); + PixmapTexture(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 // PIXMAPTEXTURE_H diff --git a/src/plugins/scenegraph/softwarecontext/pluginmain.cpp b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp new file mode 100644 index 0000000000..53c1aca117 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pluginmain.h" +#include "context.h" +#include "renderloop.h" + +#include <private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> + +QT_BEGIN_NAMESPACE + +ContextPlugin::ContextPlugin(QObject *parent) + : QSGContextPlugin(parent) +{ +} + +QStringList ContextPlugin::keys() const +{ + return QStringList() << QLatin1String("softwarecontext"); +} + +QSGContext *ContextPlugin::create(const QString &) const +{ + if (!instance) + instance = new SoftwareContext::Context(); + return instance; +} + +QSGRenderLoop *ContextPlugin::createWindowManager() +{ + return new RenderLoop(); +} + +SoftwareContext::Context *ContextPlugin::instance = 0; + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/pluginmain.h b/src/plugins/scenegraph/softwarecontext/pluginmain.h new file mode 100644 index 0000000000..b7b4a1719c --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pluginmain.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGINMAIN_H +#define PLUGINMAIN_H + +#include <private/qsgcontext_p.h> +#include <private/qsgcontextplugin_p.h> + +#include <qplugin.h> + +#include "context.h" + +QT_BEGIN_NAMESPACE + +class ContextPlugin : public QSGContextPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSGContextFactoryInterface" FILE "softwarecontext.json") + +public: + ContextPlugin(QObject *parent = 0); + + QStringList keys() const override; + QSGContext *create(const QString &key) const override; + QSGRenderLoop *createWindowManager() override; + + static SoftwareContext::Context *instance; +}; + +QT_END_NAMESPACE + +#endif // PLUGINMAIN_H diff --git a/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp new file mode 100644 index 0000000000..d1fb617e3c --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp @@ -0,0 +1,442 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rectanglenode.h" +#include <qmath.h> + +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +RectangleNode::RectangleNode() + : 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 RectangleNode::setRect(const QRectF &rect) +{ + QRect alignedRect = rect.toAlignedRect(); + if (m_rect != alignedRect) { + m_rect = alignedRect; + markDirty(DirtyMaterial); + } +} + +void RectangleNode::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void RectangleNode::setPenColor(const QColor &color) +{ + if (m_penColor != color) { + m_penColor = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void RectangleNode::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 RectangleNode::setGradientStops(const QGradientStops &stops) +{ + //normalize stops + bool needsNormalization = false; + foreach (const QGradientStop &stop, 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 RectangleNode::setRadius(qreal radius) +{ + if (m_radius != radius) { + m_radius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void RectangleNode::setAligned(bool /*aligned*/) +{ +} + +void RectangleNode::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 RectangleNode::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 RectangleNode::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) { + foreach (QGradientStop stop, m_stops) { + if (stop.second.alpha() < 255) + return false; + } + } + + return true; +} + +QRectF RectangleNode::rect() const +{ + //TODO: double check that this is correct. + return m_rect; +} + +void RectangleNode::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)); + + } + + } + + int penWidth = qRound(m_penWidth); + QRectF brushRect = rect.marginsRemoved(QMargins(penWidth, penWidth, penWidth, 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 RectangleNode::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/plugins/scenegraph/softwarecontext/rectanglenode.h b/src/plugins/scenegraph/softwarecontext/rectanglenode.h new file mode 100644 index 0000000000..d864a1bb8f --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RECTANGLENODE_H +#define RECTANGLENODE_H + +#include <private/qsgadaptationlayer_p.h> + +#include <QPen> +#include <QBrush> +#include <QPixmap> + +QT_BEGIN_NAMESPACE + +class RectangleNode : public QSGRectangleNode +{ +public: + RectangleNode(); + + 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 // RECTANGLENODE_H diff --git a/src/plugins/scenegraph/softwarecontext/renderablenode.cpp b/src/plugins/scenegraph/softwarecontext/renderablenode.cpp new file mode 100644 index 0000000000..dab4a216bd --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderablenode.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderablenode.h" + +#include "imagenode.h" +#include "rectanglenode.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "painternode.h" +#include "pixmaptexture.h" + +#include <QtQuick/QSGSimpleRectNode> +#include <QtQuick/qsgsimpletexturenode.h> +#include <private/qsgtexture_p.h> +#include <private/qquickshadereffectnode_p.h> + +Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable") + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext{ + +RenderableNode::RenderableNode(NodeType type, QSGNode *node) + : m_nodeType(type) + , m_isOpaque(true) + , m_isDirty(true) + , m_opacity(1.0f) +{ + switch (m_nodeType) { + case RenderableNode::SimpleRect: + m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node); + break; + case RenderableNode::SimpleTexture: + m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node); + break; + case RenderableNode::Image: + m_handle.imageNode = static_cast<ImageNode*>(node); + break; + case RenderableNode::Painter: + m_handle.painterNode = static_cast<PainterNode*>(node); + break; + case RenderableNode::Rectangle: + m_handle.rectangleNode = static_cast<RectangleNode*>(node); + break; + case RenderableNode::Glyph: + m_handle.glpyhNode = static_cast<GlyphNode*>(node); + break; + case RenderableNode::NinePatch: + m_handle.ninePatchNode = static_cast<NinePatchNode*>(node); + break; + case RenderableNode::Invalid: + m_handle.simpleRectNode = nullptr; + break; + } +} + +RenderableNode::~RenderableNode() +{ + +} + +void RenderableNode::update() +{ + // Update the Node properties + m_isDirty = true; + + QRect boundingRect; + + switch (m_nodeType) { + case RenderableNode::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 RenderableNode::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 RenderableNode::Image: + // There isn't a way to tell, so assume it's not + m_isOpaque = false; + + boundingRect = m_handle.imageNode->rect().toRect(); + break; + case RenderableNode::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 RenderableNode::Rectangle: + if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.rectangleNode->rect().toRect(); + break; + case RenderableNode::Glyph: + // Always has alpha + m_isOpaque = false; + + boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect(); + break; + case RenderableNode::NinePatch: + // Difficult to tell, assume non-opaque + m_isOpaque = false; + + boundingRect = m_handle.ninePatchNode->bounds().toRect(); + break; + default: + break; + } + + m_boundingRect = m_transform.mapRect(boundingRect); + + if (m_clipRect.isValid()) { + m_boundingRect = m_boundingRect.intersected(m_clipRect.toRect()); + } + + // Overrides + if (m_opacity < 1.0f) + m_isOpaque = false; + + m_dirtyRegion = QRegion(m_boundingRect); +} + +QRegion RenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting) +{ + Q_ASSERT(painter); + + // Check for don't paint conditions + if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) { + m_isDirty = false; + m_dirtyRegion = QRegion(); + return QRegion(); + } + + 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); + + painter->setTransform(m_transform, false); //precalculated worldTransform + if (forceOpaquePainting || m_isOpaque) + painter->setCompositionMode(QPainter::CompositionMode_Source); + + switch (m_nodeType) { + case RenderableNode::SimpleRect: + painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color()); + break; + case RenderableNode::SimpleTexture: + { + QSGTexture *texture = m_handle.simpleTextureNode->texture(); + if (PixmapTexture *pt = dynamic_cast<PixmapTexture *>(texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, QRectF(0, 0, pm.width(), pm.height())); + } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) { + const QImage &im = pt->image(); + painter->drawImage(m_handle.simpleTextureNode->rect(), im, QRectF(0, 0, im.width(), im.height())); + } + } + break; + case RenderableNode::Image: + m_handle.imageNode->paint(painter); + break; + case RenderableNode::Painter: + m_handle.painterNode->paint(painter); + break; + case RenderableNode::Rectangle: + m_handle.rectangleNode->paint(painter); + break; + case RenderableNode::Glyph: + m_handle.glpyhNode->paint(painter); + break; + case RenderableNode::NinePatch: + m_handle.ninePatchNode->paint(painter); + break; + default: + break; + } + + painter->restore(); + + QRegion areaToBeFlushed = m_dirtyRegion; + m_previousDirtyRegion = QRegion(m_boundingRect); + m_isDirty = false; + m_dirtyRegion = QRegion(); + + return areaToBeFlushed; +} + +QRect RenderableNode::boundingRect() const +{ + // This returns the bounding area of a renderable node in world coordinates + return m_boundingRect; +} + +bool RenderableNode::isDirtyRegionEmpty() const +{ + return m_dirtyRegion.isEmpty(); +} + +void RenderableNode::setTransform(const QTransform &transform) +{ + if (m_transform == transform) + return; + m_transform = transform; + update(); +} + +void RenderableNode::setClipRect(const QRectF &clipRect) +{ + if (m_clipRect == clipRect) + return; + + m_clipRect = clipRect; + update(); +} + +void RenderableNode::setOpacity(float opacity) +{ + if (qFuzzyCompare(m_opacity, opacity)) + return; + + m_opacity = opacity; + update(); +} + +void RenderableNode::markGeometryDirty() +{ + update(); +} + +void RenderableNode::markMaterialDirty() +{ + update(); +} + +void RenderableNode::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 RenderableNode::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 RenderableNode::previousDirtyRegion() const +{ + return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect)); +} + +QRegion RenderableNode::dirtyRegion() const +{ + return m_dirtyRegion; +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/renderablenode.h b/src/plugins/scenegraph/softwarecontext/renderablenode.h new file mode 100644 index 0000000000..89ca942c07 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderablenode.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERABLENODE_H +#define RENDERABLENODE_H + +#include <QtGui/QRegion> +#include <QtCore/QRect> +#include <QtGui/QTransform> + +QT_BEGIN_NAMESPACE + +class QSGNode; +class QSGSimpleRectNode; +class QSGSimpleTextureNode; +class ImageNode; +class PainterNode; +class RectangleNode; +class GlyphNode; +class NinePatchNode; + +namespace SoftwareContext{ + +class RenderableNode +{ +public: + enum NodeType { + Invalid = -1, + SimpleRect, + SimpleTexture, + Image, + Painter, + Rectangle, + Glyph, + NinePatch + }; + + RenderableNode(NodeType type, QSGNode *node); + ~RenderableNode(); + + 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 setClipRect(const QRectF &clipRect); + void setOpacity(float opacity); + QTransform transform() const { return m_transform; } + QRectF clipRect() const { return m_clipRect; } + 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() const; + QRegion dirtyRegion() const; + +private: + union RenderableNodeHandle { + QSGSimpleRectNode *simpleRectNode; + QSGSimpleTextureNode *simpleTextureNode; + ImageNode *imageNode; + PainterNode *painterNode; + RectangleNode *rectangleNode; + GlyphNode *glpyhNode; + NinePatchNode *ninePatchNode; + }; + + const NodeType m_nodeType; + RenderableNodeHandle m_handle; + + bool m_isOpaque; + + bool m_isDirty; + QRegion m_dirtyRegion; + QRegion m_previousDirtyRegion; + + QTransform m_transform; + QRectF m_clipRect; + float m_opacity; + + QRect m_boundingRect; +}; + +} // namespace + +QT_END_NAMESPACE + +#endif // RENDERABLENODE_H diff --git a/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp new file mode 100644 index 0000000000..b9e8b531e2 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderablenodeupdater.h" + +#include "abstractsoftwarerenderer.h" +#include "imagenode.h" +#include "rectanglenode.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "painternode.h" +#include "pixmaptexture.h" + +#include <QtQuick/QSGSimpleRectNode> +#include <QtQuick/qsgsimpletexturenode.h> + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +RenderableNodeUpdater::RenderableNodeUpdater(AbstractSoftwareRenderer *renderer) + : m_renderer(renderer) +{ + m_opacityState.push(1.0f); + // Invalid RectF by default for no clip + m_clipState.push(QRectF(0.0f, 0.0f, -1.0f, -1.0f)); + m_transformState.push(QTransform()); +} + +RenderableNodeUpdater::~RenderableNodeUpdater() +{ + +} + +bool RenderableNodeUpdater::visit(QSGTransformNode *node) +{ + m_transformState.push(node->matrix().toTransform() * m_transformState.top()); + m_stateMap[node] = currentState(node); + return true; +} + +void RenderableNodeUpdater::endVisit(QSGTransformNode *) +{ + m_transformState.pop(); +} + +bool RenderableNodeUpdater::visit(QSGClipNode *node) +{ + // Make sure to translate the clip rect into world coordinates + if (!m_clipState.top().isValid()) + m_clipState.push(m_transformState.top().mapRect(node->clipRect())); + else + m_clipState.push(m_transformState.top().mapRect(node->clipRect()).intersected(m_clipState.top())); + m_stateMap[node] = currentState(node); + return true; +} + +void RenderableNodeUpdater::endVisit(QSGClipNode *) +{ + m_clipState.pop(); +} + +bool RenderableNodeUpdater::visit(QSGGeometryNode *node) +{ + if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) { + return updateRenderableNode(RenderableNode::SimpleRect, rectNode); + } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) { + return updateRenderableNode(RenderableNode::SimpleTexture, tn); + } else { + // We dont know, so skip + return false; + } +} + +void RenderableNodeUpdater::endVisit(QSGGeometryNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGOpacityNode *node) +{ + m_opacityState.push(m_opacityState.top() * node->opacity()); + m_stateMap[node] = currentState(node); + return true; +} + +void RenderableNodeUpdater::endVisit(QSGOpacityNode *) +{ + m_opacityState.pop(); +} + +bool RenderableNodeUpdater::visit(QSGImageNode *node) +{ + return updateRenderableNode(RenderableNode::Image, node); +} + +void RenderableNodeUpdater::endVisit(QSGImageNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGPainterNode *node) +{ + return updateRenderableNode(RenderableNode::Painter, node); +} + +void RenderableNodeUpdater::endVisit(QSGPainterNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGRectangleNode *node) +{ + return updateRenderableNode(RenderableNode::Rectangle, node); +} + +void RenderableNodeUpdater::endVisit(QSGRectangleNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGGlyphNode *node) +{ + return updateRenderableNode(RenderableNode::Glyph, node); +} + +void RenderableNodeUpdater::endVisit(QSGGlyphNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGNinePatchNode *node) +{ + return updateRenderableNode(RenderableNode::NinePatch, node); +} + +void RenderableNodeUpdater::endVisit(QSGNinePatchNode *) +{ +} + +bool RenderableNodeUpdater::visit(QSGRootNode *node) +{ + m_stateMap[node] = currentState(node); + return true; +} + +void RenderableNodeUpdater::endVisit(QSGRootNode *) +{ +} + +void RenderableNodeUpdater::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); + + } 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(QRectF(0.0f, 0.0f, -1.0f, -1.0f)); + } + + // 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; + } + default: + Q_UNREACHABLE(); + break; + } +} + +RenderableNodeUpdater::NodeState RenderableNodeUpdater::currentState(QSGNode *node) const +{ + NodeState state; + state.opacity = m_opacityState.top(); + state.clip = m_clipState.top(); + state.transform = m_transformState.top(); + state.parent = node->parent(); + return state; +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h new file mode 100644 index 0000000000..f878bbe94f --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERABLENODEUPDATER_H +#define RENDERABLENODEUPDATER_H + +#include "renderablenode.h" +#include "abstractsoftwarerenderer.h" + +#include <private/qsgadaptationlayer_p.h> + +#include <QTransform> +#include <QStack> +#include <QRectF> + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +class RenderableNodeUpdater : public QSGNodeVisitorEx +{ +public: + RenderableNodeUpdater(AbstractSoftwareRenderer *renderer); + virtual ~RenderableNodeUpdater(); + + 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(QSGImageNode *) override; + void endVisit(QSGImageNode *) override; + bool visit(QSGPainterNode *) override; + void endVisit(QSGPainterNode *) override; + bool visit(QSGRectangleNode *) override; + void endVisit(QSGRectangleNode *) override; + bool visit(QSGGlyphNode *) override; + void endVisit(QSGGlyphNode *) override; + bool visit(QSGNinePatchNode *) override; + void endVisit(QSGNinePatchNode *) override; + bool visit(QSGRootNode *) override; + void endVisit(QSGRootNode *) override; + + void updateNodes(QSGNode *node, bool isNodeRemoved = false); + +private: + struct NodeState { + float opacity; + QRectF clip; + QTransform transform; + QSGNode *parent; + }; + + NodeState currentState(QSGNode *node) const; + + template<class NODE> + bool updateRenderableNode(RenderableNode::NodeType type, NODE *node); + + AbstractSoftwareRenderer *m_renderer; + QStack<float> m_opacityState; + QStack<QRectF> m_clipState; + QStack<QTransform> m_transformState; + QHash<QSGNode*,NodeState> m_stateMap; +}; + +template<class NODE> +bool RenderableNodeUpdater::updateRenderableNode(RenderableNode::NodeType type, NODE *node) +{ + //Check if we already know about node + auto renderableNode = m_renderer->renderableNode(node); + if (renderableNode == nullptr) { + renderableNode = new RenderableNode(type, node); + m_renderer->addNodeMapping(node, renderableNode); + } + + //Update the node + renderableNode->setTransform(m_transformState.top()); + renderableNode->setOpacity(m_opacityState.top()); + renderableNode->setClipRect(m_clipState.top()); + + renderableNode->update(); + m_stateMap[node] = currentState(node); + + return true; +} + +} // namespace + +QT_END_NAMESPACE + +#endif // RENDERABLENODEUPDATER_H diff --git a/src/plugins/scenegraph/softwarecontext/renderer.cpp b/src/plugins/scenegraph/softwarecontext/renderer.cpp new file mode 100644 index 0000000000..6d2d390d43 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderer.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderer.h" + +#include "renderablenodeupdater.h" +#include "renderlistbuilder.h" +#include "context.h" +#include "renderablenode.h" + +#include <QtGui/QWindow> +#include <QtQuick/QSGSimpleRectNode> + +#include <QElapsedTimer> + +Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer") + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +Renderer::Renderer(QSGRenderContext *context) + : AbstractSoftwareRenderer(context) +{ +} + +Renderer::~Renderer() +{ +} + +void Renderer::renderScene(GLuint) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void Renderer::render() +{ + QElapsedTimer renderTimer; + + QWindow *currentWindow = static_cast<RenderContext*>(m_context)->currentWindow; + if (!m_backingStore) + m_backingStore.reset(new QBackingStore(currentWindow)); + + if (m_backingStore->size() != currentWindow->size()) { + m_backingStore->resize(currentWindow->size()); + } + + setBackgroundColor(clearColor()); + setBackgroundSize(currentWindow->size()); + + const QRect rect(0, 0, currentWindow->width(), currentWindow->height()); + m_backingStore->beginPaint(rect); + + QPaintDevice *device = m_backingStore->paintDevice(); +#ifndef QTQUICK2D_DEBUG_FLUSH + QPainter painter(device); +#else + if (m_outputBuffer.size() != m_backingStore->size()) { + m_outputBuffer = QImage(m_backingStore->size(), QImage::Format_ARGB32_Premultiplied); + m_outputBuffer.fill(Qt::transparent); + } + QPainter painter(&m_outputBuffer); +#endif + painter.setRenderHint(QPainter::Antialiasing); + + // 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. + optimizeRenderList(); + qint64 optimizeRenderListTime = renderTimer.restart(); + + // Render the contents Renderlist + QRegion dirtyRegion = renderNodes(&painter); + qint64 renderTime = renderTimer.elapsed(); + + qCDebug(lcRenderer) << "render" << dirtyRegion << buildRenderListTime << optimizeRenderListTime << renderTime; + +#ifdef QTQUICK2D_DEBUG_FLUSH + // Keep up with the last 5 flushes + if (m_previousFlushes.count() == 5) + m_previousFlushes.pop_front(); + m_previousFlushes.append(dirtyRegion); + + QPainter backingStorePainter(device); + backingStorePainter.drawImage(QRect(0, 0, m_backingStore->size().width(), m_backingStore->size().height()), m_outputBuffer, m_outputBuffer.rect()); + QPen pen(Qt::NoPen); + QBrush brush(QColor(255, 0, 0, 50)); + backingStorePainter.setPen(pen); + backingStorePainter.setBrush(brush); + for (auto region : qAsConst(m_previousFlushes)) { + backingStorePainter.drawRects(region.rects()); + } + m_backingStore->endPaint(); + + m_backingStore->flush(rect); +#else + m_backingStore->endPaint(); + // Flush the updated regions to the window + m_backingStore->flush(dirtyRegion); +#endif +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/renderer.h b/src/plugins/scenegraph/softwarecontext/renderer.h new file mode 100644 index 0000000000..d9a86b9bb0 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderer.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include "abstractsoftwarerenderer.h" + +#include <QtGui/QBackingStore> + +#ifdef QTQUICK2D_DEBUG_FLUSH +#include <QtGui/QImage> +#endif + +QT_BEGIN_NAMESPACE + +class QSGSimpleRectNode; + +namespace SoftwareContext{ + +class Renderer : public AbstractSoftwareRenderer +{ +public: + Renderer(QSGRenderContext *context); + virtual ~Renderer(); + + QBackingStore *backingStore() const { return m_backingStore.data(); } + +protected: + void renderScene(GLuint fboId = 0) final; + void render() final; + +private: + QScopedPointer<QBackingStore> m_backingStore; + +#ifdef QTQUICK2D_DEBUG_FLUSH + QVector<QRegion> m_previousFlushes; + QImage m_outputBuffer; +#endif +}; + +} // namespace + +QT_END_NAMESPACE + +#endif // RENDERER_H diff --git a/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp new file mode 100644 index 0000000000..890b76b246 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderlistbuilder.h" + +#include "renderablenode.h" +#include "abstractsoftwarerenderer.h" +#include "imagenode.h" +#include "rectanglenode.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "painternode.h" +#include "pixmaptexture.h" + +#include <QtQuick/QSGSimpleRectNode> +#include <QtQuick/qsgsimpletexturenode.h> + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + + +RenderListBuilder::RenderListBuilder(AbstractSoftwareRenderer *renderer) + : m_renderer(renderer) +{ + +} + +bool RenderListBuilder::visit(QSGTransformNode *) +{ + return true; +} + +void RenderListBuilder::endVisit(QSGTransformNode *) +{ +} + +bool RenderListBuilder::visit(QSGClipNode *) +{ + return true; +} + +void RenderListBuilder::endVisit(QSGClipNode *) +{ +} + +bool RenderListBuilder::visit(QSGGeometryNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGGeometryNode *) +{ +} + +bool RenderListBuilder::visit(QSGOpacityNode *) +{ + return true; +} + +void RenderListBuilder::endVisit(QSGOpacityNode *) +{ +} + +bool RenderListBuilder::visit(QSGImageNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGImageNode *) +{ +} + +bool RenderListBuilder::visit(QSGPainterNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGPainterNode *) +{ +} + +bool RenderListBuilder::visit(QSGRectangleNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGRectangleNode *) +{ +} + +bool RenderListBuilder::visit(QSGGlyphNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGGlyphNode *) +{ +} + +bool RenderListBuilder::visit(QSGNinePatchNode *node) +{ + return addRenderableNode(node); +} + +void RenderListBuilder::endVisit(QSGNinePatchNode *) +{ +} + +bool RenderListBuilder::visit(QSGRootNode *) +{ + return true; +} + +void RenderListBuilder::endVisit(QSGRootNode *) +{ +} + +bool RenderListBuilder::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; +} + +} // namespace + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/renderlistbuilder.h b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.h new file mode 100644 index 0000000000..333aac263e --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERLISTBUILDER_H +#define RENDERLISTBUILDER_H + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { + +class AbstractSoftwareRenderer; + +class RenderListBuilder : public QSGNodeVisitorEx +{ +public: + RenderListBuilder(AbstractSoftwareRenderer *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(QSGImageNode *) override; + void endVisit(QSGImageNode *) override; + bool visit(QSGPainterNode *) override; + void endVisit(QSGPainterNode *) override; + bool visit(QSGRectangleNode *) override; + void endVisit(QSGRectangleNode *) override; + bool visit(QSGGlyphNode *) override; + void endVisit(QSGGlyphNode *) override; + bool visit(QSGNinePatchNode *) override; + void endVisit(QSGNinePatchNode *) override; + bool visit(QSGRootNode *) override; + void endVisit(QSGRootNode *) override; + +private: + bool addRenderableNode(QSGNode *node); + + AbstractSoftwareRenderer *m_renderer; +}; + +} + +QT_END_NAMESPACE + +#endif // RENDERLISTBUILDER_H diff --git a/src/plugins/scenegraph/softwarecontext/renderloop.cpp b/src/plugins/scenegraph/softwarecontext/renderloop.cpp new file mode 100644 index 0000000000..0ad95148ec --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderloop.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "renderloop.h" + +#include "context.h" + +#include <QtCore/QCoreApplication> + +#include <private/qquickwindow_p.h> +#include <QElapsedTimer> +#include <private/qquickprofiler_p.h> + +QT_BEGIN_NAMESPACE + +RenderLoop::RenderLoop() +{ + sg = QSGContext::createDefaultContext(); + rc = sg->createRenderContext(); +} + +RenderLoop::~RenderLoop() +{ + delete rc; + delete sg; +} + +void RenderLoop::show(QQuickWindow *window) +{ + WindowData data; + data.updatePending = false; + data.grabOnly = false; + m_windows[window] = data; + + maybeUpdate(window); +} + +void RenderLoop::hide(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->fireAboutToStop(); +} + +void RenderLoop::windowDestroyed(QQuickWindow *window) +{ + m_windows.remove(window); + hide(window); + + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->cleanupNodesOnShutdown(); + + if (m_windows.size() == 0) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +void RenderLoop::renderWindow(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) + return; + + WindowData &data = const_cast<WindowData &>(m_windows[window]); + + // ### create QPainter and set up pointer to current window/painter + SoftwareContext::RenderContext *ctx = static_cast<SoftwareContext::RenderContext*>(cd->context); + ctx->currentWindow = window; + ctx->initializeIfNeeded(); + + bool alsoSwap = data.updatePending; + data.updatePending = false; + + if (!data.grabOnly) { + cd->flushDelayedTouchEvent(); + // 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); + + cd->renderSceneGraph(window->size()); + + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (data.grabOnly) { + // #### grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), false, false); + data.grabOnly = false; + } + + if (alsoSwap && window->isVisible()) { +// ### gl->swapBuffers(window); + 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 'basic' 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 RenderLoop::exposureChanged(QQuickWindow *window) +{ + if (window->isExposed()) { + m_windows[window].updatePending = true; + renderWindow(window); + } +} + +QImage RenderLoop::grab(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return QImage(); + + m_windows[window].grabOnly = true; + + renderWindow(window); + + QImage grabbed = grabContent; + grabContent = QImage(); + return grabbed; +} + + + +void RenderLoop::maybeUpdate(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows[window].updatePending = true; + window->requestUpdate(); +} + +QSurface::SurfaceType RenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + + + +QSGContext *RenderLoop::sceneGraphContext() const +{ + return sg; +} + + +void RenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + renderWindow(window); +} + +QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/softwarecontext/renderloop.h b/src/plugins/scenegraph/softwarecontext/renderloop.h new file mode 100644 index 0000000000..385ed63b3b --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderloop.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERLOOP_H +#define RENDERLOOP_H + +#include <private/qsgrenderloop_p.h> + +QT_BEGIN_NAMESPACE + +class RenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + RenderLoop(); + ~RenderLoop(); + + 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; + + QSGContext *sg; + QSGRenderContext *rc; + + QImage grabContent; +}; + +QT_END_NAMESPACE + +#endif // RENDERLOOP_H diff --git a/src/plugins/scenegraph/softwarecontext/softwarecontext.json b/src/plugins/scenegraph/softwarecontext/softwarecontext.json new file mode 100644 index 0000000000..ca87c6c38a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarecontext.json @@ -0,0 +1,3 @@ +{ + "Keys": ["softwarecontext"] +} diff --git a/src/plugins/scenegraph/softwarecontext/softwarecontext.pro b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro new file mode 100644 index 0000000000..603910b8aa --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro @@ -0,0 +1,52 @@ +TARGET=softwarecontext + +QT += gui-private core-private quick-private qml-private + +PLUGIN_TYPE = scenegraph +PLUGIN_CLASS_NAME = ContextPlugin +load(qt_plugin) + +QMAKE_TARGET_PRODUCT = "Qt Quick 2D Renderer (Qt $$QT_VERSION)" +QMAKE_TARGET_DESCRIPTION = "Quick 2D Renderer for Qt." + + +#DEFINES += QTQUICK2D_DEBUG_FLUSH + +SOURCES += \ + context.cpp \ + pluginmain.cpp \ + renderloop.cpp \ + rectanglenode.cpp \ + imagenode.cpp \ + pixmaptexture.cpp \ + glyphnode.cpp \ + ninepatchnode.cpp \ + softwarelayer.cpp \ + painternode.cpp \ + renderablenode.cpp \ + renderer.cpp \ + pixmaprenderer.cpp \ + renderablenodeupdater.cpp \ + renderlistbuilder.cpp \ + abstractsoftwarerenderer.cpp + +HEADERS += \ + context.h \ + pluginmain.h \ + renderloop.h \ + rectanglenode.h \ + imagenode.h \ + pixmaptexture.h \ + glyphnode.h \ + ninepatchnode.h \ + softwarelayer.h \ + painternode.h \ + renderablenode.h \ + renderer.h \ + pixmaprenderer.h \ + renderablenodeupdater.h \ + renderlistbuilder.h \ + abstractsoftwarerenderer.h + +OTHER_FILES += softwarecontext.json + diff --git a/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp new file mode 100644 index 0000000000..395361da86 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "softwarelayer.h" + +#include "context.h" +#include "pixmaprenderer.h" + +QT_BEGIN_NAMESPACE + +SoftwareLayer::SoftwareLayer(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) +{ + +} + +SoftwareLayer::~SoftwareLayer() +{ + invalidated(); +} + +int SoftwareLayer::textureId() const +{ + return 0; +} + +QSize SoftwareLayer::textureSize() const +{ + return m_pixmap.size(); +} + +bool SoftwareLayer::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool SoftwareLayer::hasMipmaps() const +{ + return false; +} + +void SoftwareLayer::bind() +{ +} + +bool SoftwareLayer::updateTexture() +{ + bool doGrab = (m_live || m_grab) && m_dirtyTexture; + if (doGrab) + grab(); + if (m_grab) + emit scheduledUpdateCompleted(); + m_grab = false; + return doGrab; +} + +void SoftwareLayer::setItem(QSGNode *item) +{ + if (item == m_item) + return; + m_item = item; + + if (m_live && !m_item) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + markDirtyTexture(); +} + +void SoftwareLayer::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + + if (m_live && m_size.isNull()) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + if (m_dirtyTexture) { + emit updateRequested(); + } +} + +QImage SoftwareLayer::toImage() const +{ + return m_pixmap.toImage(); +} + +void SoftwareLayer::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + + if (m_live && (!m_item || m_size.isNull())) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void SoftwareLayer::setFormat(GLenum) +{ +} + +void SoftwareLayer::setHasMipmaps(bool) +{ +} + +void SoftwareLayer::setDevicePixelRatio(qreal ratio) +{ + m_device_pixel_ratio = ratio; +} + +void SoftwareLayer::setMirrorHorizontal(bool mirror) +{ + if (m_mirrorHorizontal == mirror) + return; + m_mirrorHorizontal = mirror; + markDirtyTexture(); +} + +void SoftwareLayer::setMirrorVertical(bool mirror) +{ + if (m_mirrorVertical == mirror) + return; + m_mirrorVertical = mirror; + markDirtyTexture(); +} + +void SoftwareLayer::markDirtyTexture() +{ + m_dirtyTexture = true; + if (m_live || m_grab) { + emit updateRequested(); + } +} + +void SoftwareLayer::invalidated() +{ + delete m_renderer; + m_renderer = 0; +} + +void SoftwareLayer::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 SoftwareContext::PixmapRenderer(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/plugins/scenegraph/softwarecontext/softwarelayer.h b/src/plugins/scenegraph/softwarecontext/softwarelayer.h new file mode 100644 index 0000000000..95c05bf7e0 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarelayer.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SOFTWARELAYER_H +#define SOFTWARELAYER_H + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +namespace SoftwareContext { +class PixmapRenderer; +} + +class SoftwareLayer : public QSGLayer +{ + Q_OBJECT +public: + SoftwareLayer(QSGRenderContext *renderContext); + ~SoftwareLayer(); + + 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(GLenum) 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; + SoftwareContext::PixmapRenderer *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 // SOFTWARELAYER_H |