aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-01-04 15:12:32 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-01-10 14:47:57 +0000
commit0271da9ff4001d26596a9172329691674e147ada (patch)
tree5114cc82e67712b9975b961dcb44e0e5ccb327bf /src/quick
parent7599fbffbf8ad88fa4487cd7d8bad90eb0b2d952 (diff)
Allow multiple paths in a PathItem
Instead of PathItem { item properties stroke/fill properties path: Path { ... } } switch to PathItem { item properties VisualPath { stroke/fill settings Path { ... } } VisualPath { stroke/fill settings Path { ... } } ... } Limiting PathItem to a single path is arguably too limited. Applications will likely try to work this around by using multiple PathItems. While this is not particularly bad for the generic (geometry node based) implementation, it is a massive overkill for the rendernode-based ones. Therefore, avoid the hassle and allow multiple paths with different stroke/fill parameters inside a single PathItem. Change-Id: Ie7980cd656deb7d4cb1ee4eaa3c090c4b0493c7d Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/items/qquickitemsmodule.cpp1
-rw-r--r--src/quick/items/qquickpathitem.cpp460
-rw-r--r--src/quick/items/qquickpathitem_p.h70
-rw-r--r--src/quick/items/qquickpathitem_p_p.h68
-rw-r--r--src/quick/items/qquickpathitemgenericrenderer.cpp358
-rw-r--r--src/quick/items/qquickpathitemgenericrenderer_p.h105
-rw-r--r--src/quick/items/qquickpathitemnvprrenderer.cpp519
-rw-r--r--src/quick/items/qquickpathitemnvprrenderer_p.h108
-rw-r--r--src/quick/items/qquickpathitemsoftwarerenderer.cpp162
-rw-r--r--src/quick/items/qquickpathitemsoftwarerenderer_p.h57
-rw-r--r--src/quick/util/qquickpath.cpp73
11 files changed, 1158 insertions, 823 deletions
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 9c70daab1f..e9439275ac 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -376,6 +376,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathArc, 2>(uri, 2, 9, "PathArc");
qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
qmlRegisterType<QQuickPathItem>(uri, 2, 9, "PathItem");
+ qmlRegisterType<QQuickVisualPath>(uri, 2, 9, "VisualPath");
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");
diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp
index 0a4c721a74..0dce376945 100644
--- a/src/quick/items/qquickpathitem.cpp
+++ b/src/quick/items/qquickpathitem.cpp
@@ -48,333 +48,425 @@
QT_BEGIN_NAMESPACE
-QQuickPathItemPrivate::QQuickPathItemPrivate()
- : rendererType(QQuickPathItem::UnknownRenderer),
- renderer(nullptr),
- path(nullptr),
+QQuickVisualPathPrivate::QQuickVisualPathPrivate()
+ : path(nullptr),
dirty(DirtyAll),
strokeColor(Qt::white),
strokeWidth(1),
fillColor(Qt::white),
- fillRule(QQuickPathItem::OddEvenFill),
- joinStyle(QQuickPathItem::BevelJoin),
+ fillRule(QQuickVisualPath::OddEvenFill),
+ joinStyle(QQuickVisualPath::BevelJoin),
miterLimit(2),
- capStyle(QQuickPathItem::SquareCap),
- strokeStyle(QQuickPathItem::SolidLine),
+ capStyle(QQuickVisualPath::SquareCap),
+ strokeStyle(QQuickVisualPath::SolidLine),
dashOffset(0),
fillGradient(nullptr)
{
dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
}
-QQuickPathItemPrivate::~QQuickPathItemPrivate()
+QQuickVisualPath::QQuickVisualPath(QObject *parent)
+ : QObject(*(new QQuickVisualPathPrivate), parent)
{
- 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)
+QQuickVisualPath::~QQuickVisualPath()
{
- setFlag(ItemHasContents);
}
-QQuickPathItem::~QQuickPathItem()
+QQuickPath *QQuickVisualPath::path() const
{
-}
-
-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);
+ Q_D(const QQuickVisualPath);
return d->path;
}
-void QQuickPathItem::setPath(QQuickPath *path)
+void QQuickVisualPath::setPath(QQuickPath *path)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->path == path)
return;
if (d->path)
qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()),
- this, QQuickPathItem, SLOT(_q_pathChanged()));
+ this, QQuickVisualPath, SLOT(_q_pathChanged()));
d->path = path;
qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()),
- this, QQuickPathItem, SLOT(_q_pathChanged()));
+ this, QQuickVisualPath, SLOT(_q_pathChanged()));
- d->dirty |= QQuickPathItemPrivate::DirtyPath;
+ d->dirty |= QQuickVisualPathPrivate::DirtyPath;
emit pathChanged();
- polish();
+ emit changed();
}
-void QQuickPathItemPrivate::_q_pathChanged()
+void QQuickVisualPathPrivate::_q_pathChanged()
{
- Q_Q(QQuickPathItem);
+ Q_Q(QQuickVisualPath);
dirty |= DirtyPath;
- q->polish();
+ emit q->changed();
}
-QColor QQuickPathItem::strokeColor() const
+QColor QQuickVisualPath::strokeColor() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->strokeColor;
}
-void QQuickPathItem::setStrokeColor(const QColor &color)
+void QQuickVisualPath::setStrokeColor(const QColor &color)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->strokeColor != color) {
d->strokeColor = color;
- d->dirty |= QQuickPathItemPrivate::DirtyStrokeColor;
+ d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor;
emit strokeColorChanged();
- polish();
+ emit changed();
}
}
-qreal QQuickPathItem::strokeWidth() const
+qreal QQuickVisualPath::strokeWidth() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->strokeWidth;
}
-void QQuickPathItem::setStrokeWidth(qreal w)
+void QQuickVisualPath::setStrokeWidth(qreal w)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->strokeWidth != w) {
d->strokeWidth = w;
- d->dirty |= QQuickPathItemPrivate::DirtyStrokeWidth;
+ d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth;
emit strokeWidthChanged();
- polish();
+ emit changed();
}
}
-QColor QQuickPathItem::fillColor() const
+QColor QQuickVisualPath::fillColor() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->fillColor;
}
-void QQuickPathItem::setFillColor(const QColor &color)
+void QQuickVisualPath::setFillColor(const QColor &color)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->fillColor != color) {
d->fillColor = color;
- d->dirty |= QQuickPathItemPrivate::DirtyFillColor;
+ d->dirty |= QQuickVisualPathPrivate::DirtyFillColor;
emit fillColorChanged();
- polish();
+ emit changed();
}
}
-QQuickPathItem::FillRule QQuickPathItem::fillRule() const
+QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->fillRule;
}
-void QQuickPathItem::setFillRule(FillRule fillRule)
+void QQuickVisualPath::setFillRule(FillRule fillRule)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->fillRule != fillRule) {
d->fillRule = fillRule;
- d->dirty |= QQuickPathItemPrivate::DirtyFillRule;
+ d->dirty |= QQuickVisualPathPrivate::DirtyFillRule;
emit fillRuleChanged();
- polish();
+ emit changed();
}
}
-QQuickPathItem::JoinStyle QQuickPathItem::joinStyle() const
+QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->joinStyle;
}
-void QQuickPathItem::setJoinStyle(JoinStyle style)
+void QQuickVisualPath::setJoinStyle(JoinStyle style)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->joinStyle != style) {
d->joinStyle = style;
- d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ d->dirty |= QQuickVisualPathPrivate::DirtyStyle;
emit joinStyleChanged();
- polish();
+ emit changed();
}
}
-int QQuickPathItem::miterLimit() const
+int QQuickVisualPath::miterLimit() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->miterLimit;
}
-void QQuickPathItem::setMiterLimit(int limit)
+void QQuickVisualPath::setMiterLimit(int limit)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->miterLimit != limit) {
d->miterLimit = limit;
- d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ d->dirty |= QQuickVisualPathPrivate::DirtyStyle;
emit miterLimitChanged();
- polish();
+ emit changed();
}
}
-QQuickPathItem::CapStyle QQuickPathItem::capStyle() const
+QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->capStyle;
}
-void QQuickPathItem::setCapStyle(CapStyle style)
+void QQuickVisualPath::setCapStyle(CapStyle style)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->capStyle != style) {
d->capStyle = style;
- d->dirty |= QQuickPathItemPrivate::DirtyStyle;
+ d->dirty |= QQuickVisualPathPrivate::DirtyStyle;
emit capStyleChanged();
- polish();
+ emit changed();
}
}
-QQuickPathItem::StrokeStyle QQuickPathItem::strokeStyle() const
+QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->strokeStyle;
}
-void QQuickPathItem::setStrokeStyle(StrokeStyle style)
+void QQuickVisualPath::setStrokeStyle(StrokeStyle style)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->strokeStyle != style) {
d->strokeStyle = style;
- d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ d->dirty |= QQuickVisualPathPrivate::DirtyDash;
emit strokeStyleChanged();
- polish();
+ emit changed();
}
}
-qreal QQuickPathItem::dashOffset() const
+qreal QQuickVisualPath::dashOffset() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->dashOffset;
}
-void QQuickPathItem::setDashOffset(qreal offset)
+void QQuickVisualPath::setDashOffset(qreal offset)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->dashOffset != offset) {
d->dashOffset = offset;
- d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ d->dirty |= QQuickVisualPathPrivate::DirtyDash;
emit dashOffsetChanged();
- polish();
+ emit changed();
}
}
-QVector<qreal> QQuickPathItem::dashPattern() const
+QVector<qreal> QQuickVisualPath::dashPattern() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->dashPattern;
}
-void QQuickPathItem::setDashPattern(const QVector<qreal> &array)
+void QQuickVisualPath::setDashPattern(const QVector<qreal> &array)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->dashPattern != array) {
d->dashPattern = array;
- d->dirty |= QQuickPathItemPrivate::DirtyDash;
+ d->dirty |= QQuickVisualPathPrivate::DirtyDash;
emit dashPatternChanged();
- polish();
+ emit changed();
}
}
-QQuickPathGradient *QQuickPathItem::fillGradient() const
+QQuickPathGradient *QQuickVisualPath::fillGradient() const
{
- Q_D(const QQuickPathItem);
+ Q_D(const QQuickVisualPath);
return d->fillGradient;
}
-void QQuickPathItem::setFillGradient(QQuickPathGradient *gradient)
+void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient)
{
- Q_D(QQuickPathItem);
+ Q_D(QQuickVisualPath);
if (d->fillGradient != gradient) {
if (d->fillGradient)
qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()),
- this, QQuickPathItem, SLOT(_q_fillGradientChanged()));
+ this, QQuickVisualPath, 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();
+ this, QQuickVisualPath, SLOT(_q_fillGradientChanged()));
+ d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient;
+ emit changed();
}
}
-void QQuickPathItemPrivate::_q_fillGradientChanged()
+void QQuickVisualPathPrivate::_q_fillGradientChanged()
{
- Q_Q(QQuickPathItem);
+ Q_Q(QQuickVisualPath);
dirty |= DirtyFillGradient;
- q->polish();
+ emit q->changed();
}
-void QQuickPathItem::resetFillGradient()
+void QQuickVisualPath::resetFillGradient()
{
setFillGradient(nullptr);
}
+/*!
+ \qmltype PathItem
+ \instantiates QQuickPathItem
+ \inqmlmodule QtQuick
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits Item
+ \brief Renders a path
+ \since 5.10
+
+ 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 \l 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.
+
+ \note Limited support for PathSvg is also provided in most cases. However,
+ there is no guarantee that this element is going to be supported for all
+ future PathItem backends. It is recommended to avoid the PathSvg element in
+ practice.
+
+ See \l Path for a detailed overview of the supported path elements.
+
+ \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
+*/
+
+QQuickPathItemPrivate::QQuickPathItemPrivate()
+ : componentComplete(true),
+ vpChanged(false),
+ rendererType(QQuickPathItem::UnknownRenderer),
+ renderer(nullptr)
+{
+}
+
+QQuickPathItemPrivate::~QQuickPathItemPrivate()
+{
+ delete renderer;
+}
+
+void QQuickPathItemPrivate::_q_visualPathChanged()
+{
+ Q_Q(QQuickPathItem);
+ vpChanged = true;
+ q->polish();
+}
+
+QQuickPathItem::QQuickPathItem(QQuickItem *parent)
+ : QQuickItem(*(new QQuickPathItemPrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickPathItem::~QQuickPathItem()
+{
+}
+
+QQuickPathItem::RendererType QQuickPathItem::rendererType() const
+{
+ Q_D(const QQuickPathItem);
+ return d->rendererType;
+}
+
+static QQuickVisualPath *vpe_at(QQmlListProperty<QQuickVisualPath> *property, int index)
+{
+ QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast<QQuickPathItem *>(property->object));
+ return d->vp.at(index);
+}
+
+static void vpe_append(QQmlListProperty<QQuickVisualPath> *property, QQuickVisualPath *obj)
+{
+ QQuickPathItem *item = static_cast<QQuickPathItem *>(property->object);
+ QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item);
+ d->vp.append(obj);
+
+ if (d->componentComplete) {
+ QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged()));
+ d->_q_visualPathChanged();
+ }
+}
+
+static int vpe_count(QQmlListProperty<QQuickVisualPath> *property)
+{
+ QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast<QQuickPathItem *>(property->object));
+ return d->vp.count();
+}
+
+static void vpe_clear(QQmlListProperty<QQuickVisualPath> *property)
+{
+ QQuickPathItem *item = static_cast<QQuickPathItem *>(property->object);
+ QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item);
+
+ for (QQuickVisualPath *p : d->vp)
+ QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged()));
+
+ d->vp.clear();
+
+ if (d->componentComplete)
+ d->_q_visualPathChanged();
+}
+
+QQmlListProperty<QQuickVisualPath> QQuickPathItem::visualPaths()
+{
+ return QQmlListProperty<QQuickVisualPath>(this,
+ nullptr,
+ vpe_append,
+ vpe_count,
+ vpe_at,
+ vpe_clear);
+}
+
+void QQuickPathItem::classBegin()
+{
+ Q_D(QQuickPathItem);
+ d->componentComplete = false;
+}
+
+void QQuickPathItem::componentComplete()
+{
+ Q_D(QQuickPathItem);
+ d->componentComplete = true;
+
+ for (QQuickVisualPath *p : d->vp)
+ connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged()));
+
+ d->_q_visualPathChanged();
+}
+
void QQuickPathItem::updatePolish()
{
Q_D(QQuickPathItem);
- if (!d->dirty)
+ if (!d->vpChanged)
return;
+ d->vpChanged = false;
+
if (!d->renderer) {
d->createRenderer();
if (!d->renderer)
@@ -392,9 +484,11 @@ void QQuickPathItem::updatePolish()
void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data)
{
+ Q_D(QQuickPathItem);
+
// sync may have been deferred; do it now if the item became visible
if (change == ItemVisibleHasChanged && data.boolValue)
- polish();
+ d->_q_visualPathChanged();
QQuickItem::itemChange(change, data);
}
@@ -407,8 +501,8 @@ QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
Q_D(QQuickPathItem);
if (d->renderer) {
if (!node)
- node = d->createRenderNode();
- d->renderer->updatePathRenderNode();
+ node = d->createNode();
+ d->renderer->updateNode();
}
return node;
}
@@ -444,7 +538,7 @@ void QQuickPathItemPrivate::createRenderer()
}
// the node lives on the render thread
-QSGNode *QQuickPathItemPrivate::createRenderNode()
+QSGNode *QQuickPathItemPrivate::createNode()
{
Q_Q(QQuickPathItem);
QSGNode *node = nullptr;
@@ -454,9 +548,6 @@ QSGNode *QQuickPathItemPrivate::createRenderNode()
if (!ri)
return node;
- const bool hasFill = fillColor != Qt::transparent;
- const bool hasStroke = strokeWidth >= 0.0f && strokeColor != Qt::transparent;
-
switch (ri->graphicsApi()) {
#ifndef QT_NO_OPENGL
case QSGRendererInterface::OpenGL:
@@ -465,9 +556,9 @@ QSGNode *QQuickPathItemPrivate::createRenderNode()
static_cast<QQuickPathItemNvprRenderer *>(renderer)->setNode(
static_cast<QQuickPathItemNvprRenderNode *>(node));
} else {
- node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke);
+ node = new QQuickPathItemGenericNode;
static_cast<QQuickPathItemGenericRenderer *>(renderer)->setRootNode(
- static_cast<QQuickPathItemGenericRootRenderNode *>(node));
+ static_cast<QQuickPathItemGenericNode *>(node));
}
break;
#endif
@@ -486,29 +577,36 @@ QSGNode *QQuickPathItemPrivate::createRenderNode()
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);
+ const int count = vp.count();
+ renderer->beginSync(count);
+
+ for (int i = 0; i < count; ++i) {
+ QQuickVisualPath *p = vp[i];
+ int &dirty(QQuickVisualPathPrivate::get(p)->dirty);
+
+ if (dirty & QQuickVisualPathPrivate::DirtyPath)
+ renderer->setPath(i, p->path());
+ if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor)
+ renderer->setStrokeColor(i, p->strokeColor());
+ if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth)
+ renderer->setStrokeWidth(i, p->strokeWidth());
+ if (dirty & QQuickVisualPathPrivate::DirtyFillColor)
+ renderer->setFillColor(i, p->fillColor());
+ if (dirty & QQuickVisualPathPrivate::DirtyFillRule)
+ renderer->setFillRule(i, p->fillRule());
+ if (dirty & QQuickVisualPathPrivate::DirtyStyle) {
+ renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
+ renderer->setCapStyle(i, p->capStyle());
+ }
+ if (dirty & QQuickVisualPathPrivate::DirtyDash)
+ renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
+ if (dirty & QQuickVisualPathPrivate::DirtyFillGradient)
+ renderer->setFillGradient(i, p->fillGradient());
+
+ dirty = 0;
}
- if (dirty & DirtyDash)
- renderer->setStrokeStyle(strokeStyle, dashOffset, dashPattern);
- if (dirty & DirtyFillGradient)
- renderer->setFillGradient(fillGradient);
renderer->endSync();
- dirty = 0;
}
// ***** gradient support *****
diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h
index 39b407cf87..0c1d0f061b 100644
--- a/src/quick/items/qquickpathitem_p.h
+++ b/src/quick/items/qquickpathitem_p.h
@@ -61,6 +61,7 @@ QT_REQUIRE_CONFIG(quick_path);
QT_BEGIN_NAMESPACE
+class QQuickVisualPathPrivate;
class QQuickPathItemPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject
@@ -150,13 +151,12 @@ private:
QPointF m_end;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem
+class Q_QUICK_PRIVATE_EXPORT QQuickVisualPath : public QObject
{
Q_OBJECT
- Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged)
-
Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged)
+ Q_CLASSINFO("DefaultProperty", "path")
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged)
Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged)
@@ -197,18 +197,8 @@ public:
};
Q_ENUM(StrokeStyle)
- enum RendererType {
- UnknownRenderer,
- GeometryRenderer,
- NvprRenderer,
- SoftwareRenderer
- };
- Q_ENUM(RendererType)
-
- QQuickPathItem(QQuickItem *parent = nullptr);
- ~QQuickPathItem();
-
- RendererType rendererType() const;
+ QQuickVisualPath(QObject *parent = nullptr);
+ ~QQuickVisualPath();
QQuickPath *path() const;
void setPath(QQuickPath *path);
@@ -247,13 +237,8 @@ public:
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 changed();
void pathChanged();
void strokeColorChanged();
void strokeWidthChanged();
@@ -268,12 +253,51 @@ Q_SIGNALS:
void fillGradientChanged();
private:
- Q_DISABLE_COPY(QQuickPathItem)
- Q_DECLARE_PRIVATE(QQuickPathItem)
+ Q_DISABLE_COPY(QQuickVisualPath)
+ Q_DECLARE_PRIVATE(QQuickVisualPath)
Q_PRIVATE_SLOT(d_func(), void _q_pathChanged())
Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged)
+ Q_PROPERTY(QQmlListProperty<QQuickVisualPath> visualPaths READ visualPaths)
+ Q_CLASSINFO("DefaultProperty", "visualPaths")
+
+public:
+ enum RendererType {
+ UnknownRenderer,
+ GeometryRenderer,
+ NvprRenderer,
+ SoftwareRenderer
+ };
+ Q_ENUM(RendererType)
+
+ QQuickPathItem(QQuickItem *parent = nullptr);
+ ~QQuickPathItem();
+
+ RendererType rendererType() const;
+
+ QQmlListProperty<QQuickVisualPath> visualPaths();
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
+ void updatePolish() override;
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
+ void componentComplete() override;
+ void classBegin() override;
+
+Q_SIGNALS:
+ void rendererChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickPathItem)
+ Q_DECLARE_PRIVATE(QQuickPathItem)
+ Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged())
+};
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickPathItem)
diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h
index 366628d867..5e6400edc6 100644
--- a/src/quick/items/qquickpathitem_p_p.h
+++ b/src/quick/items/qquickpathitem_p_p.h
@@ -68,26 +68,26 @@ 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,
+ virtual void beginSync(int totalCount) = 0;
+ virtual void setPath(int index, const QQuickPath *path) = 0;
+ virtual void setStrokeColor(int index, const QColor &color) = 0;
+ virtual void setStrokeWidth(int index, qreal w) = 0;
+ virtual void setFillColor(int index, const QColor &color) = 0;
+ virtual void setFillRule(int index, QQuickVisualPath::FillRule fillRule) = 0;
+ virtual void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) = 0;
+ virtual void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) = 0;
+ virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) = 0;
- virtual void setFillGradient(QQuickPathGradient *gradient) = 0;
+ virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0;
virtual void endSync() = 0;
// Render thread, with gui blocked
- virtual void updatePathRenderNode() = 0;
+ virtual void updateNode() = 0;
};
-class QQuickPathItemPrivate : public QQuickItemPrivate
+class QQuickVisualPathPrivate : public QObjectPrivate
{
- Q_DECLARE_PUBLIC(QQuickPathItem)
+ Q_DECLARE_PUBLIC(QQuickVisualPath)
public:
enum Dirty {
@@ -103,33 +103,51 @@ public:
DirtyAll = 0xFF
};
- QQuickPathItemPrivate();
- ~QQuickPathItemPrivate();
-
- void createRenderer();
- QSGNode *createRenderNode();
- void sync();
+ QQuickVisualPathPrivate();
void _q_pathChanged();
void _q_fillGradientChanged();
- QQuickPathItem::RendererType rendererType;
- QQuickAbstractPathRenderer *renderer;
+ static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); }
+
QQuickPath *path;
int dirty;
QColor strokeColor;
qreal strokeWidth;
QColor fillColor;
- QQuickPathItem::FillRule fillRule;
- QQuickPathItem::JoinStyle joinStyle;
+ QQuickVisualPath::FillRule fillRule;
+ QQuickVisualPath::JoinStyle joinStyle;
int miterLimit;
- QQuickPathItem::CapStyle capStyle;
- QQuickPathItem::StrokeStyle strokeStyle;
+ QQuickVisualPath::CapStyle capStyle;
+ QQuickVisualPath::StrokeStyle strokeStyle;
qreal dashOffset;
QVector<qreal> dashPattern;
QQuickPathGradient *fillGradient;
};
+class QQuickPathItemPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPathItem)
+
+public:
+ QQuickPathItemPrivate();
+ ~QQuickPathItemPrivate();
+
+ void createRenderer();
+ QSGNode *createNode();
+ void sync();
+
+ void _q_visualPathChanged();
+
+ static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); }
+
+ bool componentComplete;
+ bool vpChanged;
+ QQuickPathItem::RendererType rendererType;
+ QQuickAbstractPathRenderer *renderer;
+ QVector<QQuickVisualPath *> vp;
+};
+
#ifndef QT_NO_OPENGL
class QQuickPathItemGradientCache : public QOpenGLSharedResource
diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp
index 4b15daef9a..b42ff1d4b0 100644
--- a/src/quick/items/qquickpathitemgenericrenderer.cpp
+++ b/src/quick/items/qquickpathitemgenericrenderer.cpp
@@ -66,42 +66,16 @@ static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QCol
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)
+QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window)
: 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)
+void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m)
{
switch (m) {
case MatSolidColor:
@@ -126,279 +100,335 @@ void QQuickPathItemGenericRenderNode::activateMaterial(Material m)
}
// sync, and so triangulation too, happens on the gui thread
-void QQuickPathItemGenericRenderer::beginSync()
+void QQuickPathItemGenericRenderer::beginSync(int totalCount)
{
- m_syncDirty = 0;
+ if (m_vp.count() != totalCount) {
+ m_vp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
+ for (VisualPathData &d : m_vp)
+ d.syncDirty = 0;
}
-void QQuickPathItemGenericRenderer::setPath(const QQuickPath *path)
+void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path)
{
- m_path = path ? path->path() : QPainterPath();
- m_syncDirty |= DirtyGeom;
+ VisualPathData &d(m_vp[index]);
+ d.path = path ? path->path() : QPainterPath();
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color)
+void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color)
{
- m_strokeColor = colorToColor4ub(color);
- m_syncDirty |= DirtyColor;
+ VisualPathData &d(m_vp[index]);
+ d.strokeColor = colorToColor4ub(color);
+ d.syncDirty |= DirtyColor;
}
-void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w)
+void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w)
{
- m_strokeWidth = w;
+ VisualPathData &d(m_vp[index]);
+ d.strokeWidth = w;
if (w >= 0.0f)
- m_pen.setWidthF(w);
- m_syncDirty |= DirtyGeom;
+ d.pen.setWidthF(w);
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setFillColor(const QColor &color)
+void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color)
{
- m_fillColor = colorToColor4ub(color);
- m_syncDirty |= DirtyColor;
+ VisualPathData &d(m_vp[index]);
+ d.fillColor = colorToColor4ub(color);
+ d.syncDirty |= DirtyColor;
}
-void QQuickPathItemGenericRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule)
{
- m_fillRule = Qt::FillRule(fillRule);
- m_syncDirty |= DirtyGeom;
+ VisualPathData &d(m_vp[index]);
+ d.fillRule = Qt::FillRule(fillRule);
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit)
{
- m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
- m_pen.setMiterLimit(miterLimit);
- m_syncDirty |= DirtyGeom;
+ VisualPathData &d(m_vp[index]);
+ d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ d.pen.setMiterLimit(miterLimit);
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle)
{
- m_pen.setCapStyle(Qt::PenCapStyle(capStyle));
- m_syncDirty |= DirtyGeom;
+ VisualPathData &d(m_vp[index]);
+ d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
+void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::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);
+ VisualPathData &d(m_vp[index]);
+ d.pen.setStyle(Qt::PenStyle(strokeStyle));
+ if (strokeStyle == QQuickVisualPath::DashLine) {
+ d.pen.setDashPattern(dashPattern);
+ d.pen.setDashOffset(dashOffset);
}
- m_syncDirty |= DirtyGeom;
+ d.syncDirty |= DirtyGeom;
}
-void QQuickPathItemGenericRenderer::setFillGradient(QQuickPathGradient *gradient)
+void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient)
{
- m_fillGradientActive = gradient != nullptr;
+ VisualPathData &d(m_vp[index]);
+ d.fillGradientActive = gradient != nullptr;
if (gradient) {
- m_fillGradient.stops = gradient->sortedGradientStops();
- m_fillGradient.spread = gradient->spread();
+ d.fillGradient.stops = gradient->sortedGradientStops();
+ d.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());
+ d.fillGradient.start = QPointF(g->x1(), g->y1());
+ d.fillGradient.end = QPointF(g->x2(), g->y2());
} else {
Q_UNREACHABLE();
}
}
- m_syncDirty |= DirtyFillGradient;
+ d.syncDirty |= DirtyFillGradient;
}
void QQuickPathItemGenericRenderer::endSync()
{
- if (!m_syncDirty)
- return;
+ for (VisualPathData &d : m_vp) {
+ if (!d.syncDirty)
+ continue;
+
+ m_accDirty |= d.syncDirty;
+
+ // 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 updateNode()
+ // on the render thread (with the gui thread blocked). For our purposes
+ // here syncDirty is still required since geometry regeneration must only
+ // happen when there was an actual change in this particular sync round.
+ d.effectiveDirty |= d.syncDirty;
+
+ if (d.path.isEmpty()) {
+ d.fillVertices.clear();
+ d.fillIndices.clear();
+ d.strokeVertices.clear();
+ continue;
+ }
- // 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;
+ if (d.syncDirty & DirtyGeom) {
+ if (d.fillColor.a)
+ triangulateFill(&d);
+ if (d.strokeWidth >= 0.0f && d.strokeColor.a)
+ triangulateStroke(&d);
+ }
}
-
- triangulateFill();
- triangulateStroke();
}
-void QQuickPathItemGenericRenderer::triangulateFill()
+void QQuickPathItemGenericRenderer::triangulateFill(VisualPathData *d)
{
- m_path.setFillRule(m_fillRule);
+ d->path.setFillRule(d->fillRule);
- const QVectorPath &vp = qtVectorPathForPath(m_path);
+ const QVectorPath &vp = qtVectorPathForPath(d->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());
+ d->fillVertices.resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(d->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);
+ vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, d->fillColor);
- m_fillIndices.resize(ts.indices.size());
- quint16 *idst = m_fillIndices.data();
+ d->fillIndices.resize(ts.indices.size());
+ quint16 *idst = d->fillIndices.data();
if (ts.indices.type() == QVertexIndexVector::UnsignedShort) {
- memcpy(idst, ts.indices.data(), m_fillIndices.count() * sizeof(quint16));
+ memcpy(idst, ts.indices.data(), d->fillIndices.count() * sizeof(quint16));
} else {
const quint32 *isrc = (const quint32 *) ts.indices.data();
- for (int i = 0; i < m_fillIndices.count(); ++i)
+ for (int i = 0; i < d->fillIndices.count(); ++i)
idst[i] = isrc[i];
}
}
-void QQuickPathItemGenericRenderer::triangulateStroke()
+void QQuickPathItemGenericRenderer::triangulateStroke(VisualPathData *d)
{
- const QVectorPath &vp = qtVectorPathForPath(m_path);
+ const QVectorPath &vp = qtVectorPathForPath(d->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);
+ if (d->pen.style() == Qt::SolidLine) {
+ m_stroker.process(vp, d->pen, clip, 0);
} else {
m_dashStroker.setInvScale(inverseScale);
- m_dashStroker.process(vp, m_pen, clip, 0);
+ m_dashStroker.process(vp, d->pen, clip, 0);
QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(),
m_dashStroker.elementTypes(), 0);
- m_stroker.process(dashStroke, m_pen, clip, 0);
+ m_stroker.process(dashStroke, d->pen, clip, 0);
}
if (!m_stroker.vertexCount()) {
- m_strokeVertices.clear();
+ d->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());
+ d->strokeVertices.resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(d->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);
+ vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], d->strokeColor);
}
-void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericRootRenderNode *rn)
+void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node)
{
- 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;
+ if (m_rootNode != node) {
+ m_rootNode = node;
+ m_accDirty |= DirtyList;
}
}
// on the render thread with gui blocked
-void QQuickPathItemGenericRenderer::updatePathRenderNode()
+void QQuickPathItemGenericRenderer::updateNode()
{
- if (!m_effectiveDirty || !m_rootNode)
+ if (!m_rootNode || !m_accDirty)
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;
- }
+// [ m_rootNode ]
+// / / /
+// #0 [ fill ] [ stroke ] [ next ]
+// / / |
+// #1 [ fill ] [ stroke ] [ next ]
+// / / |
+// #2 [ fill ] [ stroke ] [ next ]
+// ...
+// ...
+
+ QQuickPathItemGenericNode **nodePtr = &m_rootNode;
+ QQuickPathItemGenericNode *prevNode = nullptr;
+
+ for (VisualPathData &d : m_vp) {
+ if (!*nodePtr) {
+ *nodePtr = new QQuickPathItemGenericNode;
+ prevNode->m_next = *nodePtr;
+ prevNode->appendChildNode(*nodePtr);
+ }
- if (m_strokeWidth < 0.0f || 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;
+ QQuickPathItemGenericNode *node = *nodePtr;
+
+ if (m_accDirty & DirtyList)
+ d.effectiveDirty |= DirtyGeom;
+ if (!d.effectiveDirty)
+ continue;
+
+ if (d.fillColor.a == 0) {
+ delete node->m_fillNode;
+ node->m_fillNode = nullptr;
+ } else if (!node->m_fillNode) {
+ node->m_fillNode = new QQuickPathItemGenericStrokeFillNode(m_item->window());
+ if (node->m_strokeNode)
+ node->removeChildNode(node->m_strokeNode);
+ node->appendChildNode(node->m_fillNode);
+ if (node->m_strokeNode)
+ node->appendChildNode(node->m_strokeNode);
+ d.effectiveDirty |= DirtyGeom;
+ }
+
+ if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) {
+ delete node->m_strokeNode;
+ node->m_strokeNode = nullptr;
+ } else if (!node->m_strokeNode) {
+ node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window());
+ node->appendChildNode(node->m_strokeNode);
+ d.effectiveDirty |= DirtyGeom;
+ }
+
+ updateFillNode(&d, node);
+ updateStrokeNode(&d, node);
+
+ d.effectiveDirty = 0;
+
+ prevNode = node;
+ nodePtr = &node->m_next;
}
- updateFillNode();
- updateStrokeNode();
+ if (*nodePtr && prevNode) {
+ prevNode->removeChildNode(*nodePtr);
+ delete *nodePtr;
+ *nodePtr = nullptr;
+ }
- m_effectiveDirty = 0;
+ m_accDirty = 0;
}
-void QQuickPathItemGenericRenderer::updateFillNode()
+void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node)
{
- if (!m_rootNode->m_fillNode)
+ if (!node->m_fillNode)
return;
- QQuickPathItemGenericRenderNode *n = m_rootNode->m_fillNode;
+ QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode;
QSGGeometry *g = &n->m_geometry;
- if (m_fillVertices.isEmpty()) {
+ if (d->fillVertices.isEmpty()) {
g->allocate(0, 0);
n->markDirty(QSGNode::DirtyGeometry);
return;
}
- if (m_fillGradientActive) {
- n->activateMaterial(QQuickPathItemGenericRenderNode::MatLinearGradient);
- if (m_effectiveDirty & DirtyFillGradient) {
+ if (d->fillGradientActive) {
+ n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient);
+ if (d->effectiveDirty & DirtyFillGradient) {
// Make a copy of the data that will be accessed by the material on
// the render thread.
- n->m_fillGradient = m_fillGradient;
+ n->m_fillGradient = d->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))
+ if (!(d->effectiveDirty & DirtyGeom))
return;
}
} else {
- n->activateMaterial(QQuickPathItemGenericRenderNode::MatSolidColor);
+ n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor);
// fast path for updating only color values when no change in vertex positions
- if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) {
+ if ((d->effectiveDirty & DirtyColor) && !(d->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);
+ vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor);
n->markDirty(QSGNode::DirtyGeometry);
return;
}
}
- g->allocate(m_fillVertices.count(), m_fillIndices.count());
+ g->allocate(d->fillVertices.count(), d->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());
+ memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
n->markDirty(QSGNode::DirtyGeometry);
}
-void QQuickPathItemGenericRenderer::updateStrokeNode()
+void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node)
{
- if (!m_rootNode->m_strokeNode)
+ if (!node->m_strokeNode)
return;
- if (m_effectiveDirty == DirtyFillGradient) // not applicable
+ if (d->effectiveDirty == DirtyFillGradient) // not applicable
return;
- QQuickPathItemGenericRenderNode *n = m_rootNode->m_strokeNode;
+ QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode;
n->markDirty(QSGNode::DirtyGeometry);
QSGGeometry *g = &n->m_geometry;
- if (m_strokeVertices.isEmpty()) {
+ if (d->strokeVertices.isEmpty()) {
g->allocate(0, 0);
return;
}
- if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) {
+ if ((d->effectiveDirty & DirtyColor) && !(d->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);
+ vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor);
return;
}
- g->allocate(m_strokeVertices.count(), 0);
+ g->allocate(d->strokeVertices.count(), 0);
g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
- memcpy(g->vertexData(), m_strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
}
QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window)
@@ -415,7 +445,7 @@ QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindo
}
QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window,
- QQuickPathItemGenericRenderNode *node)
+ QQuickPathItemGenericStrokeFillNode *node)
{
QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
@@ -458,7 +488,7 @@ void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, Q
if (state.isMatrixDirty())
program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
- QQuickPathItemGenericRenderNode *node = m->node();
+ QQuickPathItemGenericStrokeFillNode *node = m->node();
program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start));
program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end));
@@ -477,8 +507,8 @@ int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) cons
Q_ASSERT(other && type() == other->type());
const QQuickPathItemLinearGradientMaterial *m = static_cast<const QQuickPathItemLinearGradientMaterial *>(other);
- QQuickPathItemGenericRenderNode *a = node();
- QQuickPathItemGenericRenderNode *b = m->node();
+ QQuickPathItemGenericStrokeFillNode *a = node();
+ QQuickPathItemGenericStrokeFillNode *b = m->node();
Q_ASSERT(a && b);
if (a == b)
return 0;
diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h
index a094b9fca6..4454b14a13 100644
--- a/src/quick/items/qquickpathitemgenericrenderer_p.h
+++ b/src/quick/items/qquickpathitemgenericrenderer_p.h
@@ -59,7 +59,7 @@
QT_BEGIN_NAMESPACE
-class QQuickPathItemGenericRootRenderNode;
+class QQuickPathItemGenericNode;
class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer
{
@@ -68,67 +68,69 @@ public:
DirtyGeom = 0x01,
DirtyColor = 0x02,
DirtyFillGradient = 0x04,
-
- DirtyAll = 0xFF
+ DirtyList = 0x08
};
QQuickPathItemGenericRenderer(QQuickItem *item)
: m_item(item),
m_rootNode(nullptr),
- m_effectiveDirty(0)
+ m_accDirty(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,
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
- void setFillGradient(QQuickPathGradient *gradient) override;
+ void setFillGradient(int index, QQuickPathGradient *gradient) override;
void endSync() override;
- void updatePathRenderNode() override;
- void setRootNode(QQuickPathItemGenericRootRenderNode *rn);
+ void updateNode() override;
+
+ void setRootNode(QQuickPathItemGenericNode *node);
struct Color4ub { unsigned char r, g, b, a; };
private:
- void triangulateFill();
- void triangulateStroke();
- void updateFillNode();
- void updateStrokeNode();
+ struct VisualPathData {
+ float strokeWidth;
+ QPen pen;
+ Color4ub strokeColor;
+ Color4ub fillColor;
+ Qt::FillRule fillRule;
+ QPainterPath path;
+ bool fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc fillGradient;
+ QVector<QSGGeometry::ColoredPoint2D> fillVertices;
+ QVector<quint16> fillIndices;
+ QVector<QSGGeometry::ColoredPoint2D> strokeVertices;
+ int syncDirty;
+ int effectiveDirty = 0;
+ };
+
+ void triangulateFill(VisualPathData *d);
+ void triangulateStroke(VisualPathData *d);
+
+ void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node);
+ void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node);
QQuickItem *m_item;
- QQuickPathItemGenericRootRenderNode *m_rootNode;
+ QQuickPathItemGenericNode *m_rootNode;
QTriangulatingStroker m_stroker;
QDashedStrokeProcessor m_dashStroker;
-
- float m_strokeWidth;
- 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;
+ QVector<VisualPathData> m_vp;
+ int m_accDirty;
};
-class QQuickPathItemGenericRenderNode : public QSGGeometryNode
+class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode
{
public:
- QQuickPathItemGenericRenderNode(QQuickWindow *window, QQuickPathItemGenericRootRenderNode *rootNode);
- ~QQuickPathItemGenericRenderNode();
+ QQuickPathItemGenericStrokeFillNode(QQuickWindow *window);
enum Material {
MatSolidColor,
@@ -138,7 +140,6 @@ public:
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;
@@ -146,7 +147,6 @@ public:
private:
QSGGeometry m_geometry;
QQuickWindow *m_window;
- QQuickPathItemGenericRootRenderNode *m_rootNode;
QSGMaterial *m_material;
QScopedPointer<QSGMaterial> m_solidColorMaterial;
QScopedPointer<QSGMaterial> m_linearGradientMaterial;
@@ -154,24 +154,19 @@ private:
friend class QQuickPathItemGenericRenderer;
};
-class QQuickPathItemGenericRootRenderNode : public QSGNode
+class QQuickPathItemGenericNode : public QSGNode
{
public:
- QQuickPathItemGenericRootRenderNode(QQuickWindow *window, bool hasFill, bool hasStroke);
- ~QQuickPathItemGenericRootRenderNode();
-
-private:
- QQuickPathItemGenericRenderNode *m_fillNode;
- QQuickPathItemGenericRenderNode *m_strokeNode;
-
- friend class QQuickPathItemGenericRenderer;
+ QQuickPathItemGenericStrokeFillNode *m_fillNode = nullptr;
+ QQuickPathItemGenericStrokeFillNode *m_strokeNode = nullptr;
+ QQuickPathItemGenericNode *m_next = nullptr;
};
class QQuickPathItemGenericMaterialFactory
{
public:
static QSGMaterial *createVertexColor(QQuickWindow *window);
- static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericRenderNode *node);
+ static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericStrokeFillNode *node);
};
#ifndef QT_NO_OPENGL
@@ -197,7 +192,7 @@ private:
class QQuickPathItemLinearGradientMaterial : public QSGMaterial
{
public:
- QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericRenderNode *node)
+ QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node)
: m_node(node)
{
// Passing RequiresFullMatrix is essential in order to prevent the
@@ -220,10 +215,10 @@ public:
return new QQuickPathItemLinearGradientShader;
}
- QQuickPathItemGenericRenderNode *node() const { return m_node; }
+ QQuickPathItemGenericStrokeFillNode *node() const { return m_node; }
private:
- QQuickPathItemGenericRenderNode *m_node;
+ QQuickPathItemGenericStrokeFillNode *m_node;
};
#endif // QT_NO_OPENGL
diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp
index 5641d33f85..2338e51ff8 100644
--- a/src/quick/items/qquickpathitemnvprrenderer.cpp
+++ b/src/quick/items/qquickpathitemnvprrenderer.cpp
@@ -46,93 +46,109 @@
QT_BEGIN_NAMESPACE
-void QQuickPathItemNvprRenderer::beginSync()
+void QQuickPathItemNvprRenderer::beginSync(int totalCount)
{
- // nothing to do here
+ if (m_vp.count() != totalCount) {
+ m_vp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
}
-void QQuickPathItemNvprRenderer::setPath(const QQuickPath *path)
+void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path)
{
- convertPath(path);
- m_dirty |= DirtyPath;
+ VisualPathGuiData &d(m_vp[index]);
+ convertPath(path, &d);
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
}
-void QQuickPathItemNvprRenderer::setStrokeColor(const QColor &color)
+void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color)
{
- m_strokeColor = color;
- m_dirty |= DirtyStyle;
+ VisualPathGuiData &d(m_vp[index]);
+ d.strokeColor = color;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
}
-void QQuickPathItemNvprRenderer::setStrokeWidth(qreal w)
+void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w)
{
- m_strokeWidth = w;
- m_dirty |= DirtyStyle;
+ VisualPathGuiData &d(m_vp[index]);
+ d.strokeWidth = w;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
}
-void QQuickPathItemNvprRenderer::setFillColor(const QColor &color)
+void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color)
{
- m_fillColor = color;
- m_dirty |= DirtyStyle;
+ VisualPathGuiData &d(m_vp[index]);
+ d.fillColor = color;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
}
-void QQuickPathItemNvprRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule)
{
- m_fillRule = fillRule;
- m_dirty |= DirtyFillRule;
+ VisualPathGuiData &d(m_vp[index]);
+ d.fillRule = fillRule;
+ d.dirty |= DirtyFillRule;
+ m_accDirty |= DirtyFillRule;
}
-void QQuickPathItemNvprRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit)
{
- m_joinStyle = joinStyle;
- m_miterLimit = miterLimit;
- m_dirty |= DirtyStyle;
+ VisualPathGuiData &d(m_vp[index]);
+ d.joinStyle = joinStyle;
+ d.miterLimit = miterLimit;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
}
-void QQuickPathItemNvprRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle)
{
- m_capStyle = capStyle;
- m_dirty |= DirtyStyle;
+ VisualPathGuiData &d(m_vp[index]);
+ d.capStyle = capStyle;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
}
-void QQuickPathItemNvprRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
- qreal dashOffset, const QVector<qreal> &dashPattern)
+void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
{
- m_dashActive = strokeStyle == QQuickPathItem::DashLine;
- m_dashOffset = dashOffset;
- m_dashPattern = dashPattern;
- m_dirty |= DirtyDash;
+ VisualPathGuiData &d(m_vp[index]);
+ d.dashActive = strokeStyle == QQuickVisualPath::DashLine;
+ d.dashOffset = dashOffset;
+ d.dashPattern = dashPattern;
+ d.dirty |= DirtyDash;
+ m_accDirty |= DirtyDash;
}
-void QQuickPathItemNvprRenderer::setFillGradient(QQuickPathGradient *gradient)
+void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient *gradient)
{
- m_fillGradientActive = gradient != nullptr;
+ VisualPathGuiData &d(m_vp[index]);
+ d.fillGradientActive = gradient != nullptr;
if (gradient) {
- m_fillGradient.stops = gradient->sortedGradientStops();
- m_fillGradient.spread = gradient->spread();
+ d.fillGradient.stops = gradient->sortedGradientStops();
+ d.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());
+ d.fillGradient.start = QPointF(g->x1(), g->y1());
+ d.fillGradient.end = QPointF(g->x2(), g->y2());
} else {
Q_UNREACHABLE();
}
}
- m_dirty |= DirtyFillGradient;
+ d.dirty |= DirtyFillGradient;
+ m_accDirty |= 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;
+ m_accDirty |= DirtyList;
}
}
@@ -140,6 +156,10 @@ QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path
{
QDebugStateSaver saver(debug);
debug.space().noquote();
+ if (!path.str.isEmpty()) {
+ debug << "Path with SVG string" << path.str;
+ return debug;
+ }
debug << "Path with" << path.cmd.count() << "commands";
int ci = 0;
for (GLubyte cmd : path.cmd) {
@@ -201,9 +221,9 @@ static inline void appendControl2Coords(QVector<GLfloat> *v, QQuickPathCubic *c,
v->append(p.y());
}
-void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path)
+void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d)
{
- m_path = NvprPath();
+ d->path = NvprPath();
if (!path)
return;
@@ -211,27 +231,31 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path)
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());
+ QPointF startPos(path->startX(), path->startY());
+ QPointF pos(startPos);
+ if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) {
+ d->path.cmd.append(GL_MOVE_TO_NV);
+ d->path.coord.append(pos.x());
+ d->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);
+ d->path.cmd.append(GL_MOVE_TO_NV);
+ appendCoords(&d->path.coord, o, &pos);
+ startPos = pos;
} else if (QQuickPathLine *o = qobject_cast<QQuickPathLine *>(e)) {
- m_path.cmd.append(GL_LINE_TO_NV);
- appendCoords(&m_path.coord, o, &pos);
+ d->path.cmd.append(GL_LINE_TO_NV);
+ appendCoords(&d->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);
+ d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV);
+ appendControlCoords(&d->path.coord, o, pos);
+ appendCoords(&d->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);
+ d->path.cmd.append(GL_CUBIC_CURVE_TO_NV);
+ appendControl1Coords(&d->path.coord, o, pos);
+ appendControl2Coords(&d->path.coord, o, pos);
+ appendCoords(&d->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;
@@ -239,18 +263,29 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path)
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(o->xAxisRotation());
- appendCoords(&m_path.coord, o, &pos);
+ d->path.cmd.append(cmd);
+ d->path.coord.append(o->radiusX());
+ d->path.coord.append(o->radiusY());
+ d->path.coord.append(o->xAxisRotation());
+ appendCoords(&d->path.coord, o, &pos);
+ } else if (QQuickPathSvg *o = qobject_cast<QQuickPathSvg *>(e)) {
+ // PathSvg cannot be combined with other elements. But take at
+ // least startX and startY into account.
+ if (d->path.str.isEmpty())
+ d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8();
+ d->path.str.append(o->path().toUtf8());
} 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);
+ // For compatibility with QTriangulatingStroker. SVG and others would not
+ // implicitly close the path when end_pos == start_pos (start_pos being the
+ // last moveTo pos); that would still need an explicit 'z' or similar. We
+ // don't have an explicit close command, so just fake a close when the
+ // positions match.
+ if (pos == startPos)
+ d->path.cmd.append(GL_CLOSE_PATH_NV);
}
static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity)
@@ -259,88 +294,103 @@ static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity)
return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o);
}
-void QQuickPathItemNvprRenderer::updatePathRenderNode()
+void QQuickPathItemNvprRenderer::updateNode()
{
// Called on the render thread with gui blocked -> update the node with its
// own copy of all relevant data.
- if (!m_dirty)
+ if (!m_accDirty)
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();
+ const int count = m_vp.count();
+ const bool listChanged = m_accDirty & DirtyList;
+ if (listChanged)
+ m_node->m_vp.resize(count);
+
+ for (int i = 0; i < count; ++i) {
+ VisualPathGuiData &src(m_vp[i]);
+ QQuickPathItemNvprRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]);
+
+ int dirty = src.dirty;
+ src.dirty = 0;
+ if (listChanged)
+ dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient;
+
+ // updateNode() can be called several times with different dirty
+ // states before render() gets invoked. So accumulate.
+ dst.dirty |= dirty;
+
+ if (dirty & DirtyPath)
+ dst.source = src.path;
+
+ if (dirty & DirtyStyle) {
+ dst.strokeWidth = src.strokeWidth;
+ dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f);
+ dst.fillColor = qsg_premultiply(src.fillColor, 1.0f);
+ switch (src.joinStyle) {
+ case QQuickVisualPath::MiterJoin:
+ dst.joinStyle = GL_MITER_TRUNCATE_NV;
+ break;
+ case QQuickVisualPath::BevelJoin:
+ dst.joinStyle = GL_BEVEL_NV;
+ break;
+ case QQuickVisualPath::RoundJoin:
+ dst.joinStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ dst.miterLimit = src.miterLimit;
+ switch (src.capStyle) {
+ case QQuickVisualPath::FlatCap:
+ dst.capStyle = GL_FLAT;
+ break;
+ case QQuickVisualPath::SquareCap:
+ dst.capStyle = GL_SQUARE_NV;
+ break;
+ case QQuickVisualPath::RoundCap:
+ dst.capStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
}
- }
- if (m_dirty & DirtyFillRule) {
- switch (m_fillRule) {
- case QQuickPathItem::OddEvenFill:
- m_node->m_fillRule = GL_INVERT;
- break;
- case QQuickPathItem::WindingFill:
- m_node->m_fillRule = GL_COUNT_UP_NV;
- break;
- default:
- Q_UNREACHABLE();
+ if (dirty & DirtyFillRule) {
+ switch (src.fillRule) {
+ case QQuickVisualPath::OddEvenFill:
+ dst.fillRule = GL_INVERT;
+ break;
+ case QQuickVisualPath::WindingFill:
+ dst.fillRule = GL_COUNT_UP_NV;
+ 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 (dirty & DirtyDash) {
+ dst.dashOffset = src.dashOffset;
+ if (src.dashActive) {
+ dst.dashPattern.resize(src.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 < src.dashPattern.count(); ++i)
+ dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth;
+ } else {
+ dst.dashPattern.clear();
+ }
}
- }
- if (m_dirty & DirtyFillGradient) {
- m_node->m_fillGradientActive = m_fillGradientActive;
- if (m_fillGradientActive)
- m_node->m_fillGradient = m_fillGradient;
+ if (dirty & DirtyFillGradient) {
+ dst.fillGradientActive = src.fillGradientActive;
+ if (src.fillGradientActive)
+ dst.fillGradient = src.fillGradient;
+ }
}
m_node->markDirty(QSGNode::DirtyMaterial);
- m_dirty = 0;
+ m_accDirty = 0;
}
bool QQuickPathItemNvprRenderNode::nvprInited = false;
@@ -359,14 +409,15 @@ QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode()
void QQuickPathItemNvprRenderNode::releaseResources()
{
- if (m_path) {
- nvpr.deletePaths(m_path, 1);
- m_path = 0;
- }
-
- if (m_fallbackFbo) {
- delete m_fallbackFbo;
- m_fallbackFbo = nullptr;
+ for (VisualPathRenderData &d : m_vp) {
+ if (d.path) {
+ nvpr.deletePaths(d.path, 1);
+ d.path = 0;
+ }
+ if (d.fallbackFbo) {
+ delete d.fallbackFbo;
+ d.fallbackFbo = nullptr;
+ }
}
m_fallbackBlitter.destroy();
@@ -449,48 +500,52 @@ QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMate
return &mtl;
}
-void QQuickPathItemNvprRenderNode::updatePath()
+void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d)
{
- if (m_dirty & QQuickPathItemNvprRenderer::DirtyPath) {
- if (!m_path) {
- m_path = nvpr.genPaths(1);
- Q_ASSERT(m_path != 0);
+ if (d->dirty & QQuickPathItemNvprRenderer::DirtyPath) {
+ if (!d->path) {
+ d->path = nvpr.genPaths(1);
+ Q_ASSERT(d->path != 0);
+ }
+ if (d->source.str.isEmpty()) {
+ nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(),
+ d->source.coord.count(), GL_FLOAT, d->source.coord.constData());
+ } else {
+ nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData());
}
- 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 (d->dirty & QQuickPathItemNvprRenderer::DirtyStyle) {
+ nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth);
+ nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle);
+ nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit);
+ nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle);
+ nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle);
}
- if (m_dirty & QQuickPathItemNvprRenderer::DirtyDash) {
- nvpr.pathParameterf(m_path, GL_PATH_DASH_OFFSET_NV, m_dashOffset);
+ if (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) {
+ nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset);
// count == 0 -> no dash
- nvpr.pathDashArray(m_path, m_dashPattern.count(), m_dashPattern.constData());
+ nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData());
}
}
-void QQuickPathItemNvprRenderNode::renderStroke(int strokeStencilValue, int writeMask)
+void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask)
{
QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
- m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w());
+ d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w());
f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
- nvpr.stencilThenCoverStrokePath(m_path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV);
+ nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV);
}
-void QQuickPathItemNvprRenderNode::renderFill()
+void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d)
{
QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr;
- if (m_fillGradientActive) {
+ if (d->fillGradientActive) {
mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient);
- QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient);
+ QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(d->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
@@ -498,20 +553,20 @@ void QQuickPathItemNvprRenderNode::renderFill()
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());
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y());
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y());
} else {
mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
- m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w());
+ d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w());
}
f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
const int writeMask = 0xFF;
- nvpr.stencilThenCoverFillPath(m_path, m_fillRule, writeMask, GL_BOUNDING_BOX_NV);
+ nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV);
}
-void QQuickPathItemNvprRenderNode::renderOffscreenFill()
+void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d)
{
QQuickWindow *w = m_item->window();
const qreal dpr = w->effectiveDevicePixelRatio();
@@ -520,13 +575,13 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill()
if (rtSize.isEmpty())
rtSize = w->size() * dpr;
- if (m_fallbackFbo && m_fallbackFbo->size() != itemSize) {
- delete m_fallbackFbo;
- m_fallbackFbo = nullptr;
+ if (d->fallbackFbo && d->fallbackFbo->size() != itemSize) {
+ delete d->fallbackFbo;
+ d->fallbackFbo = nullptr;
}
- if (!m_fallbackFbo)
- m_fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil);
- if (!m_fallbackFbo->bind())
+ if (!d->fallbackFbo)
+ d->fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil);
+ if (!d->fallbackFbo->bind())
return;
f->glViewport(0, 0, itemSize.width(), itemSize.height());
@@ -541,9 +596,9 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill()
proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1);
nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData());
- renderFill();
+ renderFill(d);
- m_fallbackFbo->release();
+ d->fallbackFbo->release();
f->glViewport(0, 0, rtSize.width(), rtSize.height());
}
@@ -573,8 +628,6 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state)
nvprInited = true;
}
- updatePath();
-
f->glUseProgram(0);
f->glStencilMask(~0);
f->glEnable(GL_STENCIL_TEST);
@@ -583,70 +636,74 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state)
// when true, the stencil buffer already has a clip path with a ref value of sv
const int sv = state->stencilValue();
- const bool hasFill = !qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive;
- const bool hasStroke = m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w());
+ for (VisualPathRenderData &d : m_vp) {
+ updatePath(&d);
- if (hasFill && stencilClip) {
- // Fall back to a texture when complex clipping is in use and we have
- // to fill. Reconciling glStencilFillPath's and the scenegraph's clip
- // stencil semantics has not succeeded so far...
- renderOffscreenFill();
- }
+ const bool hasFill = !qFuzzyIsNull(d.fillColor.w()) || d.fillGradientActive;
+ const bool hasStroke = d.strokeWidth >= 0.0f && !qFuzzyIsNull(d.strokeColor.w());
+
+ if (hasFill && stencilClip) {
+ // Fall back to a texture when complex clipping is in use and we have
+ // to fill. Reconciling glStencilFillPath's and the scenegraph's clip
+ // stencil semantics has not succeeded so far...
+ renderOffscreenFill(&d);
+ }
- // 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);
+ // 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());
+ 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 (state->scissorEnabled()) {
+ // scissor rect is already set, just enable scissoring
+ f->glEnable(GL_SCISSOR_TEST);
+ }
- // Fill!
- if (hasFill) {
- if (!stencilClip) {
- setupStencilForCover(false, 0);
- renderFill();
- } else {
- if (!m_fallbackBlitter.isCreated())
- m_fallbackBlitter.create();
- f->glStencilFunc(GL_EQUAL, sv, 0xFF);
- f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- m_fallbackBlitter.texturedQuad(m_fallbackFbo->texture(), m_fallbackFbo->size(),
- *state->projectionMatrix(), *matrix(),
- inheritedOpacity());
+ // Fill!
+ if (hasFill) {
+ if (!stencilClip) {
+ setupStencilForCover(false, 0);
+ renderFill(&d);
+ } else {
+ if (!m_fallbackBlitter.isCreated())
+ m_fallbackBlitter.create();
+ f->glStencilFunc(GL_EQUAL, sv, 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(),
+ *state->projectionMatrix(), *matrix(),
+ inheritedOpacity());
+ }
}
- }
- // Stroke!
- if (hasStroke) {
- const int strokeStencilValue = 0x80;
- const int writeMask = 0x80;
-
- setupStencilForCover(stencilClip, sv);
- if (stencilClip) {
- // for the stencil step (eff. read mask == 0xFF & ~writeMask)
- nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF);
- // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F.
- // This assumes the clip stencil value is <= 127.
- if (sv >= strokeStencilValue)
- qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv);
+ // Stroke!
+ if (hasStroke) {
+ const int strokeStencilValue = 0x80;
+ const int writeMask = 0x80;
+
+ setupStencilForCover(stencilClip, sv);
+ if (stencilClip) {
+ // for the stencil step (eff. read mask == 0xFF & ~writeMask)
+ nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF);
+ // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F.
+ // This assumes the clip stencil value is <= 127.
+ if (sv >= strokeStencilValue)
+ qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv);
+ }
+
+ renderStroke(&d, strokeStencilValue, writeMask);
}
- renderStroke(strokeStencilValue, writeMask);
- }
+ if (stencilClip)
+ nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0);
- if (stencilClip)
- nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0);
+ d.dirty = 0;
+ }
f->glBindProgramPipeline(0);
-
- m_dirty = 0;
}
QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const
diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h
index 7dd2d564c5..f594609e83 100644
--- a/src/quick/items/qquickpathitemnvprrenderer_p.h
+++ b/src/quick/items/qquickpathitemnvprrenderer_p.h
@@ -76,55 +76,61 @@ public:
DirtyFillRule = 0x04,
DirtyDash = 0x08,
DirtyFillGradient = 0x10,
-
- DirtyAll = 0xFF
+ DirtyList = 0x20
};
QQuickPathItemNvprRenderer(QQuickItem *item)
: m_item(item)
{ }
- 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,
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
- void setFillGradient(QQuickPathGradient *gradient) override;
+ void setFillGradient(int index, QQuickPathGradient *gradient) override;
void endSync() override;
- void updatePathRenderNode() override;
+
+ void updateNode() override;
void setNode(QQuickPathItemNvprRenderNode *node);
struct NvprPath {
QVector<GLubyte> cmd;
QVector<GLfloat> coord;
+ QByteArray str;
};
private:
- void convertPath(const QQuickPath *path);
+ struct VisualPathGuiData {
+ int dirty = 0;
+ NvprPath path;
+ qreal strokeWidth;
+ QColor strokeColor;
+ QColor fillColor;
+ QQuickVisualPath::JoinStyle joinStyle;
+ int miterLimit;
+ QQuickVisualPath::CapStyle capStyle;
+ QQuickVisualPath::FillRule fillRule;
+ bool dashActive;
+ qreal dashOffset;
+ QVector<qreal> dashPattern;
+ bool fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc fillGradient;
+ };
+
+ void convertPath(const QQuickPath *path, VisualPathGuiData *d);
QQuickItem *m_item;
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;
+ int m_accDirty = 0;
+
+ QVector<VisualPathGuiData> m_vp;
};
QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path);
@@ -187,10 +193,28 @@ public:
static bool isSupported();
private:
- void updatePath();
- void renderStroke(int strokeStencilValue, int writeMask);
- void renderFill();
- void renderOffscreenFill();
+ struct VisualPathRenderData {
+ GLuint path = 0;
+ int dirty = 0;
+ QQuickPathItemNvprRenderer::NvprPath source;
+ GLfloat strokeWidth;
+ QVector4D strokeColor;
+ QVector4D fillColor;
+ GLenum joinStyle;
+ GLint miterLimit;
+ GLenum capStyle;
+ GLenum fillRule;
+ GLfloat dashOffset;
+ QVector<GLfloat> dashPattern;
+ bool fillGradientActive;
+ QQuickPathItemGradientCache::GradientDesc fillGradient;
+ QOpenGLFramebufferObject *fallbackFbo = nullptr;
+ };
+
+ void updatePath(VisualPathRenderData *d);
+ void renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask);
+ void renderFill(VisualPathRenderData *d);
+ void renderOffscreenFill(VisualPathRenderData *d);
void setupStencilForCover(bool stencilClip, int sv);
static bool nvprInited;
@@ -198,25 +222,11 @@ private:
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;
- QOpenGLFramebufferObject *m_fallbackFbo = nullptr;
QQuickNvprBlitter m_fallbackBlitter;
QOpenGLExtraFunctions *f = nullptr;
+ QVector<VisualPathRenderData> m_vp;
+
friend class QQuickPathItemNvprRenderer;
};
diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp
index c7b52e641f..6c3cf73a56 100644
--- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp
+++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp
@@ -42,77 +42,97 @@
QT_BEGIN_NAMESPACE
-void QQuickPathItemSoftwareRenderer::beginSync()
+void QQuickPathItemSoftwareRenderer::beginSync(int totalCount)
{
- // nothing to do here
+ if (m_vp.count() != totalCount) {
+ m_vp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
}
-void QQuickPathItemSoftwareRenderer::setPath(const QQuickPath *path)
+void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path)
{
- m_path = path ? path->path() : QPainterPath();
- m_dirty |= DirtyPath;
+ VisualPathGuiData &d(m_vp[index]);
+ d.path = path ? path->path() : QPainterPath();
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
}
-void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color)
+void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color)
{
- m_pen.setColor(color);
- m_dirty |= DirtyPen;
+ VisualPathGuiData &d(m_vp[index]);
+ d.pen.setColor(color);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
}
-void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w)
+void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w)
{
- m_strokeWidth = w;
+ VisualPathGuiData &d(m_vp[index]);
+ d.strokeWidth = w;
if (w >= 0.0f)
- m_pen.setWidthF(w);
- m_dirty |= DirtyPen;
+ d.pen.setWidthF(w);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
}
-void QQuickPathItemSoftwareRenderer::setFillColor(const QColor &color)
+void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color)
{
- m_fillColor = color;
- m_brush.setColor(m_fillColor);
- m_dirty |= DirtyBrush;
+ VisualPathGuiData &d(m_vp[index]);
+ d.fillColor = color;
+ d.brush.setColor(color);
+ d.dirty |= DirtyBrush;
+ m_accDirty |= DirtyBrush;
}
-void QQuickPathItemSoftwareRenderer::setFillRule(QQuickPathItem::FillRule fillRule)
+void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule)
{
- m_fillRule = Qt::FillRule(fillRule);
- m_dirty |= DirtyFillRule;
+ VisualPathGuiData &d(m_vp[index]);
+ d.fillRule = Qt::FillRule(fillRule);
+ d.dirty |= DirtyFillRule;
+ m_accDirty |= DirtyFillRule;
}
-void QQuickPathItemSoftwareRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit)
+void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit)
{
- m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
- m_pen.setMiterLimit(miterLimit);
- m_dirty |= DirtyPen;
+ VisualPathGuiData &d(m_vp[index]);
+ d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ d.pen.setMiterLimit(miterLimit);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
}
-void QQuickPathItemSoftwareRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle)
+void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle)
{
- m_pen.setCapStyle(Qt::PenCapStyle(capStyle));
- m_dirty |= DirtyPen;
+ VisualPathGuiData &d(m_vp[index]);
+ d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
}
-void QQuickPathItemSoftwareRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle,
- qreal dashOffset, const QVector<qreal> &dashPattern)
+void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
{
+ VisualPathGuiData &d(m_vp[index]);
switch (strokeStyle) {
- case QQuickPathItem::SolidLine:
- m_pen.setStyle(Qt::SolidLine);
+ case QQuickVisualPath::SolidLine:
+ d.pen.setStyle(Qt::SolidLine);
break;
- case QQuickPathItem::DashLine:
- m_pen.setStyle(Qt::CustomDashLine);
- m_pen.setDashPattern(dashPattern);
- m_pen.setDashOffset(dashOffset);
+ case QQuickVisualPath::DashLine:
+ d.pen.setStyle(Qt::CustomDashLine);
+ d.pen.setDashPattern(dashPattern);
+ d.pen.setDashOffset(dashOffset);
break;
default:
break;
}
- m_dirty |= DirtyPen;
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
}
-void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradient)
+void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient)
{
+ VisualPathGuiData &d(m_vp[index]);
if (QQuickPathLinearGradient *linearGradient = qobject_cast<QQuickPathLinearGradient *>(gradient)) {
QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(),
linearGradient->x2(), linearGradient->y2());
@@ -130,57 +150,61 @@ void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradien
default:
break;
}
- m_brush = QBrush(painterGradient);
+ d.brush = QBrush(painterGradient);
} else {
- m_brush = QBrush(m_fillColor);
+ d.brush = QBrush(d.fillColor);
}
- m_dirty |= DirtyBrush;
+ d.dirty |= DirtyBrush;
+ m_accDirty |= 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;
+ m_accDirty |= DirtyList;
}
}
-void QQuickPathItemSoftwareRenderer::updatePathRenderNode()
+void QQuickPathItemSoftwareRenderer::updateNode()
{
- if (!m_dirty)
+ if (!m_accDirty)
return;
- // updatePathRenderNode() can be called several times with different dirty
- // state before render() gets invoked. So accumulate.
- m_node->m_dirty |= m_dirty;
+ const int count = m_vp.count();
+ const bool listChanged = m_accDirty & DirtyList;
+ if (listChanged)
+ m_node->m_vp.resize(count);
- if (m_dirty & DirtyPath) {
- m_node->m_path = m_path;
- m_node->m_path.setFillRule(m_fillRule);
- }
+ for (int i = 0; i < count; ++i) {
+ VisualPathGuiData &src(m_vp[i]);
+ QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]);
- if (m_dirty & DirtyFillRule)
- m_node->m_path.setFillRule(m_fillRule);
+ if (listChanged || (src.dirty & DirtyPath)) {
+ dst.path = src.path;
+ dst.path.setFillRule(src.fillRule);
+ }
- if (m_dirty & DirtyPen) {
- m_node->m_pen = m_pen;
- m_node->m_strokeWidth = m_strokeWidth;
- }
+ if (listChanged || (src.dirty & DirtyFillRule))
+ dst.path.setFillRule(src.fillRule);
+
+ if (listChanged || (src.dirty & DirtyPen)) {
+ dst.pen = src.pen;
+ dst.strokeWidth = src.strokeWidth;
+ }
+
+ if (listChanged || (src.dirty & DirtyBrush))
+ dst.brush = src.brush;
- if (m_dirty & DirtyBrush)
- m_node->m_brush = m_brush;
+ src.dirty = 0;
+ }
m_node->markDirty(QSGNode::DirtyMaterial);
- m_dirty = 0;
+ m_accDirty = 0;
}
QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item)
@@ -199,7 +223,7 @@ void QQuickPathItemSoftwareRenderNode::releaseResources()
void QQuickPathItemSoftwareRenderNode::render(const RenderState *state)
{
- if (m_path.isEmpty())
+ if (m_vp.isEmpty())
return;
QSGRendererInterface *rif = m_item->window()->rendererInterface();
@@ -213,11 +237,11 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state)
p->setTransform(matrix()->toTransform());
p->setOpacity(inheritedOpacity());
- p->setPen(m_strokeWidth >= 0.0f && 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;
+ for (const VisualPathRenderData &d : qAsConst(m_vp)) {
+ p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen);
+ p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush);
+ p->drawPath(d.path);
+ }
}
QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const
diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h
index 6c7d052596..60b52e2def 100644
--- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h
+++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h
@@ -68,36 +68,39 @@ public:
DirtyPen = 0x02,
DirtyFillRule = 0x04,
DirtyBrush = 0x08,
-
- DirtyAll = 0xFF
+ DirtyList = 0x10
};
- 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,
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle,
qreal dashOffset, const QVector<qreal> &dashPattern) override;
- void setFillGradient(QQuickPathGradient *gradient) override;
+ void setFillGradient(int index, QQuickPathGradient *gradient) override;
void endSync() override;
- void updatePathRenderNode() override;
+
+ void updateNode() override;
void setNode(QQuickPathItemSoftwareRenderNode *node);
private:
QQuickPathItemSoftwareRenderNode *m_node = nullptr;
- int m_dirty = 0;
-
- QPainterPath m_path;
- QPen m_pen;
- float m_strokeWidth;
- QColor m_fillColor;
- QBrush m_brush;
- Qt::FillRule m_fillRule;
+ int m_accDirty = 0;
+ struct VisualPathGuiData {
+ int dirty = 0;
+ QPainterPath path;
+ QPen pen;
+ float strokeWidth;
+ QColor fillColor;
+ QBrush brush;
+ Qt::FillRule fillRule;
+ };
+ QVector<VisualPathGuiData> m_vp;
};
class QQuickPathItemSoftwareRenderNode : public QSGRenderNode
@@ -114,12 +117,14 @@ public:
private:
QQuickPathItem *m_item;
- int m_dirty = 0;
- QPainterPath m_path;
- QPen m_pen;
- float m_strokeWidth;
- QBrush m_brush;
+ struct VisualPathRenderData {
+ QPainterPath path;
+ QPen pen;
+ float strokeWidth;
+ QBrush brush;
+ };
+ QVector<VisualPathRenderData> m_vp;
friend class QQuickPathItemSoftwareRenderer;
};
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 228056fdb5..337d63e53b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -79,6 +79,73 @@ QT_BEGIN_NAMESPACE
PathAttribute allows named attributes with values to be defined
along the path.
+ Path and the other types for specifying path elements are shared between
+ \l PathView and \l PathItem. The following table provides an overview of the
+ applicability of the various path elements:
+
+ \table
+ \header
+ \li Element
+ \li PathView
+ \li PathItem
+ \li PathItem, GL_NV_path_rendering
+ \li PathItem, software
+ \row
+ \li PathMove
+ \li N/A
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathQuad
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathCubic
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathArc
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathSvg
+ \li Yes
+ \li Yes
+ \li Yes, with limitations
+ \li Yes
+ \row
+ \li PathAttribute
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathPercent
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathCurve
+ \li Yes
+ \li No
+ \li No
+ \li No
+ \endtable
+
\sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
*/
QQuickPath::QQuickPath(QObject *parent)
@@ -1872,6 +1939,11 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
\endqml
\endtable
+ \note Mixing PathSvg with other type of elements is not always supported,
+ therefore it is strongly recommended to avoid this. For example, when
+ \l PathItem is backed by \c{GL_NV_path_rendering}, a Path can contain one or
+ more PathSvg elements, or one or more other type of elements, but not both.
+
\sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
*/
@@ -1896,6 +1968,7 @@ void QQuickPathSvg::setPath(const QString &path)
_path = path;
emit pathChanged();
+ emit changed();
}
void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)