aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
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;
+}