diff options
Diffstat (limited to 'src/quick/items/qquickpathitem.cpp')
-rw-r--r-- | src/quick/items/qquickpathitem.cpp | 460 |
1 files changed, 279 insertions, 181 deletions
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 ***** |