aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2016-12-03 21:30:12 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2016-12-19 17:03:46 +0000
commitc9023c28764e70cd1c6f9cfc3506e6185299548e (patch)
tree1578cf3ea1f97b024f036b923cb1e02fee0b92cc /src/quick/items
parent25277124d8b7581a9390702ad3d4bcd4be8dedd7 (diff)
Add QQuickPathItem and its backend infra
The generic backend uses the triangulator from QtGui, but is in fact OpenGL-only for now due to materials. The NVPR backend uses GL_NV_path_rendering on NVIDIA hardware with OpenGL 4.3+ or OpenGL ES 3.1+. The software backend simply uses QPainter. With the generic backend each PathItem is backed by a non-visual root node and 0, 1 or 2 child geometry nodes, depending on the presence of visible stroking and filling. The potentially expensive triangulation happens on updatePolish(), on the gui thread. This is proven to provide much smoother results when compared to doing the geometry generation on the render thread in updatePaintNode(), in particular on power-limited embedded devices. The NVPR backend uses a QSGRenderNode in DepthAware mode so that the batch renderer can continue to rely on the depth buffer and use opaque batches. Due to not relying on slow CPU-side triangulation, this backend uses 5-10 times less CPU, even when properties of the path or its elements are animated. The path itself is specified with the PathView's Path, PathLine, PathArc, PathQuad, etc. types. This allows for consistency with PathView and the 2D Canvas and avoids a naming mess in the API. However, there won't be a 100% symmetry: backends like NVPR will not rely on QPainterPath but process the path elements on their own (as QPainterPath is essentially useless with these APIs), which can lead to differences in the supported path elements. The supported common set is currently Move, Line, Quad, Cubic, Arc. The patch introduces PathMove, which is essentially PathLine but maps to moveTo instead of lineTo. More types may get added later (e.g. NVPR can do a wide variety of optimized rounded rects, but this requires directly specifying a GL_ROUNDED_RECTx_NV command, thus neededing a dedicated Path type on our side too) For filling with gradients only linear gradients are supported at the moment. In addition to the declarative API, a more lightweight, QObject-less JS-callable API should be considered as well for the future. Change-Id: I335ad64b425ee279505d60e3e57ac6841e1cbd24 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/items.pri15
-rw-r--r--src/quick/items/items.qrc4
-rw-r--r--src/quick/items/qquickitemsmodule.cpp9
-rw-r--r--src/quick/items/qquickpathitem.cpp792
-rw-r--r--src/quick/items/qquickpathitem_p.h281
-rw-r--r--src/quick/items/qquickpathitem_p_p.h177
-rw-r--r--src/quick/items/qquickpathitemgenericrenderer.cpp510
-rw-r--r--src/quick/items/qquickpathitemgenericrenderer_p.h232
-rw-r--r--src/quick/items/qquickpathitemnvprrenderer.cpp567
-rw-r--r--src/quick/items/qquickpathitemnvprrenderer_p.h194
-rw-r--r--src/quick/items/qquickpathitemsoftwarerenderer.cpp234
-rw-r--r--src/quick/items/qquickpathitemsoftwarerenderer_p.h127
-rw-r--r--src/quick/items/shaders/lineargradient.frag9
-rw-r--r--src/quick/items/shaders/lineargradient.vert15
-rw-r--r--src/quick/items/shaders/lineargradient_core.frag12
-rw-r--r--src/quick/items/shaders/lineargradient_core.vert17
16 files changed, 3193 insertions, 2 deletions
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 0f8061b5ef..511c6f18d8 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -148,9 +148,20 @@ qtConfig(quick-listview) {
qtConfig(quick-pathview) {
HEADERS += \
$$PWD/qquickpathview_p.h \
- $$PWD/qquickpathview_p_p.h
+ $$PWD/qquickpathview_p_p.h \
+ $$PWD/qquickpathitem_p.h \
+ $$PWD/qquickpathitem_p_p.h \
+ $$PWD/qquickpathitemgenericrenderer_p.h \
+ $$PWD/qquickpathitemsoftwarerenderer_p.h
SOURCES += \
- $$PWD/qquickpathview.cpp
+ $$PWD/qquickpathview.cpp \
+ $$PWD/qquickpathitem.cpp \
+ $$PWD/qquickpathitemgenericrenderer.cpp \
+ $$PWD/qquickpathitemsoftwarerenderer.cpp
+ qtConfig(opengl) {
+ HEADERS += $$PWD/qquickpathitemnvprrenderer_p.h
+ SOURCES += $$PWD/qquickpathitemnvprrenderer.cpp
+ }
}
qtConfig(quick-positioners) {
diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc
index 6aaf757c29..da9bf0c828 100644
--- a/src/quick/items/items.qrc
+++ b/src/quick/items/items.qrc
@@ -8,5 +8,9 @@
<file>shaders/shadereffect_core.vert</file>
<file>shaders/shadereffectfallback_core.frag</file>
<file>shaders/shadereffectfallback_core.vert</file>
+ <file>shaders/lineargradient.vert</file>
+ <file>shaders/lineargradient.frag</file>
+ <file>shaders/lineargradient_core.vert</file>
+ <file>shaders/lineargradient_core.frag</file>
</qresource>
</RCC>
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index dbe30fbc83..b0d7f7f8a3 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -70,6 +70,7 @@
#if QT_CONFIG(quick_path)
#include <private/qquickpath_p.h>
#include <private/qquickpathinterpolator_p.h>
+#include "qquickpathitem_p.h"
#endif
#if QT_CONFIG(quick_positioners)
#include "qquickpositioners_p.h"
@@ -370,6 +371,14 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
#endif
qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea");
+
+#if QT_CONFIG(quick_path)
+ qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
+ qmlRegisterType<QQuickPathItem>(uri, 2, 9, "PathItem");
+ qmlRegisterType<QQuickPathGradientStop>(uri, 2, 9, "PathGradientStop");
+ qmlRegisterUncreatableType<QQuickPathGradient>(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class"));
+ qmlRegisterType<QQuickPathLinearGradient>(uri, 2, 9, "PathLinearGradient");
+#endif
}
static void initResources()
diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp
new file mode 100644
index 0000000000..0d2c7a8bbe
--- /dev/null
+++ b/src/quick/items/qquickpathitem.cpp
@@ -0,0 +1,792 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpathitem_p.h"
+#include "qquickpathitem_p_p.h"
+#include "qquickpathitemgenericrenderer_p.h"
+#include "qquickpathitemnvprrenderer_p.h"
+#include "qquickpathitemsoftwarerenderer_p.h"
+#include <private/qsgtexture_p.h>
+#include <QtGui/private/qdrawhelper_p.h>
+#include <QOpenGLFunctions>
+
+QT_BEGIN_NAMESPACE
+
+QQuickPathItemPrivate::QQuickPathItemPrivate()
+ : rendererType(QQuickPathItem::UnknownRenderer),
+ renderer(nullptr),
+ path(nullptr),
+ dirty(DirtyAll),
+ strokeColor(Qt::white),
+ strokeWidth(1),
+ fillColor(Qt::white),
+ fillRule(QQuickPathItem::OddEvenFill),
+ joinStyle(QQuickPathItem::BevelJoin),
+ miterLimit(2),
+ capStyle(QQuickPathItem::SquareCap),
+ strokeStyle(QQuickPathItem::SolidLine),
+ dashOffset(0),
+ fillGradient(nullptr)
+{
+ dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
+}
+
+QQuickPathItemPrivate::~QQuickPathItemPrivate()
+{
+ delete renderer;
+}
+
+/*!
+ \qmltype PathItem
+ \instantiates QQuickPathItem
+ \inqmlmodule QtQuick
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits Item
+ \brief Renders a path
+
+ Renders a path either by generating geometry via QPainterPath and manual
+ triangulation or by using an extension like \c{GL_NV_path_rendering}.
+
+ This approach is different from rendering shapes via QQuickPaintedItem or
+ the 2D Canvas because the path never gets rasterized in software. Therefore
+ it is suitable for creating shapes spreading over larger areas of the
+ screen, avoiding the performance penalty for texture uploads or framebuffer
+ blits.
+
+ Nonetheless it is important to be aware of performance implications, in
+ particular when the application is running on the generic PathItem
+ implementation due to not having support for accelerated path rendering.
+ The geometry generation happens entirely on the CPU in this case, and this
+ is potentially expensive. Changing the set of path elements, changing the
+ properties of these elements, or changing certain properties of the
+ PathItem itself all lead to retriangulation on every change. Therefore,
+ applying animation to such properties can heavily affect performance on
+ less powerful systems. If animating properties other than stroke and fill
+ colors is a must, it is recommended to target systems providing
+ \c{GL_NV_path_rendering} where the cost of path property changes is much
+ smaller.
+
+ \note The types for specifying path elements are shared between PathView
+ and PathItem. However, not all PathItem implementations support all path
+ element types, while some may not make sense for PathView. PathItem's
+ currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
+ PathArc.
+
+ \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc
+*/
+
+QQuickPathItem::QQuickPathItem(QQuickItem *parent)
+ : QQuickItem(*(new QQuickPathItemPrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickPathItem::~QQuickPathItem()
+{
+}
+
+QQuickPathItem::RendererType QQuickPathItem::rendererType() const
+{
+ Q_D(const QQuickPathItem);
+ return d->rendererType;
+}
+
+/*!
+ \qmlproperty Path QtQuick::PathItem::path
+ This property holds the path to be rendered.
+ For more information see the \l Path documentation.
+*/
+QQuickPath *QQuickPathItem::path() const
+{
+ Q_D(const QQuickPathItem);
+ return d->path;
+}
+
+void QQuickPathItem::setPath(QQuickPath *path)
+{
+ Q_D(QQuickPathItem);
+ if (d->path == path)
+ return;
+
+ if (d->path)
+ qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()),
+ this, QQuickPathItem, SLOT(_q_pathChanged()));
+ d->path = path;
+ qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
+ this, QQuickPathItem, SLOT(_q_pathChanged()));
+
+ d->dirty |= QQuickPathItemPrivate::DirtyPath;
+ emit pathChanged();
+ polish();
+}
+
+void QQuickPathItemPrivate::_q_pathChanged()
+{
+ Q_Q(QQuickPathItem);
+ dirty |= DirtyPath;
+ q->polish();
+}
+
+QColor QQuickPathItem::strokeColor() const
+{
+ Q_D(const QQuickPathItem);
+ return d->strokeColor;
+}
+
+void QQuickPathItem::setStrokeColor(const QColor &color)
+{
+ Q_D(QQuickPathItem);
+ if (d->strokeColor != color) {
+ d->strokeColor = color;
+ d->dirty |= QQuickPathItemPrivate::DirtyStrokeColor;
+ emit strokeColorChanged();
+ polish();
+ }
+}
+
+qreal QQuickPathItem::strokeWidth() const
+{
+ Q_D(const QQuickPathItem);
+ return d->strokeWidth;
+}
+
+void QQuickPathItem::setStrokeWidth(qreal w)
+{
+ Q_D(QQuickPathItem);
+ if (d->strokeWidth != w) {
+ d->strokeWidth = w;
+ d->dirty |= QQuickPathItemPrivate::DirtyStrokeWidth;
+ emit strokeWidthChanged();
+ polish();
+ }
+}
+
+QColor QQuickPathItem::fillColor() const
+{
+ Q_D(const QQuickPathItem);
+ return d->fillColor;
+}
+
+void QQuickPathItem::setFillColor(const QColor &color)
+{
+ Q_D(QQuickPathItem);
+ if (d->fillColor != color) {
+ d->fillColor = color;
+ d->dirty |= QQuickPathItemPrivate::DirtyFillColor;
+ emit fillColorChanged();
+ polish();
+ }
+}
+
+QQuickPathItem::FillRule QQuickPathItem::fillRule() const
+{
+ Q_D(const QQuickPathItem);
+ return d->fillRule;
+}
+
+void QQuickPathItem::setFillRule(FillRule fillRule)
+{
+ Q_D(QQuickPathItem);
+ if (d->fillRule != fillRule) {
+ d->fillRule = fillRule;
+ d->dirty |= QQuickPathItemPrivate::DirtyFillRule;
+ emit fillRuleChanged();
+ polish();
+ }
+}
+
+QQuickPathItem::JoinStyle QQuickPathItem::joinStyle() const
+{
+ Q_D(const QQuickPathItem);
+ return d->joinStyle;
+}
+
+void QQuickPathItem::setJoinStyle(JoinStyle style)
+{
+ Q_D(QQuickPathItem);
+ if (d->joinStyle != style) {
+ d->joinStyle = style;
+ d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ emit joinStyleChanged();
+ polish();
+ }
+}
+
+int QQuickPathItem::miterLimit() const
+{
+ Q_D(const QQuickPathItem);
+ return d->miterLimit;
+}
+
+void QQuickPathItem::setMiterLimit(int limit)
+{
+ Q_D(QQuickPathItem);
+ if (d->miterLimit != limit) {
+ d->miterLimit = limit;
+ d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ emit miterLimitChanged();
+ polish();
+ }
+}
+
+QQuickPathItem::CapStyle QQuickPathItem::capStyle() const
+{
+ Q_D(const QQuickPathItem);
+ return d->capStyle;
+}
+
+void QQuickPathItem::setCapStyle(CapStyle style)
+{
+ Q_D(QQuickPathItem);
+ if (d->capStyle != style) {
+ d->capStyle = style;
+ d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ emit capStyleChanged();
+ polish();
+ }
+}
+
+QQuickPathItem::StrokeStyle QQuickPathItem::strokeStyle() const
+{
+ Q_D(const QQuickPathItem);
+ return d->strokeStyle;
+}
+
+void QQuickPathItem::setStrokeStyle(StrokeStyle style)
+{
+ Q_D(QQuickPathItem);
+ if (d->strokeStyle != style) {
+ d->strokeStyle = style;
+ d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ emit strokeStyleChanged();
+ polish();
+ }
+}
+
+qreal QQuickPathItem::dashOffset() const
+{
+ Q_D(const QQuickPathItem);
+ return d->dashOffset;
+}
+
+void QQuickPathItem::setDashOffset(qreal offset)
+{
+ Q_D(QQuickPathItem);
+ if (d->dashOffset != offset) {
+ d->dashOffset = offset;
+ d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ emit dashOffsetChanged();
+ polish();
+ }
+}
+
+QVector<qreal> QQuickPathItem::dashPattern() const
+{
+ Q_D(const QQuickPathItem);
+ return d->dashPattern;
+}
+
+void QQuickPathItem::setDashPattern(const QVector<qreal> &array)
+{
+ Q_D(QQuickPathItem);
+ if (d->dashPattern != array) {
+ d->dashPattern = array;
+ d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ emit dashPatternChanged();
+ polish();
+ }
+}
+
+QQuickPathGradient *QQuickPathItem::fillGradient() const
+{
+ Q_D(const QQuickPathItem);
+ return d->fillGradient;
+}
+
+void QQuickPathItem::setFillGradient(QQuickPathGradient *gradient)
+{
+ Q_D(QQuickPathItem);
+ if (d->fillGradient != gradient) {
+ if (d->fillGradient)
+ qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()),
+ this, QQuickPathItem, SLOT(_q_fillGradientChanged()));
+ d->fillGradient = gradient;
+ if (d->fillGradient)
+ qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()),
+ this, QQuickPathItem, SLOT(_q_fillGradientChanged()));
+ d->dirty |= QQuickPathItemPrivate::DirtyFillGradient;
+ polish();
+ }
+}
+
+void QQuickPathItemPrivate::_q_fillGradientChanged()
+{
+ Q_Q(QQuickPathItem);
+ dirty |= DirtyFillGradient;
+ q->polish();
+}
+
+void QQuickPathItem::resetFillGradient()
+{
+ setFillGradient(nullptr);
+}
+
+void QQuickPathItem::updatePolish()
+{
+ Q_D(QQuickPathItem);
+
+ if (!d->dirty)
+ return;
+
+ if (!d->renderer) {
+ d->createRenderer();
+ if (!d->renderer)
+ return;
+ emit rendererChanged();
+ }
+
+ // endSync() is where expensive calculations may happen, depending on the
+ // backend. Therefore do this only when the item is visible.
+ if (isVisible())
+ d->sync();
+
+ update();
+}
+
+void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data)
+{
+ // sync may have been deferred; do it now if the item became visible
+ if (change == ItemVisibleHasChanged && data.boolValue)
+ polish();
+
+ QQuickItem::itemChange(change, data);
+}
+
+QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ // Called on the render thread, with the gui thread blocked. We can now
+ // safely access gui thread data.
+
+ Q_D(QQuickPathItem);
+ if (d->renderer) {
+ if (!node)
+ node = d->createRenderNode();
+ d->renderer->updatePathRenderNode();
+ }
+ return node;
+}
+
+// the renderer object lives on the gui thread
+void QQuickPathItemPrivate::createRenderer()
+{
+ Q_Q(QQuickPathItem);
+ QSGRendererInterface *ri = q->window()->rendererInterface();
+ if (!ri)
+ return;
+
+ switch (ri->graphicsApi()) {
+#ifndef QT_NO_OPENGL
+ case QSGRendererInterface::OpenGL:
+ if (QQuickPathItemNvprRenderNode::isSupported()) {
+ rendererType = QQuickPathItem::NvprRenderer;
+ renderer = new QQuickPathItemNvprRenderer;
+ } else {
+ rendererType = QQuickPathItem::GeometryRenderer;
+ renderer = new QQuickPathItemGenericRenderer(q);
+ }
+ break;
+#endif
+ case QSGRendererInterface::Software:
+ rendererType = QQuickPathItem::SoftwareRenderer;
+ renderer = new QQuickPathItemSoftwareRenderer;
+ break;
+ default:
+ qWarning("No path backend for this graphics API yet");
+ break;
+ }
+}
+
+// the node lives on the render thread
+QSGNode *QQuickPathItemPrivate::createRenderNode()
+{
+ Q_Q(QQuickPathItem);
+ QSGNode *node = nullptr;
+ if (!q->window())
+ return node;
+ QSGRendererInterface *ri = q->window()->rendererInterface();
+ if (!ri)
+ return node;
+
+ const bool hasFill = fillColor != Qt::transparent;
+ const bool hasStroke = !qFuzzyIsNull(strokeWidth) && strokeColor != Qt::transparent;
+
+ switch (ri->graphicsApi()) {
+#ifndef QT_NO_OPENGL
+ case QSGRendererInterface::OpenGL:
+ if (QQuickPathItemNvprRenderNode::isSupported()) {
+ node = new QQuickPathItemNvprRenderNode(q);
+ static_cast<QQuickPathItemNvprRenderer *>(renderer)->setNode(
+ static_cast<QQuickPathItemNvprRenderNode *>(node));
+ } else {
+ node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke);
+ static_cast<QQuickPathItemGenericRenderer *>(renderer)->setRootNode(
+ static_cast<QQuickPathItemGenericRootRenderNode *>(node));
+ }
+ break;
+#endif
+ case QSGRendererInterface::Software:
+ node = new QQuickPathItemSoftwareRenderNode(q);
+ static_cast<QQuickPathItemSoftwareRenderer *>(renderer)->setNode(
+ static_cast<QQuickPathItemSoftwareRenderNode *>(node));
+ break;
+ default:
+ qWarning("No path backend for this graphics API yet");
+ break;
+ }
+
+ return node;
+}
+
+void QQuickPathItemPrivate::sync()
+{
+ renderer->beginSync();
+
+ if (dirty & QQuickPathItemPrivate::DirtyPath)
+ renderer->setPath(path);
+ if (dirty & DirtyStrokeColor)
+ renderer->setStrokeColor(strokeColor);
+ if (dirty & DirtyStrokeWidth)
+ renderer->setStrokeWidth(strokeWidth);
+ if (dirty & DirtyFillColor)
+ renderer->setFillColor(fillColor);
+ if (dirty & DirtyFillRule)
+ renderer->setFillRule(fillRule);
+ if (dirty & DirtyStyle) {
+ renderer->setJoinStyle(joinStyle, miterLimit);
+ renderer->setCapStyle(capStyle);
+ }
+ if (dirty & DirtyDash)
+ renderer->setStrokeStyle(strokeStyle, dashOffset, dashPattern);
+ if (dirty & DirtyFillGradient)
+ renderer->setFillGradient(fillGradient);
+
+ renderer->endSync();
+ dirty = 0;
+}
+
+// ***** gradient support *****
+
+QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent)
+ : QObject(parent),
+ m_position(0),
+ m_color(Qt::black)
+{
+}
+
+qreal QQuickPathGradientStop::position() const
+{
+ return m_position;
+}
+
+void QQuickPathGradientStop::setPosition(qreal position)
+{
+ if (m_position != position) {
+ m_position = position;
+ if (QQuickPathGradient *grad = qobject_cast<QQuickPathGradient *>(parent()))
+ emit grad->updated();
+ }
+}
+
+QColor QQuickPathGradientStop::color() const
+{
+ return m_color;
+}
+
+void QQuickPathGradientStop::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ if (QQuickPathGradient *grad = qobject_cast<QQuickPathGradient *>(parent()))
+ emit grad->updated();
+ }
+}
+
+QQuickPathGradient::QQuickPathGradient(QObject *parent)
+ : QObject(parent),
+ m_spread(PadSpread)
+{
+}
+
+void QQuickPathGradient::appendStop(QQmlListProperty<QObject> *list, QObject *stop)
+{
+ QQuickPathGradientStop *sstop = qobject_cast<QQuickPathGradientStop *>(stop);
+ if (!sstop) {
+ qWarning("Gradient stop list only supports QQuickPathGradientStop elements");
+ return;
+ }
+ QQuickPathGradient *grad = qobject_cast<QQuickPathGradient *>(list->object);
+ Q_ASSERT(grad);
+ sstop->setParent(grad);
+ grad->m_stops.append(sstop);
+}
+
+QQmlListProperty<QObject> QQuickPathGradient::stops()
+{
+ return QQmlListProperty<QObject>(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr);
+}
+
+QGradientStops QQuickPathGradient::sortedGradientStops() const
+{
+ QGradientStops result;
+ for (int i = 0; i < m_stops.count(); ++i) {
+ QQuickPathGradientStop *s = static_cast<QQuickPathGradientStop *>(m_stops[i]);
+ int j = 0;
+ while (j < result.count() && result[j].first < s->position())
+ ++j;
+ result.insert(j, QGradientStop(s->position(), s->color()));
+ }
+ return result;
+}
+
+QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const
+{
+ return m_spread;
+}
+
+void QQuickPathGradient::setSpread(SpreadMode mode)
+{
+ if (m_spread != mode) {
+ m_spread = mode;
+ emit spreadChanged();
+ emit updated();
+ }
+}
+
+QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent)
+ : QQuickPathGradient(parent)
+{
+}
+
+qreal QQuickPathLinearGradient::x1() const
+{
+ return m_start.x();
+}
+
+void QQuickPathLinearGradient::setX1(qreal v)
+{
+ if (m_start.x() != v) {
+ m_start.setX(v);
+ emit x1Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickPathLinearGradient::y1() const
+{
+ return m_start.y();
+}
+
+void QQuickPathLinearGradient::setY1(qreal v)
+{
+ if (m_start.y() != v) {
+ m_start.setY(v);
+ emit y1Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickPathLinearGradient::x2() const
+{
+ return m_end.x();
+}
+
+void QQuickPathLinearGradient::setX2(qreal v)
+{
+ if (m_end.x() != v) {
+ m_end.setX(v);
+ emit x2Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickPathLinearGradient::y2() const
+{
+ return m_end.y();
+}
+
+void QQuickPathLinearGradient::setY2(qreal v)
+{
+ if (m_end.y() != v) {
+ m_end.setY(v);
+ emit y2Changed();
+ emit updated();
+ }
+}
+
+#ifndef QT_NO_OPENGL
+
+// contexts sharing with each other get the same cache instance
+class QQuickPathItemGradientCacheWrapper
+{
+public:
+ QQuickPathItemGradientCache *get(QOpenGLContext *context)
+ {
+ return m_resource.value<QQuickPathItemGradientCache>(context);
+ }
+
+private:
+ QOpenGLMultiGroupSharedResource m_resource;
+};
+
+QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache()
+{
+ static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches;
+ return qt_path_gradient_caches.get(QOpenGLContext::currentContext());
+}
+
+// let QOpenGLContext manage the lifetime of the cached textures
+QQuickPathItemGradientCache::~QQuickPathItemGradientCache()
+{
+ m_cache.clear();
+}
+
+void QQuickPathItemGradientCache::invalidateResource()
+{
+ m_cache.clear();
+}
+
+void QQuickPathItemGradientCache::freeResource(QOpenGLContext *)
+{
+ qDeleteAll(m_cache);
+ m_cache.clear();
+}
+
+static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient,
+ uint *colorTable, int size, float opacity)
+{
+ int pos = 0;
+ const QGradientStops &s = gradient.stops;
+ const bool colorInterpolation = true;
+
+ uint alpha = qRound(opacity * 256);
+ uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
+ qreal incr = 1.0 / qreal(size);
+ qreal fpos = 1.5 * incr;
+ colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color));
+
+ while (fpos <= s.first().first) {
+ colorTable[pos] = colorTable[pos - 1];
+ pos++;
+ fpos += incr;
+ }
+
+ if (colorInterpolation)
+ current_color = qPremultiply(current_color);
+
+ const int sLast = s.size() - 1;
+ for (int i = 0; i < sLast; ++i) {
+ qreal delta = 1/(s[i+1].first - s[i].first);
+ uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
+ if (colorInterpolation)
+ next_color = qPremultiply(next_color);
+
+ while (fpos < s[i+1].first && pos < size) {
+ int dist = int(256 * ((fpos - s[i].first) * delta));
+ int idist = 256 - dist;
+ if (colorInterpolation)
+ colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+ else
+ colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
+ ++pos;
+ fpos += incr;
+ }
+ current_color = next_color;
+ }
+
+ Q_ASSERT(s.size() > 0);
+
+ uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
+ for ( ; pos < size; ++pos)
+ colorTable[pos] = last_color;
+
+ colorTable[size-1] = last_color;
+}
+
+QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad)
+{
+ QSGPlainTexture *tx = m_cache[grad];
+ if (!tx) {
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ GLuint id;
+ f->glGenTextures(1, &id);
+ f->glBindTexture(GL_TEXTURE_2D, id);
+ static const uint W = 1024; // texture size is 1024x1
+ uint buf[W];
+ generateGradientColorTable(grad, buf, W, 1.0f);
+ f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
+ tx = new QSGPlainTexture;
+ tx->setTextureId(id);
+ switch (grad.spread) {
+ case QQuickPathGradient::PadSpread:
+ tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ break;
+ case QQuickPathGradient::RepeatSpread:
+ tx->setHorizontalWrapMode(QSGTexture::Repeat);
+ tx->setVerticalWrapMode(QSGTexture::Repeat);
+ break;
+ case QQuickPathGradient::ReflectSpread:
+ tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
+ tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
+ break;
+ default:
+ qWarning("Unknown gradient spread mode %d", grad.spread);
+ break;
+ }
+ m_cache[grad] = tx;
+ }
+ return tx;
+}
+
+#endif // QT_NO_OPENGL
+
+QT_END_NAMESPACE
+
+#include "moc_qquickpathitem_p.cpp"
diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h
new file mode 100644
index 0000000000..39b407cf87
--- /dev/null
+++ b/src/quick/items/qquickpathitem_p.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPATHITEM_P_H
+#define QQUICKPATHITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+
+#include <private/qtquickglobal_p.h>
+#include <private/qquickpath_p.h>
+#include <QGradientStops>
+
+QT_REQUIRE_CONFIG(quick_path);
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPathItemPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal position READ position WRITE setPosition)
+ Q_PROPERTY(QColor color READ color WRITE setColor)
+
+public:
+ QQuickPathGradientStop(QObject *parent = nullptr);
+
+ qreal position() const;
+ void setPosition(qreal position);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+private:
+ qreal m_position;
+ QColor m_color;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPathGradient : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> stops READ stops)
+ Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged)
+ Q_CLASSINFO("DefaultProperty", "stops")
+
+public:
+ enum SpreadMode {
+ PadSpread,
+ RepeatSpread,
+ ReflectSpread
+ };
+ Q_ENUM(SpreadMode)
+
+ QQuickPathGradient(QObject *parent = nullptr);
+
+ QQmlListProperty<QObject> stops();
+
+ QGradientStops sortedGradientStops() const;
+
+ SpreadMode spread() const;
+ void setSpread(SpreadMode mode);
+
+signals:
+ void updated();
+ void spreadChanged();
+
+private:
+ static void appendStop(QQmlListProperty<QObject> *list, QObject *stop);
+
+ QVector<QObject *> m_stops;
+ SpreadMode m_spread;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPathLinearGradient : public QQuickPathGradient
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed)
+ Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed)
+ Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed)
+ Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed)
+ Q_CLASSINFO("DefaultProperty", "stops")
+
+public:
+ QQuickPathLinearGradient(QObject *parent = nullptr);
+
+ qreal x1() const;
+ void setX1(qreal v);
+ qreal y1() const;
+ void setY1(qreal v);
+ qreal x2() const;
+ void setX2(qreal v);
+ qreal y2() const;
+ void setY2(qreal v);
+
+signals:
+ void x1Changed();
+ void y1Changed();
+ void x2Changed();
+ void y2Changed();
+
+private:
+ QPointF m_start;
+ QPointF m_end;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged)
+
+ Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged)
+
+ Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged)
+ Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged)
+ Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged)
+ Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged)
+ Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged)
+ Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged)
+ Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged)
+ Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged)
+ Q_PROPERTY(QVector<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged)
+ Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient)
+
+public:
+ enum FillRule {
+ OddEvenFill = Qt::OddEvenFill,
+ WindingFill = Qt::WindingFill
+ };
+ Q_ENUM(FillRule)
+
+ enum JoinStyle {
+ MiterJoin = Qt::MiterJoin,
+ BevelJoin = Qt::BevelJoin,
+ RoundJoin = Qt::RoundJoin
+ };
+ Q_ENUM(JoinStyle)
+
+ enum CapStyle {
+ FlatCap = Qt::FlatCap,
+ SquareCap = Qt::SquareCap,
+ RoundCap = Qt::RoundCap
+ };
+ Q_ENUM(CapStyle)
+
+ enum StrokeStyle {
+ SolidLine = Qt::SolidLine,
+ DashLine = Qt::DashLine
+ };
+ Q_ENUM(StrokeStyle)
+
+ enum RendererType {
+ UnknownRenderer,
+ GeometryRenderer,
+ NvprRenderer,
+ SoftwareRenderer
+ };
+ Q_ENUM(RendererType)
+
+ QQuickPathItem(QQuickItem *parent = nullptr);
+ ~QQuickPathItem();
+
+ RendererType rendererType() const;
+
+ QQuickPath *path() const;
+ void setPath(QQuickPath *path);
+
+ QColor strokeColor() const;
+ void setStrokeColor(const QColor &color);
+
+ qreal strokeWidth() const;
+ void setStrokeWidth(qreal w);
+
+ QColor fillColor() const;
+ void setFillColor(const QColor &color);
+
+ FillRule fillRule() const;
+ void setFillRule(FillRule fillRule);
+
+ JoinStyle joinStyle() const;
+ void setJoinStyle(JoinStyle style);
+
+ int miterLimit() const;
+ void setMiterLimit(int limit);
+
+ CapStyle capStyle() const;
+ void setCapStyle(CapStyle style);
+
+ StrokeStyle strokeStyle() const;
+ void setStrokeStyle(StrokeStyle style);
+
+ qreal dashOffset() const;
+ void setDashOffset(qreal offset);
+
+ QVector<qreal> dashPattern() const;
+ void setDashPattern(const QVector<qreal> &array);
+
+ QQuickPathGradient *fillGradient() const;
+ void setFillGradient(QQuickPathGradient *gradient);
+ void resetFillGradient();
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
+ void updatePolish() override;
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
+
+Q_SIGNALS:
+ void rendererChanged();
+ void pathChanged();
+ void strokeColorChanged();
+ void strokeWidthChanged();
+ void fillColorChanged();
+ void fillRuleChanged();
+ void joinStyleChanged();
+ void miterLimitChanged();
+ void capStyleChanged();
+ void strokeStyleChanged();
+ void dashOffsetChanged();
+ void dashPatternChanged();
+ void fillGradientChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickPathItem)
+ Q_DECLARE_PRIVATE(QQuickPathItem)
+ Q_PRIVATE_SLOT(d_func(), void _q_pathChanged())
+ Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPathItem)
+
+#endif // QQUICKPATHITEM_P_H
diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h
new file mode 100644
index 0000000000..366628d867
--- /dev/null
+++ b/src/quick/items/qquickpathitem_p_p.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPATHITEM_P_P_H
+#define QQUICKPATHITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpathitem_p.h"
+#include "qquickitem_p.h"
+#include <QPainterPath>
+#include <QColor>
+#include <QBrush>
+#include <private/qopenglcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGPlainTexture;
+
+class QQuickAbstractPathRenderer
+{
+public:
+ virtual ~QQuickAbstractPathRenderer() { }
+
+ // Gui thread
+ virtual void beginSync() = 0;
+ virtual void setPath(const QQuickPath *path) = 0;
+ virtual void setStrokeColor(const QColor &color) = 0;
+ virtual void setStrokeWidth(qreal w) = 0;
+ virtual void setFillColor(const QColor &color) = 0;
+ virtual void setFillRule(QQuickPathItem::FillRule fillRule) = 0;
+ virtual void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) = 0;
+ virtual void setCapStyle(QQuickPathItem::CapStyle capStyle) = 0;
+ virtual void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) = 0;
+ virtual void setFillGradient(QQuickPathGradient *gradient) = 0;
+ virtual void endSync() = 0;
+
+ // Render thread, with gui blocked
+ virtual void updatePathRenderNode() = 0;
+};
+
+class QQuickPathItemPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPathItem)
+
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyStrokeColor = 0x02,
+ DirtyStrokeWidth = 0x04,
+ DirtyFillColor = 0x08,
+ DirtyFillRule = 0x10,
+ DirtyStyle = 0x20,
+ DirtyDash = 0x40,
+ DirtyFillGradient = 0x80,
+
+ DirtyAll = 0xFF
+ };
+
+ QQuickPathItemPrivate();
+ ~QQuickPathItemPrivate();
+
+ void createRenderer();
+ QSGNode *createRenderNode();
+ void sync();
+
+ void _q_pathChanged();
+ void _q_fillGradientChanged();
+
+ QQuickPathItem::RendererType rendererType;
+ QQuickAbstractPathRenderer *renderer;
+ QQuickPath *path;
+ int dirty;
+ QColor strokeColor;
+ qreal strokeWidth;
+ QColor fillColor;
+ QQuickPathItem::FillRule fillRule;
+ QQuickPathItem::JoinStyle joinStyle;
+ int miterLimit;
+ QQuickPathItem::CapStyle capStyle;
+ QQuickPathItem::StrokeStyle strokeStyle;
+ qreal dashOffset;
+ QVector<qreal> dashPattern;
+ QQuickPathGradient *fillGradient;
+};
+
+#ifndef QT_NO_OPENGL
+
+class QQuickPathItemGradientCache : public QOpenGLSharedResource
+{
+public:
+ struct GradientDesc {
+ QGradientStops stops;
+ QPointF start;
+ QPointF end;
+ QQuickPathGradient::SpreadMode spread;
+ bool operator==(const GradientDesc &other) const
+ {
+ return start == other.start && end == other.end && spread == other.spread
+ && stops == other.stops;
+ }
+ };
+
+ QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { }
+ ~QQuickPathItemGradientCache();
+
+ void invalidateResource() override;
+ void freeResource(QOpenGLContext *) override;
+
+ QSGTexture *get(const GradientDesc &grad);
+
+ static QQuickPathItemGradientCache *currentCache();
+
+private:
+ QHash<GradientDesc, QSGPlainTexture *> m_cache;
+};
+
+inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0)
+{
+ uint h = seed;
+ h += v.start.x() + v.end.y() + v.spread;
+ for (int i = 0; i < 3 && i < v.stops.count(); ++i)
+ h += v.stops[i].second.rgba();
+ return h;
+}
+
+#endif // QT_NO_OPENGL
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp
new file mode 100644
index 0000000000..9bd03c0e51
--- /dev/null
+++ b/src/quick/items/qquickpathitemgenericrenderer.cpp
@@ -0,0 +1,510 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpathitemgenericrenderer_p.h"
+#include <QtGui/private/qtriangulator_p.h>
+#include <QSGVertexColorMaterial>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal SCALE = 100;
+
+struct ColoredVertex // must match QSGGeometry::ColoredPoint2D
+{
+ float x, y;
+ QQuickPathItemGenericRenderer::Color4ub color;
+ void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor)
+ {
+ x = nx; y = ny; color = ncolor;
+ }
+};
+
+static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c)
+{
+ QQuickPathItemGenericRenderer::Color4ub color = {
+ uchar(qRound(c.redF() * c.alphaF() * 255)),
+ uchar(qRound(c.greenF() * c.alphaF() * 255)),
+ uchar(qRound(c.blueF() * c.alphaF() * 255)),
+ uchar(qRound(c.alphaF() * 255))
+ };
+ return color;
+}
+
+QQuickPathItemGenericRootRenderNode::QQuickPathItemGenericRootRenderNode(QQuickWindow *window,
+ bool hasFill,
+ bool hasStroke)
+ : m_fillNode(nullptr),
+ m_strokeNode(nullptr)
+{
+ if (hasFill) {
+ m_fillNode = new QQuickPathItemGenericRenderNode(window, this);
+ appendChildNode(m_fillNode);
+ }
+ if (hasStroke) {
+ m_strokeNode = new QQuickPathItemGenericRenderNode(window, this);
+ appendChildNode(m_strokeNode);
+ }
+}
+
+QQuickPathItemGenericRootRenderNode::~QQuickPathItemGenericRootRenderNode()
+{
+}
+
+QQuickPathItemGenericRenderNode::QQuickPathItemGenericRenderNode(QQuickWindow *window,
+ QQuickPathItemGenericRootRenderNode *rootNode)
+ : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0),
+ m_window(window),
+ m_rootNode(rootNode),
+ m_material(nullptr)
+{
+ setGeometry(&m_geometry);
+ activateMaterial(MatSolidColor);
+}
+
+QQuickPathItemGenericRenderNode::~QQuickPathItemGenericRenderNode()
+{
+}
+
+void QQuickPathItemGenericRenderNode::activateMaterial(Material m)
+{
+ switch (m) {
+ case MatSolidColor:
+ // Use vertexcolor material. Items with different colors remain batchable
+ // this way, at the expense of having to provide per-vertex color values.
+ if (!m_solidColorMaterial)
+ m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window));
+ m_material = m_solidColorMaterial.data();
+ break;
+ case MatLinearGradient:
+ if (!m_linearGradientMaterial)
+ m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this));
+ m_material = m_linearGradientMaterial.data();
+ break;
+ default:
+ qWarning("Unknown material %d", m);
+ return;
+ }
+
+ if (material() != m_material)
+ setMaterial(m_material);
+}
+
+// sync, and so triangulation too, happens on the gui thread
+void QQuickPathItemGenericRenderer::beginSync()
+{
+ m_syncDirty = 0;
+}
+
+void QQuickPathItemGenericRenderer::setPath(const QQuickPath *path)
+{
+ m_path = path ? path->path() : QPainterPath();
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color)
+{
+ m_strokeColor = colorToColor4ub(color);
+ m_syncDirty |= DirtyColor;
+}
+
+void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w)
+{
+ m_pen.setWidthF(w);
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setFillColor(const QColor &color)
+{
+ m_fillColor = colorToColor4ub(color);
+ m_syncDirty |= DirtyColor;
+}
+
+void QQuickPathItemGenericRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+{
+ m_fillRule = Qt::FillRule(fillRule);
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+{
+ m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ m_pen.setMiterLimit(miterLimit);
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+{
+ m_pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ m_pen.setStyle(Qt::PenStyle(strokeStyle));
+ if (strokeStyle == QQuickPathItem::DashLine) {
+ m_pen.setDashPattern(dashPattern);
+ m_pen.setDashOffset(dashOffset);
+ }
+ m_syncDirty |= DirtyGeom;
+}
+
+void QQuickPathItemGenericRenderer::setFillGradient(QQuickPathGradient *gradient)
+{
+ m_fillGradientActive = gradient != nullptr;
+ if (gradient) {
+ m_fillGradient.stops = gradient->sortedGradientStops();
+ m_fillGradient.spread = gradient->spread();
+ if (QQuickPathLinearGradient *g = qobject_cast<QQuickPathLinearGradient *>(gradient)) {
+ m_fillGradient.start = QPointF(g->x1(), g->y1());
+ m_fillGradient.end = QPointF(g->x2(), g->y2());
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+ m_syncDirty |= DirtyFillGradient;
+}
+
+void QQuickPathItemGenericRenderer::endSync()
+{
+ if (!m_syncDirty)
+ return;
+
+ // Use a shadow dirty flag in order to avoid losing state in case there are
+ // multiple syncs with different dirty flags before we get to
+ // updatePathRenderNode() on the render thread (with the gui thread
+ // blocked). For our purposes here m_syncDirty is still required since
+ // geometry regeneration must only happen when there was an actual change
+ // in this particular sync round.
+ m_effectiveDirty |= m_syncDirty;
+
+ if (m_path.isEmpty()) {
+ m_fillVertices.clear();
+ m_fillIndices.clear();
+ m_strokeVertices.clear();
+ return;
+ }
+
+ triangulateFill();
+ triangulateStroke();
+}
+
+void QQuickPathItemGenericRenderer::triangulateFill()
+{
+ m_path.setFillRule(m_fillRule);
+
+ const QVectorPath &vp = qtVectorPathForPath(m_path);
+
+ QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE));
+ const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2
+ m_fillVertices.resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(m_fillVertices.data());
+ const qreal *vsrc = ts.vertices.constData();
+ for (int i = 0; i < vertexCount; ++i)
+ vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, m_fillColor);
+
+ m_fillIndices.resize(ts.indices.size());
+ quint16 *idst = m_fillIndices.data();
+ if (ts.indices.type() == QVertexIndexVector::UnsignedShort) {
+ memcpy(idst, ts.indices.data(), m_fillIndices.count() * sizeof(quint16));
+ } else {
+ const quint32 *isrc = (const quint32 *) ts.indices.data();
+ for (int i = 0; i < m_fillIndices.count(); ++i)
+ idst[i] = isrc[i];
+ }
+}
+
+void QQuickPathItemGenericRenderer::triangulateStroke()
+{
+ const QVectorPath &vp = qtVectorPathForPath(m_path);
+
+ const QRectF clip(0, 0, m_item->width(), m_item->height());
+ const qreal inverseScale = 1.0 / SCALE;
+ m_stroker.setInvScale(inverseScale);
+ if (m_pen.style() == Qt::SolidLine) {
+ m_stroker.process(vp, m_pen, clip, 0);
+ } else {
+ m_dashStroker.setInvScale(inverseScale);
+ m_dashStroker.process(vp, m_pen, clip, 0);
+ QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(),
+ m_dashStroker.elementTypes(), 0);
+ m_stroker.process(dashStroke, m_pen, clip, 0);
+ }
+
+ if (!m_stroker.vertexCount()) {
+ m_strokeVertices.clear();
+ return;
+ }
+
+ const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2
+ m_strokeVertices.resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(m_strokeVertices.data());
+ const float *vsrc = m_stroker.vertices();
+ for (int i = 0; i < vertexCount; ++i)
+ vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], m_strokeColor);
+}
+
+void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericRootRenderNode *rn)
+{
+ if (m_rootNode != rn) {
+ m_rootNode = rn;
+ // Scenegraph nodes can be destroyed and then replaced by new ones over
+ // time; hence it is important to mark everything dirty for
+ // updatePathRenderNode(). We can assume the renderer has a full sync
+ // of the data at this point.
+ m_effectiveDirty = DirtyAll;
+ }
+}
+
+// on the render thread with gui blocked
+void QQuickPathItemGenericRenderer::updatePathRenderNode()
+{
+ if (!m_effectiveDirty || !m_rootNode)
+ return;
+
+ if (m_fillColor.a == 0) {
+ delete m_rootNode->m_fillNode;
+ m_rootNode->m_fillNode = nullptr;
+ } else if (!m_rootNode->m_fillNode) {
+ m_rootNode->m_fillNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode);
+ if (m_rootNode->m_strokeNode)
+ m_rootNode->removeChildNode(m_rootNode->m_strokeNode);
+ m_rootNode->appendChildNode(m_rootNode->m_fillNode);
+ if (m_rootNode->m_strokeNode)
+ m_rootNode->appendChildNode(m_rootNode->m_strokeNode);
+ m_effectiveDirty |= DirtyGeom;
+ }
+
+ if (qFuzzyIsNull(m_pen.widthF()) || m_strokeColor.a == 0) {
+ delete m_rootNode->m_strokeNode;
+ m_rootNode->m_strokeNode = nullptr;
+ } else if (!m_rootNode->m_strokeNode) {
+ m_rootNode->m_strokeNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode);
+ m_rootNode->appendChildNode(m_rootNode->m_strokeNode);
+ m_effectiveDirty |= DirtyGeom;
+ }
+
+ updateFillNode();
+ updateStrokeNode();
+
+ m_effectiveDirty = 0;
+}
+
+void QQuickPathItemGenericRenderer::updateFillNode()
+{
+ if (!m_rootNode->m_fillNode)
+ return;
+
+ QQuickPathItemGenericRenderNode *n = m_rootNode->m_fillNode;
+ QSGGeometry *g = &n->m_geometry;
+ if (m_fillVertices.isEmpty()) {
+ g->allocate(0, 0);
+ n->markDirty(QSGNode::DirtyGeometry);
+ return;
+ }
+
+ if (m_fillGradientActive) {
+ n->activateMaterial(QQuickPathItemGenericRenderNode::MatLinearGradient);
+ if (m_effectiveDirty & DirtyFillGradient) {
+ // Make a copy of the data that will be accessed by the material on
+ // the render thread.
+ n->m_fillGradient = m_fillGradient;
+ // Gradients are implemented via a texture-based material.
+ n->markDirty(QSGNode::DirtyMaterial);
+ // stop here if only the gradient changed; no need to touch the geometry
+ if (!(m_effectiveDirty & DirtyGeom))
+ return;
+ }
+ } else {
+ n->activateMaterial(QQuickPathItemGenericRenderNode::MatSolidColor);
+ // fast path for updating only color values when no change in vertex positions
+ if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) {
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
+ for (int i = 0; i < g->vertexCount(); ++i)
+ vdst[i].set(vdst[i].x, vdst[i].y, m_fillColor);
+ n->markDirty(QSGNode::DirtyGeometry);
+ return;
+ }
+ }
+
+ g->allocate(m_fillVertices.count(), m_fillIndices.count());
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(g->vertexData(), m_fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->indexData(), m_fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
+
+ n->markDirty(QSGNode::DirtyGeometry);
+}
+
+void QQuickPathItemGenericRenderer::updateStrokeNode()
+{
+ if (!m_rootNode->m_strokeNode)
+ return;
+ if (m_effectiveDirty == DirtyFillGradient) // not applicable
+ return;
+
+ QQuickPathItemGenericRenderNode *n = m_rootNode->m_strokeNode;
+ n->markDirty(QSGNode::DirtyGeometry);
+
+ QSGGeometry *g = &n->m_geometry;
+ if (m_strokeVertices.isEmpty()) {
+ g->allocate(0, 0);
+ return;
+ }
+
+ if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) {
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
+ for (int i = 0; i < g->vertexCount(); ++i)
+ vdst[i].set(vdst[i].x, vdst[i].y, m_strokeColor);
+ return;
+ }
+
+ g->allocate(m_strokeVertices.count(), 0);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ memcpy(g->vertexData(), m_strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+}
+
+QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window)
+{
+ QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
+
+#ifndef QT_NO_OPENGL
+ if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"...
+ return new QSGVertexColorMaterial;
+#endif
+
+ qWarning("Vertex-color material: Unsupported graphics API %d", api);
+ return nullptr;
+}
+
+QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window,
+ QQuickPathItemGenericRenderNode *node)
+{
+ QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
+
+#ifndef QT_NO_OPENGL
+ if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"...
+ return new QQuickPathItemLinearGradientMaterial(node);
+#endif
+
+ qWarning("Linear gradient material: Unsupported graphics API %d", api);
+ return nullptr;
+}
+
+#ifndef QT_NO_OPENGL
+
+QSGMaterialType QQuickPathItemLinearGradientShader::type;
+
+QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader()
+{
+ setShaderSourceFile(QOpenGLShader::Vertex,
+ QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment,
+ QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag"));
+}
+
+void QQuickPathItemLinearGradientShader::initialize()
+{
+ m_opacityLoc = program()->uniformLocation("opacity");
+ m_matrixLoc = program()->uniformLocation("matrix");
+ m_gradStartLoc = program()->uniformLocation("gradStart");
+ m_gradEndLoc = program()->uniformLocation("gradEnd");
+}
+
+void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *)
+{
+ QQuickPathItemLinearGradientMaterial *m = static_cast<QQuickPathItemLinearGradientMaterial *>(mat);
+
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_opacityLoc, state.opacity());
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
+
+ QQuickPathItemGenericRenderNode *node = m->node();
+ program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start));
+ program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end));
+
+ QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient);
+ tx->bind();
+}
+
+char const *const *QQuickPathItemLinearGradientShader::attributeNames() const
+{
+ static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr };
+ return attr;
+}
+
+int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QQuickPathItemLinearGradientMaterial *m = static_cast<const QQuickPathItemLinearGradientMaterial *>(other);
+
+ QQuickPathItemGenericRenderNode *a = node();
+ QQuickPathItemGenericRenderNode *b = m->node();
+ Q_ASSERT(a && b);
+ if (a == b)
+ return 0;
+
+ const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient;
+ const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient;
+ if (int d = ga->start.x() - gb->start.x())
+ return d;
+ if (int d = ga->start.y() - gb->start.y())
+ return d;
+ if (int d = ga->end.x() - gb->end.x())
+ return d;
+ if (int d = ga->end.y() - gb->end.y())
+ return d;
+
+ if (int d = ga->stops.count() - gb->stops.count())
+ return d;
+
+ for (int i = 0; i < ga->stops.count(); ++i) {
+ if (int d = ga->stops[i].first - gb->stops[i].first)
+ return d;
+ if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
+ return d;
+ }
+
+ return 0;
+}
+
+#endif // QT_NO_OPENGL
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h
new file mode 100644
index 0000000000..c186959c88
--- /dev/null
+++ b/src/quick/items/qquickpathitemgenericrenderer_p.h
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPATHITEMGENERICRENDERER_P_H
+#define QQUICKPATHITEMGENERICRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpathitem_p_p.h"
+#include <qsgnode.h>
+#include <qsggeometry.h>
+#include <qsgmaterial.h>
+#include <QtGui/private/qtriangulatingstroker_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPathItemGenericRootRenderNode;
+
+class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyGeom = 0x01,
+ DirtyColor = 0x02,
+ DirtyFillGradient = 0x04,
+
+ DirtyAll = 0xFF
+ };
+
+ QQuickPathItemGenericRenderer(QQuickItem *item)
+ : m_item(item),
+ m_rootNode(nullptr),
+ m_effectiveDirty(0)
+ { }
+
+ void beginSync() override;
+ void setPath(const QQuickPath *path) override;
+ void setStrokeColor(const QColor &color) override;
+ void setStrokeWidth(qreal w) override;
+ void setFillColor(const QColor &color) override;
+ void setFillRule(QQuickPathItem::FillRule fillRule) override;
+ void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(QQuickPathItem::CapStyle capStyle) override;
+ void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(QQuickPathGradient *gradient) override;
+ void endSync() override;
+ void updatePathRenderNode() override;
+
+ void setRootNode(QQuickPathItemGenericRootRenderNode *rn);
+
+ struct Color4ub { unsigned char r, g, b, a; };
+
+private:
+ void triangulateFill();
+ void triangulateStroke();
+ void updateFillNode();
+ void updateStrokeNode();
+
+ QQuickItem *m_item;
+ QQuickPathItemGenericRootRenderNode *m_rootNode;
+ QTriangulatingStroker m_stroker;
+ QDashedStrokeProcessor m_dashStroker;
+
+ QPen m_pen;
+ Color4ub m_strokeColor;
+ Color4ub m_fillColor;
+ Qt::FillRule m_fillRule;
+ QPainterPath m_path;
+ bool m_fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc m_fillGradient;
+
+ QVector<QSGGeometry::ColoredPoint2D> m_fillVertices;
+ QVector<quint16> m_fillIndices;
+ QVector<QSGGeometry::ColoredPoint2D> m_strokeVertices;
+
+ int m_syncDirty;
+ int m_effectiveDirty;
+};
+
+class QQuickPathItemGenericRenderNode : public QSGGeometryNode
+{
+public:
+ QQuickPathItemGenericRenderNode(QQuickWindow *window, QQuickPathItemGenericRootRenderNode *rootNode);
+ ~QQuickPathItemGenericRenderNode();
+
+ enum Material {
+ MatSolidColor,
+ MatLinearGradient
+ };
+
+ void activateMaterial(Material m);
+
+ QQuickWindow *window() const { return m_window; }
+ QQuickPathItemGenericRootRenderNode *rootNode() const { return m_rootNode; }
+
+ // shadow data for custom materials
+ QQuickPathItemGradientCache::GradientDesc m_fillGradient;
+
+private:
+ QSGGeometry m_geometry;
+ QQuickWindow *m_window;
+ QQuickPathItemGenericRootRenderNode *m_rootNode;
+ QSGMaterial *m_material;
+ QScopedPointer<QSGMaterial> m_solidColorMaterial;
+ QScopedPointer<QSGMaterial> m_linearGradientMaterial;
+
+ friend class QQuickPathItemGenericRenderer;
+};
+
+class QQuickPathItemGenericRootRenderNode : public QSGNode
+{
+public:
+ QQuickPathItemGenericRootRenderNode(QQuickWindow *window, bool hasFill, bool hasStroke);
+ ~QQuickPathItemGenericRootRenderNode();
+
+private:
+ QQuickPathItemGenericRenderNode *m_fillNode;
+ QQuickPathItemGenericRenderNode *m_strokeNode;
+
+ friend class QQuickPathItemGenericRenderer;
+};
+
+class QQuickPathItemGenericMaterialFactory
+{
+public:
+ static QSGMaterial *createVertexColor(QQuickWindow *window);
+ static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericRenderNode *node);
+};
+
+#ifndef QT_NO_OPENGL
+
+class QQuickPathItemLinearGradientShader : public QSGMaterialShader
+{
+public:
+ QQuickPathItemLinearGradientShader();
+
+ void initialize() override;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
+
+ static QSGMaterialType type;
+
+private:
+ int m_opacityLoc;
+ int m_matrixLoc;
+ int m_gradStartLoc;
+ int m_gradEndLoc;
+};
+
+class QQuickPathItemLinearGradientMaterial : public QSGMaterial
+{
+public:
+ QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericRenderNode *node)
+ : m_node(node)
+ {
+ // Passing RequiresFullMatrix is essential in order to prevent the
+ // batch renderer from baking in simple, translate-only transforms into
+ // the vertex data. The shader will rely on the fact that
+ // vertexCoord.xy is the PathItem-space coordinate and so no modifications
+ // are welcome.
+ setFlag(Blending | RequiresFullMatrix);
+ }
+
+ QSGMaterialType *type() const override
+ {
+ return &QQuickPathItemLinearGradientShader::type;
+ }
+
+ int compare(const QSGMaterial *other) const override;
+
+ QSGMaterialShader *createShader() const override
+ {
+ return new QQuickPathItemLinearGradientShader;
+ }
+
+ QQuickPathItemGenericRenderNode *node() const { return m_node; }
+
+private:
+ QQuickPathItemGenericRenderNode *m_node;
+};
+
+#endif // QT_NO_OPENGL
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPATHITEMGENERICRENDERER_P_H
diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp
new file mode 100644
index 0000000000..d07a63c86d
--- /dev/null
+++ b/src/quick/items/qquickpathitemnvprrenderer.cpp
@@ -0,0 +1,567 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpathitemnvprrenderer_p.h"
+#include <QOpenGLExtraFunctions>
+#include <private/qquickpath_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickPathItemNvprRenderer::beginSync()
+{
+ // nothing to do here
+}
+
+void QQuickPathItemNvprRenderer::setPath(const QQuickPath *path)
+{
+ convertPath(path);
+ m_dirty |= DirtyPath;
+}
+
+void QQuickPathItemNvprRenderer::setStrokeColor(const QColor &color)
+{
+ m_strokeColor = color;
+ m_dirty |= DirtyStyle;
+}
+
+void QQuickPathItemNvprRenderer::setStrokeWidth(qreal w)
+{
+ m_strokeWidth = w;
+ m_dirty |= DirtyStyle;
+}
+
+void QQuickPathItemNvprRenderer::setFillColor(const QColor &color)
+{
+ m_fillColor = color;
+ m_dirty |= DirtyStyle;
+}
+
+void QQuickPathItemNvprRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+{
+ m_fillRule = fillRule;
+ m_dirty |= DirtyFillRule;
+}
+
+void QQuickPathItemNvprRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+{
+ m_joinStyle = joinStyle;
+ m_miterLimit = miterLimit;
+ m_dirty |= DirtyStyle;
+}
+
+void QQuickPathItemNvprRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+{
+ m_capStyle = capStyle;
+ m_dirty |= DirtyStyle;
+}
+
+void QQuickPathItemNvprRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ m_dashActive = strokeStyle == QQuickPathItem::DashLine;
+ m_dashOffset = dashOffset;
+ m_dashPattern = dashPattern;
+ m_dirty |= DirtyDash;
+}
+
+void QQuickPathItemNvprRenderer::setFillGradient(QQuickPathGradient *gradient)
+{
+ m_fillGradientActive = gradient != nullptr;
+ if (gradient) {
+ m_fillGradient.stops = gradient->sortedGradientStops();
+ m_fillGradient.spread = gradient->spread();
+ if (QQuickPathLinearGradient *g = qobject_cast<QQuickPathLinearGradient *>(gradient)) {
+ m_fillGradient.start = QPointF(g->x1(), g->y1());
+ m_fillGradient.end = QPointF(g->x2(), g->y2());
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+ m_dirty |= DirtyFillGradient;
+}
+
+void QQuickPathItemNvprRenderer::endSync()
+{
+ // nothing to do here
+}
+
+void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node)
+{
+ if (m_node != node) {
+ m_node = node;
+ // Scenegraph nodes can be destroyed and then replaced by new ones over
+ // time; hence it is important to mark everything dirty for
+ // updatePathRenderNode(). We can assume the renderer has a full sync
+ // of the data at this point.
+ m_dirty = DirtyAll;
+ }
+}
+
+QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path)
+{
+ QDebugStateSaver saver(debug);
+ debug.space().noquote();
+ debug << "Path with" << path.cmd.count() << "commands";
+ int ci = 0;
+ for (GLubyte cmd : path.cmd) {
+ static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = {
+ { GL_MOVE_TO_NV, "moveTo", 2 },
+ { GL_LINE_TO_NV, "lineTo", 2 },
+ { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 },
+ { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 },
+ { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 },
+ { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 },
+ { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 },
+ { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 },
+ { GL_CLOSE_PATH_NV, "closePath", 0 } };
+ for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) {
+ if (nameTab[i].cmd == cmd) {
+ QByteArray cs;
+ for (int j = 0; j < nameTab[i].coordCount; ++j) {
+ cs.append(QByteArray::number(path.coord[ci++]));
+ cs.append(' ');
+ }
+ debug << "\n " << nameTab[i].s << " " << cs;
+ break;
+ }
+ }
+ }
+ return debug;
+}
+
+static inline void appendCoords(QVector<GLfloat> *v, QQuickCurve *c, QPointF *pos)
+{
+ QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(),
+ c->hasRelativeY() ? pos->y() + c->relativeY() : c->y());
+ v->append(p.x());
+ v->append(p.y());
+ *pos = p;
+}
+
+static inline void appendControlCoords(QVector<GLfloat> *v, QQuickPathQuad *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(),
+ c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+static inline void appendControl1Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(),
+ c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+static inline void appendControl2Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(),
+ c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path)
+{
+ m_path = NvprPath();
+ if (!path)
+ return;
+
+ const QList<QQuickPathElement *> &pp(QQuickPathPrivate::get(path)->_pathElements);
+ if (pp.isEmpty())
+ return;
+
+ QPointF pos(path->startX(), path->startY());
+ m_path.cmd.append(GL_MOVE_TO_NV);
+ m_path.coord.append(pos.x());
+ m_path.coord.append(pos.y());
+
+ for (QQuickPathElement *e : pp) {
+ if (QQuickPathMove *o = qobject_cast<QQuickPathMove *>(e)) {
+ m_path.cmd.append(GL_MOVE_TO_NV);
+ appendCoords(&m_path.coord, o, &pos);
+ } else if (QQuickPathLine *o = qobject_cast<QQuickPathLine *>(e)) {
+ m_path.cmd.append(GL_LINE_TO_NV);
+ appendCoords(&m_path.coord, o, &pos);
+ } else if (QQuickPathQuad *o = qobject_cast<QQuickPathQuad *>(e)) {
+ m_path.cmd.append(GL_QUADRATIC_CURVE_TO_NV);
+ appendControlCoords(&m_path.coord, o, pos);
+ appendCoords(&m_path.coord, o, &pos);
+ } else if (QQuickPathCubic *o = qobject_cast<QQuickPathCubic *>(e)) {
+ m_path.cmd.append(GL_CUBIC_CURVE_TO_NV);
+ appendControl1Coords(&m_path.coord, o, pos);
+ appendControl2Coords(&m_path.coord, o, pos);
+ appendCoords(&m_path.coord, o, &pos);
+ } else if (QQuickPathArc *o = qobject_cast<QQuickPathArc *>(e)) {
+ const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo
+ GLenum cmd;
+ if (o->useLargeArc())
+ cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV;
+ else
+ cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV;
+ m_path.cmd.append(cmd);
+ m_path.coord.append(o->radiusX());
+ m_path.coord.append(o->radiusY());
+ m_path.coord.append(0.0f); // X axis rotation
+ appendCoords(&m_path.coord, o, &pos);
+ } else {
+ qWarning() << "PathItem/NVPR: unsupported Path element" << e;
+ }
+ }
+
+ if (qFuzzyCompare(pos.x(), path->startX()) && qFuzzyCompare(pos.y(), path->startY()))
+ m_path.cmd.append(GL_CLOSE_PATH_NV);
+}
+
+static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity)
+{
+ const float o = c.alphaF() * globalOpacity;
+ return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o);
+}
+
+void QQuickPathItemNvprRenderer::updatePathRenderNode()
+{
+ // Called on the render thread with gui blocked -> update the node with its
+ // own copy of all relevant data.
+
+ if (!m_dirty)
+ return;
+
+ // updatePathRenderNode() can be called several times with different dirty
+ // state before render() gets invoked. So accumulate.
+ m_node->m_dirty |= m_dirty;
+
+ if (m_dirty & DirtyPath)
+ m_node->m_source = m_path;
+
+ if (m_dirty & DirtyStyle) {
+ m_node->m_strokeWidth = m_strokeWidth;
+ m_node->m_strokeColor = qsg_premultiply(m_strokeColor, 1.0f);
+ m_node->m_fillColor = qsg_premultiply(m_fillColor, 1.0f);
+ switch (m_joinStyle) {
+ case QQuickPathItem::MiterJoin:
+ m_node->m_joinStyle = GL_MITER_TRUNCATE_NV;
+ break;
+ case QQuickPathItem::BevelJoin:
+ m_node->m_joinStyle = GL_BEVEL_NV;
+ break;
+ case QQuickPathItem::RoundJoin:
+ m_node->m_joinStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ m_node->m_miterLimit = m_miterLimit;
+ switch (m_capStyle) {
+ case QQuickPathItem::FlatCap:
+ m_node->m_capStyle = GL_FLAT;
+ break;
+ case QQuickPathItem::SquareCap:
+ m_node->m_capStyle = GL_SQUARE_NV;
+ break;
+ case QQuickPathItem::RoundCap:
+ m_node->m_capStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ if (m_dirty & DirtyFillRule) {
+ switch (m_fillRule) {
+ case QQuickPathItem::OddEvenFill:
+ m_node->m_fillRule = GL_COUNT_UP_NV;
+ break;
+ case QQuickPathItem::WindingFill:
+ m_node->m_fillRule = GL_INVERT;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ if (m_dirty & DirtyDash) {
+ m_node->m_dashOffset = m_dashOffset;
+ if (m_dashActive) {
+ m_node->m_dashPattern.resize(m_dashPattern.count());
+ // Multiply by strokeWidth because the PathItem API follows QPen
+ // meaning the input dash pattern here is in width units.
+ for (int i = 0; i < m_dashPattern.count(); ++i)
+ m_node->m_dashPattern[i] = GLfloat(m_dashPattern[i]) * m_strokeWidth;
+ } else {
+ m_node->m_dashPattern.clear();
+ }
+ }
+
+ if (m_dirty & DirtyFillGradient) {
+ m_node->m_fillGradientActive = m_fillGradientActive;
+ if (m_fillGradientActive)
+ m_node->m_fillGradient = m_fillGradient;
+ }
+
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ m_dirty = 0;
+}
+
+bool QQuickPathItemNvprRenderNode::nvprInited = false;
+QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr;
+QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr;
+
+QQuickPathItemNvprRenderNode::QQuickPathItemNvprRenderNode(QQuickPathItem *item)
+ : m_item(item)
+{
+}
+
+QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode()
+{
+ releaseResources();
+}
+
+void QQuickPathItemNvprRenderNode::releaseResources()
+{
+ if (m_path) {
+ nvpr.deletePaths(m_path, 1);
+ m_path = 0;
+ }
+}
+
+void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr)
+{
+ m_nvpr = nvpr;
+}
+
+void QQuickNvprMaterialManager::releaseResources()
+{
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+ for (MaterialDesc &mtl : m_materials) {
+ if (mtl.ppl) {
+ f->glDeleteProgramPipelines(1, &mtl.ppl);
+ mtl = MaterialDesc();
+ }
+ }
+}
+
+QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m)
+{
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+ MaterialDesc &mtl(m_materials[m]);
+
+ if (!mtl.ppl) {
+ if (m == MatSolid) {
+ static const char *fragSrc =
+ "#version 310 es\n"
+ "precision highp float;\n"
+ "out vec4 fragColor;\n"
+ "uniform vec4 color;\n"
+ "uniform float opacity;\n"
+ "void main() {\n"
+ " fragColor = color * opacity;\n"
+ "}\n";
+ if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) {
+ qWarning("NVPR: Failed to create shader pipeline for solid fill");
+ return nullptr;
+ }
+ Q_ASSERT(mtl.ppl && mtl.prg);
+ mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color");
+ Q_ASSERT(mtl.uniLoc[0] >= 0);
+ mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity");
+ Q_ASSERT(mtl.uniLoc[1] >= 0);
+ } else if (m == MatLinearGradient) {
+ static const char *fragSrc =
+ "#version 310 es\n"
+ "precision highp float;\n"
+ "layout(location = 0) in vec2 uv;"
+ "uniform float opacity;\n"
+ "uniform sampler2D gradTab;\n"
+ "uniform vec2 gradStart;\n"
+ "uniform vec2 gradEnd;\n"
+ "out vec4 fragColor;\n"
+ "void main() {\n"
+ " vec2 gradVec = gradEnd - gradStart;\n"
+ " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n"
+ " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n"
+ "}\n";
+ if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) {
+ qWarning("NVPR: Failed to create shader pipeline for linear gradient");
+ return nullptr;
+ }
+ Q_ASSERT(mtl.ppl && mtl.prg);
+ mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity");
+ Q_ASSERT(mtl.uniLoc[1] >= 0);
+ mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart");
+ Q_ASSERT(mtl.uniLoc[2] >= 0);
+ mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd");
+ Q_ASSERT(mtl.uniLoc[3] >= 0);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ f->glBindProgramPipeline(mtl.ppl);
+
+ return &mtl;
+}
+
+void QQuickPathItemNvprRenderNode::updatePath()
+{
+ if (m_dirty & QQuickPathItemNvprRenderer::DirtyPath) {
+ if (!m_path) {
+ m_path = nvpr.genPaths(1);
+ Q_ASSERT(m_path != 0);
+ }
+ nvpr.pathCommands(m_path, m_source.cmd.count(), m_source.cmd.constData(),
+ m_source.coord.count(), GL_FLOAT, m_source.coord.constData());
+ }
+
+ if (m_dirty & QQuickPathItemNvprRenderer::DirtyStyle) {
+ nvpr.pathParameterf(m_path, GL_PATH_STROKE_WIDTH_NV, m_strokeWidth);
+ nvpr.pathParameteri(m_path, GL_PATH_JOIN_STYLE_NV, m_joinStyle);
+ nvpr.pathParameteri(m_path, GL_PATH_MITER_LIMIT_NV, m_miterLimit);
+ nvpr.pathParameteri(m_path, GL_PATH_END_CAPS_NV, m_capStyle);
+ nvpr.pathParameteri(m_path, GL_PATH_DASH_CAPS_NV, m_capStyle);
+ }
+
+ if (m_dirty & QQuickPathItemNvprRenderer::DirtyDash) {
+ nvpr.pathParameterf(m_path, GL_PATH_DASH_OFFSET_NV, m_dashOffset);
+ // count == 0 -> no dash
+ nvpr.pathDashArray(m_path, m_dashPattern.count(), m_dashPattern.constData());
+ }
+}
+
+void QQuickPathItemNvprRenderNode::render(const RenderState *state)
+{
+ if (!nvprInited) {
+ if (!nvpr.create()) {
+ qWarning("NVPR init failed");
+ return;
+ }
+ mtlmgr.create(&nvpr);
+ nvprInited = true;
+ }
+
+ updatePath();
+
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+ f->glUseProgram(0);
+ QQuickNvprMaterialManager::MaterialDesc *mtl;
+ if (m_fillGradientActive)
+ mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient);
+ else
+ mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
+ if (!mtl)
+ return;
+
+ // Assume stencil buffer is cleared to 0 for each frame.
+ // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear.
+ f->glStencilMask(~0);
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+
+ // Depth test against the opaque batches rendered before.
+ f->glEnable(GL_DEPTH_TEST);
+ f->glDepthFunc(GL_LESS);
+ nvpr.pathCoverDepthFunc(GL_LESS);
+ nvpr.pathStencilDepthOffset(-0.05f, -1);
+
+ nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData());
+ nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData());
+
+ if (state->scissorEnabled()) {
+ // scissor rect is already set, just enable scissoring
+ f->glEnable(GL_SCISSOR_TEST);
+ }
+
+ if (!qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive) {
+ if (m_fillGradientActive) {
+ QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient);
+ tx->bind();
+ // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5])
+ // where x and y are in path coordinate space, which is just what
+ // we need since the gradient's start and stop are in that space too.
+ GLfloat coeff[6] = { 1, 0, 0,
+ 0, 1, 0 };
+ nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff);
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y());
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y());
+ } else {
+ f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
+ m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w());
+ }
+ f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
+ nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV);
+ }
+
+ if (!qFuzzyIsNull(m_strokeWidth) && !qFuzzyIsNull(m_strokeColor.w())) {
+ if (m_fillGradientActive)
+ mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
+ f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
+ m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w());
+ f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
+ nvpr.stencilThenCoverStrokePath(m_path, 0x1, ~0, GL_CONVEX_HULL_NV);
+ }
+
+ f->glBindProgramPipeline(0);
+
+ m_dirty = 0;
+}
+
+QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const
+{
+ return BlendState | StencilState | DepthState | ScissorState;
+}
+
+QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const
+{
+ return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer
+}
+
+QRectF QQuickPathItemNvprRenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
+
+bool QQuickPathItemNvprRenderNode::isSupported()
+{
+ static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0;
+ return !nvprDisabled && QQuickNvprFunctions::isSupported();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h
new file mode 100644
index 0000000000..0075572324
--- /dev/null
+++ b/src/quick/items/qquickpathitemnvprrenderer_p.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPATHITEMNVPRRENDERER_P_H
+#define QQUICKPATHITEMNVPRRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpathitem_p_p.h"
+#include <qsgrendernode.h>
+#include <private/qquicknvprfunctions_p.h>
+#include <QColor>
+#include <QVector4D>
+#include <QDebug>
+
+#ifndef QT_NO_OPENGL
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPathItemNvprRenderNode;
+
+class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyStyle = 0x02,
+ DirtyFillRule = 0x04,
+ DirtyDash = 0x08,
+ DirtyFillGradient = 0x10,
+
+ DirtyAll = 0xFF
+ };
+
+ void beginSync() override;
+ void setPath(const QQuickPath *path) override;
+ void setStrokeColor(const QColor &color) override;
+ void setStrokeWidth(qreal w) override;
+ void setFillColor(const QColor &color) override;
+ void setFillRule(QQuickPathItem::FillRule fillRule) override;
+ void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(QQuickPathItem::CapStyle capStyle) override;
+ void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(QQuickPathGradient *gradient) override;
+ void endSync() override;
+ void updatePathRenderNode() override;
+
+ void setNode(QQuickPathItemNvprRenderNode *node);
+
+ struct NvprPath {
+ QVector<GLubyte> cmd;
+ QVector<GLfloat> coord;
+ };
+
+private:
+ void convertPath(const QQuickPath *path);
+
+ QQuickPathItemNvprRenderNode *m_node = nullptr;
+ int m_dirty = 0;
+
+ NvprPath m_path;
+ qreal m_strokeWidth;
+ QColor m_strokeColor;
+ QColor m_fillColor;
+ QQuickPathItem::JoinStyle m_joinStyle;
+ int m_miterLimit;
+ QQuickPathItem::CapStyle m_capStyle;
+ QQuickPathItem::FillRule m_fillRule;
+ bool m_dashActive;
+ qreal m_dashOffset;
+ QVector<qreal> m_dashPattern;
+ bool m_fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc m_fillGradient;
+};
+
+QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path);
+
+class QQuickNvprMaterialManager
+{
+public:
+ enum Material {
+ MatSolid,
+ MatLinearGradient,
+
+ NMaterials
+ };
+
+ struct MaterialDesc {
+ GLuint ppl = 0;
+ GLuint prg = 0;
+ int uniLoc[4];
+ };
+
+ void create(QQuickNvprFunctions *nvpr);
+ MaterialDesc *activateMaterial(Material m);
+ void releaseResources();
+
+private:
+ QQuickNvprFunctions *m_nvpr;
+ MaterialDesc m_materials[NMaterials];
+};
+
+class QQuickPathItemNvprRenderNode : public QSGRenderNode
+{
+public:
+ QQuickPathItemNvprRenderNode(QQuickPathItem *item);
+ ~QQuickPathItemNvprRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+ static bool isSupported();
+
+private:
+ void updatePath();
+
+ static bool nvprInited;
+ static QQuickNvprFunctions nvpr;
+ static QQuickNvprMaterialManager mtlmgr;
+
+ QQuickPathItem *m_item;
+ GLuint m_path = 0;
+ int m_dirty = 0;
+
+ QQuickPathItemNvprRenderer::NvprPath m_source;
+ GLfloat m_strokeWidth;
+ QVector4D m_strokeColor;
+ QVector4D m_fillColor;
+ GLenum m_joinStyle;
+ GLint m_miterLimit;
+ GLenum m_capStyle;
+ GLenum m_fillRule;
+ GLfloat m_dashOffset;
+ QVector<GLfloat> m_dashPattern;
+ bool m_fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc m_fillGradient;
+
+ friend class QQuickPathItemNvprRenderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_OPENGL
+
+#endif // QQUICKPATHITEMNVPRRENDERER_P_H
diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp
new file mode 100644
index 0000000000..40732e4bf9
--- /dev/null
+++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpathitemsoftwarerenderer_p.h"
+#include <private/qquickpath_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickPathItemSoftwareRenderer::beginSync()
+{
+ // nothing to do here
+}
+
+void QQuickPathItemSoftwareRenderer::setPath(const QQuickPath *path)
+{
+ m_path = path ? path->path() : QPainterPath();
+ m_dirty |= DirtyPath;
+}
+
+void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color)
+{
+ m_pen.setColor(color);
+ m_dirty |= DirtyPen;
+}
+
+void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w)
+{
+ m_pen.setWidthF(w);
+ m_dirty |= DirtyPen;
+}
+
+void QQuickPathItemSoftwareRenderer::setFillColor(const QColor &color)
+{
+ m_fillColor = color;
+ m_brush.setColor(m_fillColor);
+ m_dirty |= DirtyBrush;
+}
+
+void QQuickPathItemSoftwareRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+{
+ m_fillRule = Qt::FillRule(fillRule);
+ m_dirty |= DirtyFillRule;
+}
+
+void QQuickPathItemSoftwareRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+{
+ m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ m_pen.setMiterLimit(miterLimit);
+ m_dirty |= DirtyPen;
+}
+
+void QQuickPathItemSoftwareRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+{
+ m_pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ m_dirty |= DirtyPen;
+}
+
+void QQuickPathItemSoftwareRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ switch (strokeStyle) {
+ case QQuickPathItem::SolidLine:
+ m_pen.setStyle(Qt::SolidLine);
+ break;
+ case QQuickPathItem::DashLine:
+ m_pen.setStyle(Qt::CustomDashLine);
+ m_pen.setDashPattern(dashPattern);
+ m_pen.setDashOffset(dashOffset);
+ break;
+ default:
+ break;
+ }
+ m_dirty |= DirtyPen;
+}
+
+void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradient)
+{
+ if (QQuickPathLinearGradient *linearGradient = qobject_cast<QQuickPathLinearGradient *>(gradient)) {
+ QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(),
+ linearGradient->x2(), linearGradient->y2());
+ painterGradient.setStops(linearGradient->sortedGradientStops());
+ switch (gradient->spread()) {
+ case QQuickPathGradient::PadSpread:
+ painterGradient.setSpread(QGradient::PadSpread);
+ break;
+ case QQuickPathGradient::RepeatSpread:
+ painterGradient.setSpread(QGradient::RepeatSpread);
+ break;
+ case QQuickPathGradient::ReflectSpread:
+ painterGradient.setSpread(QGradient::ReflectSpread);
+ break;
+ default:
+ break;
+ }
+ m_brush = QBrush(painterGradient);
+ } else {
+ m_brush = QBrush(m_fillColor);
+ }
+ m_dirty |= DirtyBrush;
+}
+
+void QQuickPathItemSoftwareRenderer::endSync()
+{
+ // nothing to do here
+}
+
+void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node)
+{
+ if (m_node != node) {
+ m_node = node;
+ // Scenegraph nodes can be destroyed and then replaced by new ones over
+ // time; hence it is important to mark everything dirty for
+ // updatePathRenderNode(). We can assume the renderer has a full sync
+ // of the data at this point.
+ m_dirty = DirtyAll;
+ }
+}
+
+void QQuickPathItemSoftwareRenderer::updatePathRenderNode()
+{
+ if (!m_dirty)
+ return;
+
+ // updatePathRenderNode() can be called several times with different dirty
+ // state before render() gets invoked. So accumulate.
+ m_node->m_dirty |= m_dirty;
+
+ if (m_dirty & DirtyPath) {
+ m_node->m_path = m_path;
+ m_node->m_path.setFillRule(m_fillRule);
+ }
+
+ if (m_dirty & DirtyFillRule)
+ m_node->m_path.setFillRule(m_fillRule);
+
+ if (m_dirty & DirtyPen)
+ m_node->m_pen = m_pen;
+
+ if (m_dirty & DirtyBrush)
+ m_node->m_brush = m_brush;
+
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ m_dirty = 0;
+}
+
+QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item)
+ : m_item(item)
+{
+}
+
+QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode()
+{
+ releaseResources();
+}
+
+void QQuickPathItemSoftwareRenderNode::releaseResources()
+{
+}
+
+void QQuickPathItemSoftwareRenderNode::render(const RenderState *state)
+{
+ if (m_path.isEmpty())
+ return;
+
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
+ Q_ASSERT(p);
+
+ p->setTransform(matrix()->toTransform());
+ p->setOpacity(inheritedOpacity());
+
+ const QRegion *clipRegion = state->clipRegion();
+ if (clipRegion && !clipRegion->isEmpty())
+ p->setClipRegion(*clipRegion, Qt::IntersectClip);
+
+ p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen);
+ p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush);
+ p->drawPath(m_path);
+
+ m_dirty = 0;
+}
+
+QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const
+{
+ return 0;
+}
+
+QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const
+{
+ return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect()
+}
+
+QRectF QQuickPathItemSoftwareRenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h
new file mode 100644
index 0000000000..584771425d
--- /dev/null
+++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPATHITEMSOFTWARERENDERER_P_H
+#define QQUICKPATHITEMSOFTWARERENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpathitem_p_p.h"
+#include <qsgrendernode.h>
+#include <QPen>
+#include <QBrush>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPathItemSoftwareRenderNode;
+
+class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyPen = 0x02,
+ DirtyFillRule = 0x04,
+ DirtyBrush = 0x08,
+
+ DirtyAll = 0xFF
+ };
+
+ void beginSync() override;
+ void setPath(const QQuickPath *path) override;
+ void setStrokeColor(const QColor &color) override;
+ void setStrokeWidth(qreal w) override;
+ void setFillColor(const QColor &color) override;
+ void setFillRule(QQuickPathItem::FillRule fillRule) override;
+ void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(QQuickPathItem::CapStyle capStyle) override;
+ void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(QQuickPathGradient *gradient) override;
+ void endSync() override;
+ void updatePathRenderNode() override;
+
+ void setNode(QQuickPathItemSoftwareRenderNode *node);
+
+private:
+ QQuickPathItemSoftwareRenderNode *m_node = nullptr;
+ int m_dirty = 0;
+
+ QPainterPath m_path;
+ QPen m_pen;
+ QColor m_fillColor;
+ QBrush m_brush;
+ Qt::FillRule m_fillRule;
+};
+
+class QQuickPathItemSoftwareRenderNode : public QSGRenderNode
+{
+public:
+ QQuickPathItemSoftwareRenderNode(QQuickPathItem *item);
+ ~QQuickPathItemSoftwareRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+private:
+ QQuickPathItem *m_item;
+ int m_dirty = 0;
+
+ QPainterPath m_path;
+ QPen m_pen;
+ QBrush m_brush;
+
+ friend class QQuickPathItemSoftwareRenderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H
diff --git a/src/quick/items/shaders/lineargradient.frag b/src/quick/items/shaders/lineargradient.frag
new file mode 100644
index 0000000000..7f4a739109
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient.frag
@@ -0,0 +1,9 @@
+uniform sampler2D gradTabTexture;
+uniform highp float opacity;
+
+varying highp float gradTabIndex;
+
+void main()
+{
+ gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity;
+}
diff --git a/src/quick/items/shaders/lineargradient.vert b/src/quick/items/shaders/lineargradient.vert
new file mode 100644
index 0000000000..eb21b8886b
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient.vert
@@ -0,0 +1,15 @@
+attribute vec4 vertexCoord;
+attribute vec4 vertexColor;
+
+uniform mat4 matrix;
+uniform vec2 gradStart;
+uniform vec2 gradEnd;
+
+varying float gradTabIndex;
+
+void main()
+{
+ vec2 gradVec = gradEnd - gradStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gl_Position = matrix * vertexCoord;
+}
diff --git a/src/quick/items/shaders/lineargradient_core.frag b/src/quick/items/shaders/lineargradient_core.frag
new file mode 100644
index 0000000000..5908acfa67
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient_core.frag
@@ -0,0 +1,12 @@
+#version 150 core
+
+uniform sampler2D gradTabTexture;
+uniform float opacity;
+
+in float gradTabIndex;
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity;
+}
diff --git a/src/quick/items/shaders/lineargradient_core.vert b/src/quick/items/shaders/lineargradient_core.vert
new file mode 100644
index 0000000000..60b56f38e3
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient_core.vert
@@ -0,0 +1,17 @@
+#version 150 core
+
+in vec4 vertexCoord;
+in vec4 vertexColor;
+
+uniform mat4 matrix;
+uniform vec2 gradStart;
+uniform vec2 gradEnd;
+
+out float gradTabIndex;
+
+void main()
+{
+ vec2 gradVec = gradEnd - gradStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gl_Position = matrix * vertexCoord;
+}