aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
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 &)